Java异常
目录
一、异常的层次结构
1.1、Throwable
1.2、Error(错误)
1.3、Exception(异常)
运行时异常
非运行时异常 (编译异常)
1.4、可查的异常(checked exceptions)和不可查的异常(unchecked exceptions)
可查异常(编译器要求必须处置的异常):
不可查异常(编译器不要求强制处置的异常)
二、异常基础
2.1、异常关键字
2.2、异常的申明(throws)
2.3、异常的抛出(throw)
2.4、异常的自定义
2.5、异常的捕获
try-catch
try-catch-finally
try-finally
try-with-resource
2.6、异常基础总结
2.7、常用的异常
三、异常实践
3.1、只针对不正常的情况才使用异常
3.2、在 finally 块中清理资源或者使用 try-with-resource 语句
3.3、尽量使用标准的异常
3.4、对异常进行文档说明
3.5、优先捕获最具体的异常
3.6、不要捕获 Throwable 类
3.7、不要忽略异常
3.8、不要记录并抛出异常
3.9、包装异常时不要抛弃原始的异常
3.10、不要使用异常控制程序的流程
3.11、不要在finally块中使用return
四、深入理解异常
4.1、JVM处理异常的机制
4.2、异常是否耗时?为什么会耗时?
一、异常的层次结构
异常指不期而至的各种状况,如:文件找不到、网络连接失败、非法参数等。异常是一个事件,它发生在程序运行期间,干扰了正常的指令流程。Java通 过API中Throwable类的众多子类描述各种不同的异常。因而,Java异常都是对象,是Throwable子类的实例,描述了出现在一段编码中的 错误条件。当条件生成时,错误将引发异常。
Java异常类层次结构图:
1.1、Throwable
Throwable 是 Java 语言中所有错误与异常的超类。
Throwable 包含两个子类:Error(错误)和 Exception(异常),它们通常用于指示发生了异常情况。
Throwable 包含了其线程创建时线程执行堆栈的快照,它提供了 printStackTrace() 等接口用于获取堆栈跟踪数据等信息。
1.2、Error(错误)
Error 类及其子类:程序中无法处理的错误,表示运行应用程序中出现了严重的错误。
此类错误一般表示代码运行时 JVM 出现问题。通常有 Virtual MachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)等。比如 OutOfMemoryError:内存不足错误;StackOverflowError:栈溢出错误。此类错误发生时,JVM 将终止线程。
这些错误是不受检异常,非代码性错误。因此,当此类错误发生时,应用程序不应该去处理此类错误。按照Java惯例,我们是不应该实现任何新的Error子类的!
1.3、Exception(异常)
程序本身可以捕获并且可以处理的异常。Exception 这种异常又分为两类:运行时异常和编译时异常。
-
运行时异常
都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
-
非运行时异常 (编译异常)
是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。
1.4、可查的异常(checked exceptions)和不可查的异常(unchecked exceptions)
-
可查异常(编译器要求必须处置的异常):
正确的程序在运行中,很容易出现的、情理可容的异常状况。可查异常虽然是异常状况,但在一定程度上它的发生是可以预计的,而且一旦发生这种异常状况,就必须采取某种方式进行处理。
除了RuntimeException及其子类以外,其他的Exception类及其子类都属于可查异常。这种异常的特点是Java编译器会检查它,也就是说,当程序中可能出现这类异常,要么用try-catch语句捕获它,要么用throws子句声明抛出它,否则编译不会通过。
-
不可查异常(编译器不要求强制处置的异常)
包括运行时异常(RuntimeException与其子类)和错误(Error)。
二、异常基础
2.1、异常关键字
- try – 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
- catch – 用于捕获异常。catch用来捕获try语句块中发生的异常。
- finally – finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
- throw – 用于抛出异常。
- throws – 用在方法签名中,用于声明该方法可能抛出的异常。
2.2、异常的申明(throws)
在Java中,当前执行的语句必属于某个方法,Java解释器调用main方法执行开始执行程序。若方法中存在检查异常,如果不对其捕获,那必须在方法头中显式声明该异常,以便于告知方法调用者此方法有异常,需要进行处理。 在方法中声明一个异常,方法头中使用关键字throws,后面接上要声明的异常。若声明多个异常,则使用逗号分割。如下所示:
public static void method() throws IOException, FileNotFoundException{//something statements
}
注意:若是父类的方法没有声明异常,则子类继承方法后,也不能声明异常。
通常,应该捕获那些知道如何处理的异常,将不知道如何处理的异常继续传递下去。传递异常可以在方法签名处使用 throws 关键字声明可能会抛出的异常。
private static void readFile(String filePath) throws IOException {File file = new File(filePath);String result;BufferedReader reader = new BufferedReader(new FileReader(file));while((result = reader.readLine())!=null) {System.out.println(result);}reader.close();
}
Throws抛出异常的规则:
- 如果是不可查异常(unchecked exception),即Error、RuntimeException或它们的子类,那么可以不使用throws关键字来声明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。
- 必须声明方法可抛出的任何可查异常(checked exception)。即如果一个方法可能出现受可查异常,要么用try-catch语句捕获,要么用throws子句声明将它抛出,否则会导致编译错误
- 仅当抛出了异常,该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出,而不是囫囵吞枣。
- 调用方法必须遵循任何可查异常的处理和声明规则。若覆盖一个方法,则不能声明与覆盖方法不同的异常。声明的任何异常必须是被覆盖方法所声明异常的同类或子类。
2.3、异常的抛出(throw)
如果代码可能会引发某种错误,可以创建一个合适的异常类实例并抛出它,这就是抛出异常。如下所示:
public static double method(int value) {if(value == 0) {throw new ArithmeticException("参数不能为0"); //抛出一个运行时异常}return 5.0 / value;
}
大部分情况下都不需要手动抛出异常,因为Java的大部分方法要么已经处理异常,要么已声明异常。所以一般都是捕获异常或者再往上抛。
有时我们会从 catch 中抛出一个异常,目的是为了改变异常的类型。多用于在多系统集成时,当某个子系统故障,异常类型可能有多种,可以用统一的异常类型向外暴露,不需暴露太多内部异常细节。
private static void readFile(String filePath) throws MyException { try {// code} catch (IOException e) {MyException ex = new MyException("read file failed.");ex.initCause(e);throw ex;}
}
2.4、异常的自定义
习惯上,定义一个异常类应包含两个构造函数,一个无参构造函数和一个带有详细描述信息的构造函数(Throwable 的 toString 方法会打印这些详细信息,调试时很有用), 比如上面用到的自定义MyException:
public class MyException extends Exception {public MyException(){ }public MyException(String msg){super(msg);}// ...
}
2.5、异常的捕获
异常捕获处理的方法通常有:
- try-catch
- try-catch-finally
- try-finally
- try-with-resource
try-catch
在一个 try-catch 语句块中可以捕获多个异常类型,并对不同类型的异常做出不同的处理
private static void readFile(String filePath) {try {// code} catch (FileNotFoundException e) {// handle FileNotFoundException} catch (IOException e){// handle IOException}
}
同一个 catch 也可以捕获多种类型异常,用 | 隔开
private static void readFile(String filePath) {try {// code} catch (FileNotFoundException | UnknownHostException e) {// handle FileNotFoundException or UnknownHostException} catch (IOException e){// handle IOException}
}
try-catch-finally
- 常规语法
try { //执行程序代码,可能会出现异常
} catch(Exception e) { //捕获异常并处理
} finally {//必执行的代码
}
- 执行的顺序
- 当try没有捕获到异常时:try语句块中的语句逐一被执行,程序将跳过catch语句块,执行finally语句块和其后的语句;
- 当try捕获到异常,catch语句块里没有处理此异常的情况:当try语句块里的某条语句出现异常时,而没有处理此异常的catch语句块时,此异常将会抛给JVM处理,finally语句块里的语句还是会被执行,但finally语句块后的语句不会被执行;
- 当try捕获到异常,catch语句块里有处理此异常的情况:在try语句块中是按照顺序来执行的,当执行到某一条语句出现异常时,程序将跳到catch语句块,并与catch语句块逐一匹配,找到与之对应的处理程序,其他的catch语句块将不会被执行,而try语句块中,出现异常之后的语句也不会被执行,catch语句块执行完后,执行finally语句块里的语句,最后执行finally语句块后的语句;
- 一个完整的例子
private static void readFile(String filePath) throws MyException {File file = new File(filePath);String result;BufferedReader reader = null;try {reader = new BufferedReader(new FileReader(file));while((result = reader.readLine())!=null) {System.out.println(result);}} catch (IOException e) {System.out.println("readFile method catch block.");MyException ex = new MyException("read file failed.");ex.initCause(e);throw ex;} finally {System.out.println("readFile method finally block.");if (null != reader) {try {reader.close();} catch (IOException e) {e.printStackTrace();}}}
}
try-finally
可以直接用try-finally吗? 可以。
try块中引起异常,异常代码之后的语句不再执行,直接执行finally语句。 try块没有引发异常,则执行完try块就执行finally语句。
try-finally可用在不需要捕获异常的代码,可以保证资源在使用后被关闭。例如IO流中执行完相应操作后,关闭相应资源;使用Lock对象保证线程同步,通过finally可以保证锁会被释放;数据库连接代码时,关闭连接操作等等。
//以Lock加锁为例,演示try-finally
ReentrantLock lock = new ReentrantLock();
try {//需要加锁的代码
} finally {lock.unlock(); //保证锁一定被释放
}
finally遇见如下情况不会执行
- 在前面的代码中用了System.exit()退出程序。
- finally语句块中发生了异常。
- 程序所在的线程死亡。
- 关闭CPU。
try-with-resource
try-with-resource是Java 7中引入的,很容易被忽略。
上面例子中,finally 中的 close 方法也可能抛出 IOException, 从而覆盖了原始异常。JAVA 7 提供了更优雅的方式来实现资源的自动释放,自动释放的资源需要是实现了 AutoCloseable 接口的类。
- 代码实现
private static void tryWithResourceTest(){try (Scanner scanner = new Scanner(new FileInputStream("c:/abc"),"UTF-8")){// code} catch (IOException e){// handle exception}
}
- 看下Scanner
public final class Scanner implements Iterator<String>, Closeable {// ...
}
public interface Closeable extends AutoCloseable {public void close() throws IOException;
}
try 代码块退出时,会自动调用 scanner.close 方法,和把 scanner.close 方法放在 finally 代码块中不同的是,若 scanner.close 抛出异常,则会被抑制,抛出的仍然为原始异常。被抑制的异常会由 addSusppressed 方法添加到原来的异常,如果想要获取被抑制的异常列表,可以调用 getSuppressed 方法来获取。
2.6、异常基础总结
- try、catch和finally都不能单独使用,只能是try-catch、try-finally或者try-catch-finally。
- try语句块监控代码,出现异常就停止执行下面的代码,然后将异常移交给catch语句块来处理。
- finally语句块中的代码一定会被执行,常用于回收资源 。
- throws:声明一个异常,告知方法调用者。
- throw :抛出一个异常,至于该异常被捕获还是继续抛出都与它无关。
Java编程思想一书中,对异常的总结。
- 在恰当的级别处理问题。(在知道该如何处理的情况下了捕获异常。)
- 解决问题并且重新调用产生异常的方法。
- 进行少许修补,然后绕过异常发生的地方继续执行。
- 用别的数据进行计算,以代替方法预计会返回的值。
- 把当前运行环境下能做的事尽量做完,然后把相同的异常重抛到更高层。
- 把当前运行环境下能做的事尽量做完,然后把不同的异常抛到更高层。
- 终止程序。
- 进行简化(如果你的异常模式使问题变得太复杂,那么用起来会非常痛苦)。
- 让类库和程序更安全。
2.7、常用的异常
在Java中提供了一些异常用来描述经常发生的错误,对于这些异常,有的需要程序员进行捕获处理或声明抛出,有的是由Java虚拟机自动进行捕获处理。Java中常见的异常类:
-
RuntimeException
- java.lang.ArrayIndexOutOfBoundsException 数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。
- java.lang.ArithmeticException 算术条件异常。譬如:整数除零等。
- java.lang.NullPointerException 空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等
- java.lang.ClassNotFoundException 找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常。
- java.lang.NegativeArraySizeException 数组长度为负异常
- java.lang.ArrayStoreException 数组中包含不兼容的值抛出的异常
- java.lang.SecurityException 安全性异常
- java.lang.IllegalArgumentException 非法参数异常
-
IOException
- IOException:操作输入流和输出流时可能出现的异常。
- EOFException 文件已结束异常
- FileNotFoundException 文件未找到异常
-
其他
- ClassCastException 类型转换异常类
- ArrayStoreException 数组中包含不兼容的值抛出的异常
- SQLException 操作数据库异常类
- NoSuchFieldException 字段未找到异常
- NoSuchMethodException 方法未找到抛出的异常
- NumberFormatException 字符串转换为数字抛出的异常
- StringIndexOutOfBoundsException 字符串索引超出范围抛出的异常
- IllegalAccessException 不允许访问某类异常
- InstantiationException 当应用程序试图使用Class类中的newInstance()方法创建一个类的实例,而指定的类对象无法被实例化时,抛出该异常
三、异常实践
在 Java 中处理异常并不是一个简单的事情。不仅仅初学者很难理解,即使一些有经验的开发者也需要花费很多时间来思考如何处理异常,包括需要处理哪些异常,怎样处理等等。这也是绝大多数开发团队都会制定一些规则来规范进行异常处理的原因。
当你抛出或捕获异常的时候,有很多不同的情况需要考虑,而且大部分事情都是为了改善代码的可读性或者 API 的可用性。
异常不仅仅是一个错误控制机制,也是一个通信媒介。因此,为了和同事更好的合作,一个团队必须要制定出一个最佳实践和规则,只有这样,团队成员才能理解这些通用概念,同时在工作中使用它。
这里给出几个被很多团队使用的异常处理最佳实践。
3.1、只针对不正常的情况才使用异常
异常只应该被用于不正常的条件,它们永远不应该被用于正常的控制流。《阿里手册》中:【强制】Java 类库中定义的可以通过预检查方式规避的RuntimeException异常不应该通过catch 的方式来处理,比如:NullPointerException,IndexOutOfBoundsException等等。
比如,在解析字符串形式的数字时,可能存在数字格式错误,不得通过catch Exception来实现
- 代码1
if (obj != null) {//...
}
- 代码2
try { obj.method();
} catch (NullPointerException e) {//...
}
主要原因有三点:
- 异常机制的设计初衷是用于不正常的情况,所以很少会会JVM实现试图对它们的性能进行优化。所以,创建、抛出和捕获异常的开销是很昂贵的。
- 把代码放在try-catch中返回阻止了JVM实现本来可能要执行的某些特定的优化。
- 对数组进行遍历的标准模式并不会导致冗余的检查,有些现代的JVM实现会将它们优化掉。
3.2、在 finally 块中清理资源或者使用 try-with-resource 语句
当使用类似InputStream这种需要使用后关闭的资源时,一个常见的错误就是在try块的最后关闭资源。
- 错误示例
public void doNotCloseResourceInTry() {FileInputStream inputStream = null;try {File file = new File("./tmp.txt");inputStream = new FileInputStream(file);// use the inputStream to read a file// do NOT do thisinputStream.close();} catch (FileNotFoundException e) {log.error(e);} catch (IOException e) {log.error(e);}
}
问题就是,只有没有异常抛出的时候,这段代码才可以正常工作。try 代码块内代码会正常执行,并且资源可以正常关闭。但是,使用 try 代码块是有原因的,一般调用一个或多个可能抛出异常的方法,而且,你自己也可能会抛出一个异常,这意味着代码可能不会执行到 try 代码块的最后部分。结果就是,你并没有关闭资源。
所以,你应该把清理工作的代码放到 finally 里去,或者使用 try-with-resource 特性。
- 方法一:使用 finally 代码块
与前面几行 try 代码块不同,finally 代码块总是会被执行。不管 try 代码块成功执行之后还是你在 catch 代码块中处理完异常后都会执行。因此,你可以确保你清理了所有打开的资源。
public void closeResourceInFinally() {FileInputStream inputStream = null;try {File file = new File("./tmp.txt");inputStream = new FileInputStream(file);// use the inputStream to read a file} catch (FileNotFoundException e) {log.error(e);} finally {if (inputStream != null) {try {inputStream.close();} catch (IOException e) {log.error(e);}}}
}
- 方法二:Java 7 的 try-with-resource 语法
如果你的资源实现了 AutoCloseable 接口,你可以使用这个语法。大多数的 Java 标准资源都继承了这个接口。当你在 try 子句中打开资源,资源会在 try 代码块执行后或异常处理后自动关闭。
public void automaticallyCloseResource() {File file = new File("./tmp.txt");try (FileInputStream inputStream = new FileInputStream(file);) {// use the inputStream to read a file} catch (FileNotFoundException e) {log.error(e);} catch (IOException e) {log.error(e);}
}
3.3、尽量使用标准的异常
代码重用是值得提倡的,这是一条通用规则,异常也不例外。
重用现有的异常有几个好处:
- 它使得你的API更加易于学习和使用,因为它与程序员原来已经熟悉的习惯用法是一致的。
- 对于用到这些API的程序而言,它们的可读性更好,因为它们不会充斥着程序员不熟悉的异常。
- 异常类越少,意味着内存占用越小,并且转载这些类的时间开销也越小。
Java标准异常中有几个是经常被使用的异常。如下表格:
异常 | 使用场合 |
---|---|
IllegalArgumentException | 参数的值不合适 |
IllegalStateException | 参数的状态不合适 |
NullPointerException | 在null被禁止的情况下参数值为null |
IndexOutOfBoundsException | 下标越界 |
ConcurrentModificationException | 在禁止并发修改的情况下,对象检测到并发修改 |
UnsupportedOperationException | 对象不支持客户请求的方法 |
虽然它们是Java平台库迄今为止最常被重用的异常,但是,在许可的条件下,其它的异常也可以被重用。例如,如果你要实现诸如复数或者矩阵之类的算术对象,那么重用ArithmeticException和NumberFormatException将是非常合适的。如果一个异常满足你的需要,则不要犹豫,使用就可以,不过你一定要确保抛出异常的条件与该异常的文档中描述的条件一致。这种重用必须建立在语义的基础上,而不是名字的基础上。
最后,一定要清楚,选择重用哪一种异常并没有必须遵循的规则。例如,考虑纸牌对象的情形,假设有一个用于发牌操作的方法,它的参数(handSize)是发一手牌的纸牌张数。假设调用者在这个参数中传递的值大于整副牌的剩余张数。那么这种情形既可以被解释为IllegalArgumentException(handSize的值太大),也可以被解释为IllegalStateException(相对客户的请求而言,纸牌对象的纸牌太少)。
3.4、对异常进行文档说明
当在方法上声明抛出异常时,也需要进行文档说明。目的是为了给调用者提供尽可能多的信息,从而可以更好地避免或处理异常。
在 Javadoc 添加 @throws 声明,并且描述抛出异常的场景。
/**
* Method description
*
* @throws MyBusinessException - businuess exception description
*/
public void doSomething(String input) throws MyBusinessException {// ...
}
同时,在抛出MyBusinessException 异常时,需要尽可能精确地描述问题和相关信息,这样无论是打印到日志中还是在监控工具中,都能够更容易被人阅读,从而可以更好地定位具体错误信息、错误的严重程度等。
3.5、优先捕获最具体的异常
大多数 IDE 都可以帮助你实现这个最佳实践。当你尝试首先捕获较不具体的异常时,它们会报告无法访问的代码块。
但问题在于,只有匹配异常的第一个 catch 块会被执行。 因此,如果首先捕获 IllegalArgumentException ,则永远不会到达应该处理更具体的 NumberFormatException 的 catch 块,因为它是 IllegalArgumentException 的子类。
总是优先捕获最具体的异常类,并将不太具体的 catch 块添加到列表的末尾。
你可以在下面的代码片断中看到这样一个 try-catch 语句的例子。 第一个 catch 块处理所有 NumberFormatException 异常,第二个处理所有非 NumberFormatException 异常的IllegalArgumentException 异常。
public void catchMostSpecificExceptionFirst() {try {doSomething("A message");} catch (NumberFormatException e) {log.error(e);} catch (IllegalArgumentException e) {log.error(e)}
}
3.6、不要捕获 Throwable 类
Throwable 是所有异常和错误的超类。你可以在 catch 子句中使用它,但是你永远不应该这样做!
如果在 catch 子句中使用 Throwable ,它不仅会捕获所有异常,也将捕获所有的错误。JVM 抛出错误,指出不应该由应用程序处理的严重问题。 典型的例子是 OutOfMemoryError 或者 StackOverflowError 。两者都是由应用程序控制之外的情况引起的,无法处理。
所以,最好不要捕获 Throwable ,除非你确定自己处于一种特殊的情况下能够处理错误。
public void doNotCatchThrowable() {try {// do something} catch (Throwable t) {// don't do this!}
}
3.7、不要忽略异常
很多时候,开发者很有自信不会抛出异常,因此写了一个catch块,但是没有做任何处理或者记录日志。
public void doNotIgnoreExceptions() {try {// do something} catch (NumberFormatException e) {// this will never happen}
}
但现实是经常会出现无法预料的异常,或者无法确定这里的代码未来是不是会改动(删除了阻止异常抛出的代码),而此时由于异常被捕获,使得无法拿到足够的错误信息来定位问题。
合理的做法是至少要记录异常的信息。
public void logAnException() {try {// do something} catch (NumberFormatException e) {log.error("This should never happen: " + e); // see this line}
}
3.8、不要记录并抛出异常
这可能是本文中最常被忽略的最佳实践。
可以发现很多代码甚至类库中都会有捕获异常、记录日志并再次抛出的逻辑。如下:
try {new Long("xyz");
} catch (NumberFormatException e) {log.error(e);throw e;
}
这个处理逻辑看着是合理的。但这经常会给同一个异常输出多条日志。如下:
17:44:28,945 ERROR TestExceptionHandling:65 - java.lang.NumberFormatException: For input string: "xyz"
Exception in thread "main" java.lang.NumberFormatException: For input string: "xyz"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Long.parseLong(Long.java:589)
at java.lang.Long.(Long.java:965)
at com.stackify.example.TestExceptionHandling.logAndThrowException(TestExceptionHandling.java:63)
at com.stackify.example.TestExceptionHandling.main(TestExceptionHandling.java:58)
如上所示,后面的日志也没有附加更有用的信息。如果想要提供更加有用的信息,那么可以将异常包装为自定义异常。
public void wrapException(String input) throws MyBusinessException {try {// do something} catch (NumberFormatException e) {throw new MyBusinessException("A message that describes the error.", e);}
}
因此,仅仅当想要处理异常时才去捕获,否则只需要在方法签名中声明让调用者去处理。
3.9、包装异常时不要抛弃原始的异常
捕获标准异常并包装为自定义异常是一个很常见的做法。这样可以添加更为具体的异常信息并能够做针对的异常处理。 在你这样做时,请确保将原始异常设置为原因(注:参考下方代码 NumberFormatException e 中的原始异常 e )。Exception 类提供了特殊的构造函数方法,它接受一个 Throwable 作为参数。否则,你将会丢失堆栈跟踪和原始异常的消息,这将会使分析导致异常的异常事件变得困难。
public void wrapException(String input) throws MyBusinessException {try {// do something} catch (NumberFormatException e) {throw new MyBusinessException("A message that describes the error.", e);}
}
3.10、不要使用异常控制程序的流程
不应该使用异常控制应用的执行流程,例如,本应该使用if语句进行条件判断的情况下,你却使用异常处理,这是非常不好的习惯,会严重影响应用的性能。
3.11、不要在finally块中使用return
try块中的return语句执行成功后,并不马上返回,而是继续执行finally块中的语句,如果此处存在return语句,则在此直接返回,无情丢弃掉try块中的返回点。
如下是一个反例:
private int x = 0;
public int checkReturn() {try {// x等于1,此处不返回return ++x;} finally {// 返回的结果是2return ++x;}
}
四、深入理解异常
我们再深入理解下异常,看下底层实现。
4.1、JVM处理异常的机制
提到JVM处理异常的机制,就需要提及Exception Table,以下称为异常表。我们暂且不急于介绍异常表,先看一个简单的 Java 处理异常的小例子。
public static void simpleTryCatch() {try {testNPE();} catch (Exception e) {e.printStackTrace();}
}
上面的代码是一个很简单的例子,用来捕获处理一个潜在的空指针异常。
当然如果只是看简简单单的代码,我们很难看出什么高深之处,更没有了今天文章要谈论的内容。
所以这里我们需要借助一把神兵利器,它就是javap,一个用来拆解class文件的工具,和javac一样由JDK提供。
然后我们使用javap来分析这段代码(需要先使用javac编译)
//javap -c Mainpublic static void simpleTryCatch();Code:0: invokestatic #3 // Method testNPE:()V3: goto 116: astore_07: aload_08: invokevirtual #5 // Method java/lang/Exception.printStackTrace:()V11: returnException table:from to target type0 3 6 Class java/lang/Exception
看到上面的代码,应该会有会心一笑,因为终于看到了Exception table,也就是我们要研究的异常表。
异常表中包含了一个或多个异常处理者(Exception Handler)的信息,这些信息包含如下
- from 可能发生异常的起始点
- to 可能发生异常的结束点
- target 上述from和to之前发生异常后的异常处理者的位置
- type 异常处理者处理的异常的类信息
那么异常表用在什么时候呢
答案是异常发生的时候,当一个异常发生时
- 1.JVM会在当前出现异常的方法中,查找异常表,是否有合适的处理者来处理
- 2.如果当前方法异常表不为空,并且异常符合处理者的from和to节点,并且type也匹配,则JVM调用位于target的调用者来处理。
- 3.如果上一条未找到合理的处理者,则继续查找异常表中的剩余条目
- 4.如果当前方法的异常表无法处理,则向上查找(弹栈处理)刚刚调用该方法的调用处,并重复上面的操作。
- 5.如果所有的栈帧被弹出,仍然没有处理,则抛给当前的Thread,Thread则会终止。
- 6.如果当前Thread为最后一个非守护线程,且未处理异常,则会导致JVM终止运行。
以上就是JVM处理异常的一些机制。
try catch -finally
除了简单的try-catch外,我们还常常和finally做结合使用。比如这样的代码
public static void simpleTryCatchFinally() {try {testNPE();} catch (Exception e) {e.printStackTrace();} finally {System.out.println("Finally");}
}
同样我们使用javap分析一下代码
public static void simpleTryCatchFinally();Code:0: invokestatic #3 // Method testNPE:()V3: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;6: ldc #7 // String Finally8: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V11: goto 4114: astore_015: aload_016: invokevirtual #5 // Method java/lang/Exception.printStackTrace:()V19: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;22: ldc #7 // String Finally24: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V27: goto 4130: astore_131: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;34: ldc #7 // String Finally36: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V39: aload_140: athrow41: returnException table:from to target type0 3 14 Class java/lang/Exception0 3 30 any14 19 30 any
和之前有所不同,这次异常表中,有三条数据,而我们仅仅捕获了一个Exception, 异常表的后两个item的type为any; 上面的三条异常表item的意思为:
- 如果0到3之间,发生了Exception类型的异常,调用14位置的异常处理者。
- 如果0到3之间,无论发生什么异常,都调用30位置的处理者
- 如果14到19之间(即catch部分),不论发生什么异常,都调用30位置的处理者。
再次分析上面的Java代码,finally里面的部分已经被提取到了try部分和catch部分。我们再次调一下代码来看一下
public static void simpleTryCatchFinally();Code://try 部分提取finally代码,如果没有异常发生,则执行输出finally操作,直至goto到41位置,执行返回操作。 0: invokestatic #3 // Method testNPE:()V3: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;6: ldc #7 // String Finally8: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V11: goto 41//catch部分提取finally代码,如果没有异常发生,则执行输出finally操作,直至执行got到41位置,执行返回操作。14: astore_015: aload_016: invokevirtual #5 // Method java/lang/Exception.printStackTrace:()V19: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;22: ldc #7 // String Finally24: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V27: goto 41//finally部分的代码如果被调用,有可能是try部分,也有可能是catch部分发生异常。30: astore_131: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;34: ldc #7 // String Finally36: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V39: aload_140: athrow //如果异常没有被catch捕获,而是到了这里,执行完finally的语句后,仍然要把这个异常抛出去,传递给调用处。41: return
Catch先后顺序的问题
我们在代码中的catch的顺序决定了异常处理者在异常表的位置,所以,越是具体的异常要先处理,否则就会出现下面的问题
private static void misuseCatchException() {try {testNPE();} catch (Throwable t) {t.printStackTrace();} catch (Exception e) { //error occurs during compilings with tips Exception Java.lang.Exception has already benn caught.e.printStackTrace();}
}
这段代码会导致编译失败,因为先捕获Throwable后捕获Exception,会导致后面的catch永远无法被执行。
Return 和finally的问题
这算是我们扩展的一个相对比较极端的问题,就是类似这样的代码,既有return,又有finally,那么finally导致会不会执行
public static String tryCatchReturn() {try {testNPE();return "OK";} catch (Exception e) {return "ERROR";} finally {System.out.println("tryCatchReturn");}
}
答案是finally会执行,那么还是使用上面的方法,我们来看一下为什么finally会执行。
public static java.lang.String tryCatchReturn();Code:0: invokestatic #3 // Method testNPE:()V3: ldc #6 // String OK5: astore_06: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;9: ldc #8 // String tryCatchReturn11: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V14: aload_015: areturn 返回OK字符串,areturn意思为return a reference from a method16: astore_017: ldc #10 // String ERROR19: astore_120: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;23: ldc #8 // String tryCatchReturn25: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V28: aload_129: areturn //返回ERROR字符串30: astore_231: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;34: ldc #8 // String tryCatchReturn36: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V39: aload_240: athrow 如果catch有未处理的异常,抛出去。
4.2、异常是否耗时?为什么会耗时?
说用异常慢,首先来看看异常慢在哪里?有多慢?下面的测试用例简单的测试了建立对象、建立异常对象、抛出并接住异常对象三者的耗时对比:
public class ExceptionTest { private int testTimes; public ExceptionTest(int testTimes) { this.testTimes = testTimes; } public void newObject() { long l = System.nanoTime(); for (int i = 0; i < testTimes; i++) { new Object(); } System.out.println("建立对象:" + (System.nanoTime() - l)); } public void newException() { long l = System.nanoTime(); for (int i = 0; i < testTimes; i++) { new Exception(); } System.out.println("建立异常对象:" + (System.nanoTime() - l)); } public void catchException() { long l = System.nanoTime(); for (int i = 0; i < testTimes; i++) { try { throw new Exception(); } catch (Exception e) { } } System.out.println("建立、抛出并接住异常对象:" + (System.nanoTime() - l)); } public static void main(String[] args) { ExceptionTest test = new ExceptionTest(10000); test.newObject(); test.newException(); test.catchException(); }
}
运行结果:
建立对象:575817
建立异常对象:9589080
建立、抛出并接住异常对象:47394475
建立一个异常对象,是建立一个普通Object耗时的约20倍(实际上差距会比这个数字更大一些,因为循环也占用了时间,追求精确的读者可以再测一下空循环的耗时然后在对比前减掉这部分),而抛出、接住一个异常对象,所花费时间大约是建立异常对象的4倍。
那占用时间的“大头”:抛出、接住异常,系统到底做了什么事情?请参考这篇文章:
- https://www.iteye.com/blog/icyfenix-857722
参考文章
- https://blog.csdn.net/MacWx/article/details/90204111
- https://blog.csdn.net/hguisu/article/details/6155636
- https://blog.csdn.net/ThinkWon/article/details/101681073
- https://www.cnblogs.com/skywang12345/p/3544287.html
- https://www.codercto.com/a/33350.html
相关文章:
Java异常
目录 一、异常的层次结构 1.1、Throwable 1.2、Error(错误) 1.3、Exception(异常) 运行时异常 非运行时异常 (编译异常) 1.4、可查的异常(checked exceptions)和不可查的异常…...
《热带气象学报》
《热带气象学报》创刊于1984年,前身为《热带气象》,1993年更名为《热带气象学报》,是广东省气象局主管,中国气象局广州热带海洋气象研究所主办的中文学术期刊。 本刊坚持“热带气象”的办刊特色,主要刊登:…...
解决docker不加载 /etc/docker/daemon.json文件的问题
文章目录 问题起源解决方案问题反思 问题起源 如题,最近在ubuntu24.04-LTS-server安装docker时,安装成功后设置源来设置镜像。 设置完成功拉取镜像,我就关机下次使用。 但是当我重启发现呢,镜像都不在了,但是由于网络…...
数据结构(初阶7)---七大排序法(堆排序,快速排序,归并排序,希尔排序,冒泡排序,选择排序,插入排序)(详解)
排序 1.插入排序2.希尔排序3.冒泡排序4.选择排序(双头排序优化版)5.堆排序6.快速排序1). 双指针法2).前后指针法3).非递归法 7.归并排序1).递归版本(递归的回退就是归并)2).非递归版本(迭代版本) 计算机执行的最多的操作之一就有排序,排序是一项极其重要的技能 接下…...
题解 洛谷 Luogu P1182 数列分段 Section II 二分答案 C/C++
题目传送门: P1182 数列分段 Section II - 洛谷 | 计算机科学教育新生态https://www.luogu.com.cn/problem/P1182思路: 二分答案,每次以区间 [l, r] 中点 m 为每段和的阈值 判断在此前提下,划分段数是否不大于 M 是就记录答案…...
鸿蒙心路旅程:从实践到创新——开发者的深度技术分享
目录 1. 引言:成为HarmonyOS NEXT开发者的动机 2. 项目初始化与架构设计:从零开始的技术规划 2.1 DevEco Studio的配置与项目初始化 2.2 分层架构设计 3. 分布式应用设计:挑战与解决方案 3.1 分布式架构设计:分布式软总线 …...
elementUI非常规数据格式渲染复杂表格(副表头、合并单元格)
效果 数据源 前端代码 (展示以及表格处理/数据处理) 标签 <el-table :data"dataList" style"width: 100%" :span-method"objectSpanMethod"><template v-for"(item, index) in headers"><el-table-column prop"…...
mysl数据库(八)事务、三种读现象
事务、三种读现象 文章目录 事务、三种读现象一、事务介绍二、事务的使用三、三种读现象 一、事务介绍 事务是mysql的一种机制,一个事务里可以包含多条sql语句。执行事务相当于拍了一张快照,在事务执行完提交以前可以回滚至最初的状态,当然事…...
【DVWA】File Inclusion文件包含实战
安能有术无道有道无心,乐得仁心仁义正心行道。 1.File Inclusion(Low) 相关代码分析 <?php// The page we wish to display $file $_GET[ page ];?>可以看到,服务器端对page参数没有做任何的过滤跟检查。 服务器期望用户的操作是点击下面的…...
(免费送源码)计算机毕业设计原创定制:Java+ssm+JSP+Ajax SSM棕榈校园论坛的开发
摘要 随着计算机科学技术的高速发展,计算机成了人们日常生活的必需品,从而也带动了一系列与此相关产业,是人们的生活发生了翻天覆地的变化,而网络化的出现也在改变着人们传统的生活方式,包括工作,学习,社交…...
异常处理(4)throws
异常处理(4) throws 前言:运行时异常(RuntimeException)或它的子类可以不做处理,因为这类异常很普遍,若全部处理,可能会对程序的可读性和运行效率产生影响。此外,即使不使…...
点云欧式聚类,条件欧式聚类算法原理及推导
点云欧式聚类算法数学推导 点云欧式聚类(Euclidean Clustering for Point Clouds)是点云处理中常用的一种无监督聚类方法。它基于欧式距离将点云中的点划分为多个簇,常用于分割、目标检测等任务。以下是算法的数学推导和实现原理。 问题定义…...
每日十题八股-2024年11月27日
1.类型互转会出现什么问题吗? 2.为什么用bigDecimal 不用double ? 3.装箱和拆箱是什么? 4.Java为什么要有Integer? 5.Integer相比int有什么优点? 6.那为什么还要保留int类型? 7.说一下 integer的缓存 8.怎么…...
C++虚函数面试题及参考答案
什么是虚函数?它的作用是什么? 虚函数是在基类中使用关键字 virtual 声明的成员函数。当在派生类中重写(override)这个函数时,会根据对象的实际类型来调用相应的函数版本,而不是仅仅根据指针或引用的类型来…...
如何搭建C++环境--1.下载安装并调试Microsoft Visual Studio Previerw(Windows)
1.首先,打开浏览器 首先,搜索“Microsoft Visual Studio Previerw” 安装 1.运行VisualStudioSetup (1).exe 无脑一直点继续 然后就到 选择需要的语言 我一般python用pycharm Java,HTML用vscode(Microsoft Visual Studio cod…...
大数据新视界 -- Hive 函数应用:复杂数据转换的实战案例(下)(12/ 30)
💖💖💖亲爱的朋友们,热烈欢迎你们来到 青云交的博客!能与你们在此邂逅,我满心欢喜,深感无比荣幸。在这个瞬息万变的时代,我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…...
深入理解 TypeScript:联合类型与交叉类型的应用
在 TypeScript 的世界里,类型系统是核心特性之一,它提供了强大的工具来帮助开发者编写更安全、更可靠的代码。今天,我们将深入探讨 TypeScript 中的两个高级类型特性:联合类型(Union Types)和交叉类型&…...
fiddler抓包工具与requests库构建自动化报告
一. Fiddler 抓包工具 1.1 Fiddler 工具介绍和安装 Fiddler 是一款功能强大的 HTTP 调试代理工具,能够全面记录并深入检查您的计算机与互联网之间的 HTTP 和 HTTPS 通信数据。其主界面布局清晰,主要包含菜单栏、工具栏、树形标签栏和内容栏。 1.2 Fid…...
数据结构——排序算法第二幕(交换排序:冒泡排序、快速排序(三种版本) 归并排序:归并排序(分治))超详细!!!!
文章目录 前言一、交换排序1.1 冒泡排序1.2 快速排序1.2.1 hoare版本 快排1.2.2 挖坑法 快排1.2.3 lomuto前后指针 快排 二、归并排序总结 前言 继上篇学习了排序的前面两个部分:直接插入排序和选择排序 今天我们来学习排序中常用的交换排序以及非常稳定的归并排序 快排可是有多…...
Vue-常用指令
🌈个人主页:前端青山 🔥系列专栏:Vue篇 🔖人终将被年少不可得之物困其一生 依旧青山,本期给大家带来Vue篇专栏内容:Vue-常用指令 目录 1.1 v-cloak 1.2 双向数据绑定指令 v-model 1.3 v-once 1.4 绑定属性 v-bind…...
守护进程
目录 守护进程 前台进程 后台进程 session(进程会话) 前台任务和后台任务比较好 本质 绘画和终端都关掉了,那些任务仍然在 bash也退了,然后就托孤了 编辑 守护进程化---不想受到任何用户登陆和注销的影响编辑 如何…...
GPON原理
GPON网络架构 对于OLT来说,它就相当于一个指挥官,它指挥PON口下的ONU在指定的时间段内发送数据以及发起测距过程等 而ONU则是一个士兵,按照OLT的指挥做出相应 而ODN它主要就是提供一个传输通道,主要包括分光器和光纤组成 对于PO…...
华三(HCL)和华为(eNSP)模拟器共存安装手册
接上章叙述,解决同一台PC上同时部署华三(HCL)和华为(eNSP)模拟器。原因就是华三HCL 的老版本如v2及以下使用VirtualBox v5版本,可以直接和eNSP兼容Oracle VirtualBox,而其他版本均使用Oracle VirtualBox v6以上的版本,…...
类和对象--中--初始化列表(重要)、隐式类型转化(理解)、最后两个默认成员函数
1.初始化列表 1.1作用: 通过特定的值,来初始化对象。 1.2定义: 初始化列表,就相当于定义对象(开空间)。不管写不写初始化列表,每个成员变量都会走一遍初始化列表(开出对应的空间…...
clickhouse 使用global in 优化 in查询
文章目录 in例子使用global in in例子 SELECT uniq(UserID) FROM distributed_table WHERE CounterID 101500 AND UserID IN (SELECT UserID FROM distributed_table WHERE CounterID 34)对于in 查询来说,本来查询的就是分布式表,假设这个表有100 个…...
macos 14.0 Monoma 修改顶部菜单栏颜色
macos 14.0 设置暗色后顶部菜单栏还维持浅色,与整体不协调。 修改方式如下:...
鸿蒙动画开发07——粒子动画
1、概 述 粒子动画是在一定范围内随机生成的大量粒子产生运动而组成的动画。 动画元素是一个个粒子,这些粒子可以是圆点、图片。我们可以通过对粒子在颜色、透明度、大小、速度、加速度、自旋角度等维度变化做动画,来营造一种氛围感,比如下…...
Matlab 2016b安装教程附安装包下载
软件介绍 MATLAB(矩阵实验室)是MathWorks公司推出的用于算法开发、数据可视化、数据分析以及数值计算的高级技术计算语言和交互式环境的商业数学软件。MATLAB具有数值分析、数值和符号计算、工程与科学绘图、控制系统的设计与仿真、数字图像处理、数字信…...
Container image .... already present on machine 故障排除
故障现象: Normal Pulled 12s (x2 over 15s) kubelet Container image “registry.cn-hangzhou.aliyuncs.com/yinzhengjie-k8s/apps:v1” already present on machine kubectl get pods NAME READY STATUS RESTARTS AGE two-pod 1/2 Error …...
力扣 二叉树的层序遍历-102
二叉树的层序遍历-102 class Solution { public:vector<vector<int>> levelOrder(TreeNode* root) {vector<vector<int>> res; // 二维数组用来存储每层节点if (root nullptr)return res;queue<TreeNode*> q; // 队列用来进行层序遍历q.push(r…...
Java 平衡二叉树 判断 详解
判断平衡二叉树的详解(Java 实现) 平衡二叉树的定义: 平衡二叉树(Balanced Binary Tree)是指一棵二叉树中任意节点的左右子树高度差不超过 1。即: ∣ h e i g h t ( l e f t ) − h e i g h t ( r i g h …...
Java设计模式笔记(一)
Java设计模式笔记(一) (23种设计模式由于篇幅较大分为两篇展示) 一、设计模式介绍 1、设计模式的目的 让程序具有更好的: 代码重用性可读性可扩展性可靠性高内聚,低耦合 2、设计模式的七大原则 单一职…...
【人工智能学习之yolov8改进的网络怎么指定规模】
yolov8改进的网络怎么指定规模 在你更换主干网络或者做了其他修改之后,发现模型总是默认使用的n规模,而n规模有可能无法完成任务,怎么办呢,有什么办法指定规模大小呢? WARNING ⚠️ no model scale passed. Assuming …...
网络安全概述
网络安全 物理安全 网络的物理安全是整个网络系统安全的前提。在 校园网工程建设中,由于网络系统属于 弱电工程,耐压值很低。因此,在 网络工程的设计和施工中,必须优先考虑保护人和 网络设备不受电、火灾和雷击的侵害࿱…...
[MySQL#2] 库 | 表 | 详解CRUD命令 | 字符集 | 校验规则
目录 一. 库操作 1. 创建数据库 2. 字符集和校验规则 校验规则对数据库的影响 显示创建数据库时对应的命令 3. 修改数据库 4. 数据库删除 备份和恢复 还原 查看连接情况 二. 表操作 1. 创建表(定义实例化格式 2. 创建表案例 (实例化数据类型…...
【Unity基础】如何查看当前项目使用的渲染管线?
在 Unity 中,你可以通过以下几种方式查看当前项目使用的是哪个渲染管线: 1. 检查 Graphics Settings 打开 Unity 编辑器,进入顶部菜单:Edit → Project Settings → Graphics。在 Graphics Settings 窗口中,找到 Scr…...
什么是域名监控?
域名监控是持续跟踪全球域名系统(DNS)中变化以发现恶意活动迹象的过程。组织可以对其拥有的域名进行监控,以判断是否有威胁行为者试图入侵其网络。他们还可以对客户的域名使用这种技术以执行类似的检查。 你可以将域名监控比作跟踪与自己实物…...
apache中的Worker 和 Prefork 之间的区别是什么?
文章目录 内存使用稳定性兼容性适用场景 Apache中的Worker和Prefork两种工作模式在内存使用、稳定性以及兼容性等方面存在区别 内存使用 Worker:由于使用线程,内存占用较少。Prefork:每个进程独立运行,内存消耗较大。 稳定性 W…...
解决SSL VPN客户端一直提示无法连接服务器的问题
近期服务器更新VPN后,我的win10电脑一致无法连接到VPN服务器, SSL VPN客户端总是提示无法连接到服务端。网上百度尝试了各种方法后,终于通过以下设置方式解决了问题: 1、首先,在控制面板中打开“网络和共享中心”窗口&…...
网络基础概念
1.网络协议 网络协议是一组标准和规则,用于定义电子设备如何在网络上通信。这些规则涵盖了数据如何格式化,传输,路由以及接收。网络协议确保了不同制造商的设备能够相互理解和交换数据 协议分层 协议也是软件,在设计上为了更好…...
sunshine和moonlight串流网络丢失帧高的问题(局域网)
注:此贴结果仅供参考 场景环境:单身公寓 路由器:2016年的路由器 开始:电脑安装sunshine软件,手机安装moonlight软件开始串流发现网络丢失帧发现巨高 一开始怀疑就是路由器问题,因为是局域网,而…...
远程视频验证如何改变商业安全
如今,商业企业面临着无数的安全挑战。尽管企业的形态和规模各不相同——从餐厅、店面和办公楼到工业地产和购物中心——但诸如入室盗窃、盗窃、破坏和人身攻击等威胁让安全主管时刻保持警惕。 虽然传统的监控摄像头网络帮助组织扩大了其态势感知能力,但…...
CTO 实际上是做什么的?
https://vadimkravcenko.com/shorts/what-cto-does/ 有刪節 本文旨在为软件工程师解密CTO的角色,并为那些渴望担任这一职位的人提供路线图。 “他们是技术团队与公司其他部门之间的桥梁,确保技术支持并推动业务发展。” CTO的角色经常被误解。CTO有时是…...
【软考速通笔记】系统架构设计师④——系统工程基础知识
文章目录 一、前言二、系统工程方法2.1 霍尔的三维结构2.2 切克兰德法2.3 并行工程2.4 综合集成法 三、系统工程生命周期四、系统生命周期方法五、系统性能5.1 计算机的性能指标5.2 路由器的性能指标5.3 交换机的性能指标5.4 网络的性能资料5.5 操作系统的性能指标5.6 数据库的…...
2024赣ctf-web -wp
1.你到底多想要flag??? 首先来解决第一关: 先了解一下stripos(); 并且此函数处理数组返回false。而且pre_match同样遇见数组是返回false(解释一下正则 i:这是正则表达式的修饰符,代表“不区…...
Android Framework AudioFlinge 面试题及参考答案
目录 请解释什么是 AudioFlinger? AudioFlinger 在 Android 系统中的位置是什么? AudioFlinger 的主要职责有哪些? AudioFlinger 如何管理音频流? 在 AudioFlinger 中,什么是音频会话? 请简述 AudioFlinger 的工作流程。 AudioFlinger 是如何与硬件交互的? 在 A…...
英语知识在线平台:Spring Boot技术应用
2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常…...
Qt5.14.2的安装与环境变量及一些依赖库的配置
目录 1.Qt5.14.2安装 2.Qt环境变量及一些依赖库的配置 1.Qt5.14.2安装 QT从入门到入土(一)——Qt5.14.2安装教程和VS2019环境配置 - 唯有自己强大 - 博客园 2.Qt环境变量及一些依赖库的配置 假设QT安装目录为: D:\Qt\Qt5.14.2 将目录: D:\Qt\Qt5.14.…...
2024年9月中国电子学会青少年软件编程(Python)等级考试试卷(六级)答案 + 解析
一、单选题 1、下面代码运行后出现的图像是?( ) import matplotlib.pyplot as plt import numpy as np x np.array([A, B, C, D]) y np.array([30, 25, 15, 35]) plt.bar(x, y) plt.show() A. B. C. D. 正确答案:A 答案…...
go编程中yaml的inline应用
下列代码,设计 Config 和 MyConfig 是为可扩展 Config,同时 Config 作为公共部分可保持变化。采用了匿名的内嵌结构体,但又不希望 yaml 结果多出一层。如果 MyConfig 中的 Config 没有使用“yaml:",inline"”修饰,则读取…...