Java异常两万字详解,面试再也不怕被问! (qq.com)
Java的异常是Throwable
包括两个子类的子类
- 程序本身可以捕获和处理异常。Exception 这种异常可分为两类:运行异常和编译异常。
RuntimeException
类及其子类,Java 编译器不会检查它。属于非受检异常。如果这种异常可能发生在程序中,"没有通过throws声明抛出它",也"它没有被try-catch语句捕捉到",还是会通过编译。例如,Nulllpointerexception空指针异常,Arrayindexoutboundexception数组下标越界异常,Clascascastexception类型转换异常,Aritheticexception算术异常。- 编译异常,Exception 中除 RuntimeException 子类以外的异常。属于检查异常。要么通过throws发表声明,要么通过try-catch捕获,否则不能通过编译。比如 IO相关异常,ClassNotFoundException(未发现指定类异常),SQLException。
Error
类别及其子类,代表程序中无法处理的错误,表明在应用程序运行过程中出现了严重的错误。JVM一般都有问题。这些错误是非检测异常和非代码性错误。当这些错误发生时,应用程序不应处理。我们不应该实现任何新的Error子类。eg. Virtual MachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)等。比如 OutOfMemoryError:内存不足错误;StackOverflowError:栈溢出错误。
异常检查:编译器需要处理的异常。除外 RuntimeException
除了子类,其他的 Exception 异常属于异常检查。
非检测异常:编译器不检查,不需要处理异常,包括运行异常(RuntimeException
极端子类)和错误(Error
)。
throw关键字用于方法内部,只能用于抛出异常。用于抛出方法或代码块中的异常,可以抛出检查异常和非检查异常。
在方法声明中使用throws关键字,可以抛出多个异常列表,表示该方法可能抛出的异常列表。一种方法 throws 识别可能抛出的异常列表。调用该方法的方法必须包含可处理的异常代码,否则也应在方法签名中使用 throws 关键字声明相应的异常。
问:JVM是如何处理异常的?如果在一种方法中出现异常,该方法将创建一个异常对象并将其转移给JVM。异常对象包括异常名称、异常描述和异常发生时应用程序的状态。创建异常对象并将其转移给JVM的过程称为抛出异常。
可能有一系列的调用方法,最后进入抛出异常的方法。调用这一系列方法的有序列表称为调用栈。
JVM将沿着调用堆栈查找是否有可以处理异常的代码。如果是这样,请调用异常处理代码。当JVM发现可以处理异常代码时,它会将异常传输给他。如果JVM没有找到可以处理异常的代码块,将异常转移到默认的异常处理器,打印异常信息并终止应用程序。
追问:try-catch-finally 如何使用?try
块:用于捕获异常。然后可以连接零或多个catch
块,如果没有catch
块,必须跟着一个finally
块。catch
块:用于处理 try 捕获的异常。finally
块:无论是捕获还是处理异常,finally
块中的句子将被执行。当try
块或catch
块中遇到return
语句时,finally
在方法返回之前,将执行句块。
public static void main(String[] args) { System.out.println(f(2));}public static int f(int value) { try { return value * value; } finally { if (value == 2) { return 0; } }}
输出0,
当 try 语句和 finally 语句中都有 return 语句时,try 语句块中的 return 句子会被忽略。这是因为 try 语句中的 return 当返回值执行到本地变量时,返回值将首先暂时存在 finally 语句中的 return 之后,本地变量值变为 finally 语句中的 return 返回值。
追问:try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?public static int getInt() { int a = 10; try { System.out.println(a / 0); a = 20; } catch (ArithmeticException e) { a = 30; return a; /* * return a 当程序执行到这一步时,这里不是returnnn a 而是 return 30;这种返回路径形成了 * 但是,它发现后面有finally,所以继续执行finally的内容,a=40 * 回到以前的路径,继续走returnn 30.返回路径形成后,这里的a不是a变量,而是常量30 */ } finally { a = 40; } return a;}// 执行结果:30
public static int getInt() { int a = 10; try { System.out.println(a / 0); a = 20; } catch (ArithmeticException e) { a = 30; return a; } finally { a = 40; //如果是这样,又形成了一条返回路径,因为只能通过一个return返回,所以直接返回40 return a; }}// 执行结果:40
会执行,在 return 前执行。
在 finally 改变返回值的做法是不好的,因为如果有的话 finally try中的代码块 return 语句不会立即返回调用者,而是记录返回值待 finally 执行代码块后,将其返回给调用器,然后如果是 finally 修改后的返回值将返回修改后的值。
追问:finally 在什么情况下代码不会执行?try { System.out.println("Try to do something"); throw new RuntimeException("RuntimeException");} catch (Exception e) { System.out.println("Catch Exception -> " + e.getMessage()); // 当前正在运行的Java虚拟机终止 System.exit(1);} finally { System.out.println("Finally");}
输出:
Try to do somethingCatch Exception -> RuntimeException
finally情况如下 代码不会执行。
- 虚拟机终止运行
- 程序所在的线程死亡。
- 关闭 CPU。
try-with-resources
吗为了实现资源的自动释放,所需的类别已经实现AutoCloseable
接口。
private static void tryWithResourceTest(){ try (Scanner scanner = new Scanner(new FileInputStream("c:/abc"),"UTF-8")){ // code } catch (IOException e){ // handle exception }}
try 当代码块退出时,它会自动调用 scanner.close 方法,和把 scanner.close 方法放在 finally 如果代码块不同, scanner.close 抛出异常,就会被抑制,抛出仍然是原始异常。