Java中的异常处理机制的简单原理和应用
异常是指 Java 程序运行时(不是编译期)所发生的非正常的情况或者错误。
Java 使用面向对象的方式来处理异常,它把程序运行中可能发生的每个异常也都分别封装到一个对象中,该对象中包含有异常的信息。
异常的根类是java.lang.Throwable
。Throwable 有两个子类,分别是 Error 和 Exception。Java中还可以自定义异常。
Error 和 Exception 的区别
Error 一般指与虚拟机相关的问题,如系统崩溃、虚拟机错误、内存空间不足等等。对于这类错误导致的应用程序中断,仅仅依靠程序本身无法恢复和预防。如果遇到这样的错误,建议终止程序的运行。
Exception 表示程序可以处理的异常,可以进行捕获和恢复。遇到这类型的异常,应该尽可能地进行处理使程序恢复运行。Exceptio 分为运行时异常(RuntimeException)和其他异常。
RuntimeException、Error 以及它们的子类都属于免检异常。所有的其他异常都属于必检异常。开发中必须对必检异常进行处理,要么使用 try-catch 捕获,要么在方法头进行声明。
Java 异常处理机制的三种操作
声明一个异常,抛出一个异常和捕获一个异常。
声明异常
因为任何代码都有可能发生系统错误和运行时错误,因此Java不要求在方法中显式地声明 Error 和 RuntimeException。但是,方法要抛出的其他异常必须在方法头中进行显式的声明,这样,方法的调用者就会被告知进行异常处理。
为了在方法中声明一个异常,就要在方法头中使用关键字 throws。
public void method() throws Exception
关键字 throws 表明方法 method 可能会抛出一个 Exception。如果方法可能抛出多个异常,各个异常之间使用逗号进行分隔。
如果方法没有在父类中声明异常,那么就不能在子类中对其进行继承来声明异常。即凡是子类中声明的异常必须在父类中声明过。
抛出异常
检测到错误的程序可以创建一个合适的异常类型的实例并抛出它。
比如,用于检测数组下标是否越界可以使用下面的方式:
private void checkIndex(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("index: " + index + " out of bounds, size: " + size);
}
}
捕获异常
当抛出一个异常时,就可以使用 try-catch 进行捕获并处理。
JDK1.7 之后,为了简化多个异常的处理,对异常捕获进行了书写优化,简化异常的代码书写规则如下:
catch(Exception1 | Exception2 | ... | Exception e){ }
try-catch-finally
try {
// 可能出现异常的代码块
} catch (SpecificException e) {
// 异常处理
} catch(Exception e) {
// 异常处理
} finally {
// 无条件执行的语句,通常用于资源回收
}
说明:
- 不要在循环内写 try-catch
- try块,用于捕获异常,后面可以接零到多个 catch块。如果没有 catch块,则必须有 finally块。
- catch块,用于处理 try 捕获的异常,catch块 将子类异常写在前面,父类异常写在后面。
- finally块,无论是否捕获或者处理异常,finally 块中的语句都会被执行。即使 try 和 catch 块中存在 return 语句,finally块也会在方法返回之前执行。
finally 块不会执行的特殊情况:
- finally 语句块中发生了异常;
- 前面的代码中执行了 System.exit() 导致程序退出;
- 程序所在的线程死亡;
- CPU 关闭。
throw 和 throws 的区别
throw 关键字用来在程序中明确地抛出异常,相反地,throws 用来声明方法不能处理的异常。每一个方法都必须指定哪些异常不能处理,所以方法的调用者才能够确保处理可能的异常,多个异常之间使用逗号分隔。
《Java语言程序设计》(基础篇):
throw 语句类似于方法的调用,但不同于方法调用的是,它调用的是 catch 块。从某种意义上讲,catch块就像带参数的方法定义,这些参数匹配抛出的值的类型。但是,它不像方法,在执行完catch块之后,程序控制没有返回throw语句,而是执行catch块之后的下一条语句。