Java异常处理机制详解
异常(Exception)是在程序执行期间发生的特殊事件。一般来说,异常会中断正在执行的程序的正常指令流。
在程序中,异常可能产生于开发者没有预料到的各种情况,或者超出开发者的可控范围,如除数为 0、数组下标越界、试图打开一个不存在的文件等。为了能够及时有效地处理程序中的运行异常,Java 专门引入了异常机制。
异常机制(Exception Handling)是在程序执行期间对异常的发生做出响应的过程。Java 中存在两种主要的指令流,一种是正常指令流,另一种是异常处理指令流。当程序在运行过程中出现意外情况时,系统会自动生成一个异常对象来通知程序。此时,正常的程序控制流程停止运行并开始搜索匹配的异常处理逻辑块(catch 语句块)。当找到一个匹配的 catch 语句块时,将执行该块,异常对象作为参数传递给该块,并在执行 catch 语句块之后继续正常地执行程序。
若在当前方法级别中没有找到 catch 语句块,则在调用者方法级别中继续搜索,直到找到匹配的 catch 语句块。若最终没有发现相匹配的 catch 语句块,则将异常交由 Java 虚拟机处理,通常会终止当前线程,进而可能终止整个 Java 程序。
开发者可以通过提供异常处理逻辑块,在 Java 程序中预置各类潜在异常的处理方案,提升所开发的 Java 程序的健壮性。同时,开发者可以基于 Java 异常机制构建统一的错误报告模型,记录程序运行过程中产生的异常信息,为异常排查和异常修复提供依据,提高异常修复效率。
Throwable 位于异常类层次结构的顶层,下面的两个异常分支 Exception 和 Error 分别表示异常和错误,如下图所示。
图 1 Java的异常的分类
Exception 代表可控的异常,一般是程序编码错误或外在因素导致的、可以被开发者预见并提供异常解决方案的各类问题。这些问题能够被系统捕获并处理,从而避免应用程序非正常中断,如对负数开平方根、除数为 0 等。
常见的异常类如下表所示。
Error 类定义了在通常环境下不希望被程序捕获的问题,通常是与虚拟机相关的问题,如虚拟机错误、堆栈溢出和系统崩溃等,这些错误系统无法捕获并处理。对于这些错误开发者无能为力,将导致应用程序中断。
Java 中的 Exception 类可以进一步细分为运行时异常(也称为非受检异常)和编译时异常(也称为抽检异常)。
运行时异常都是 RuntimeException 类及其子类异常,如 NullPointerException、IndexOutOfBoundsException 等。此类异常是非受检异常,即编译器不会检查开发者是否为可能出现的异常提供了处理逻辑。程序可以选择捕获处理,也可以不处理。此类异常一般是由程序逻辑错误引起的,开发者应该从逻辑角度尽可能避免这类异常的发生。
编译时异常是指 RuntimeException 以外的异常,属于 Exception 类及其子类异常。从程序语法角度讲必须处理的异常如果不处理,程序就不能编译通过,如 IOException、ClassNotFoundException,以及用户自定义的 Exception 异常等。
接下来通过实例来介绍异常类的常用方法。
【实例】异常类的常用方法。
在程序中,异常可能产生于开发者没有预料到的各种情况,或者超出开发者的可控范围,如除数为 0、数组下标越界、试图打开一个不存在的文件等。为了能够及时有效地处理程序中的运行异常,Java 专门引入了异常机制。
异常机制(Exception Handling)是在程序执行期间对异常的发生做出响应的过程。Java 中存在两种主要的指令流,一种是正常指令流,另一种是异常处理指令流。当程序在运行过程中出现意外情况时,系统会自动生成一个异常对象来通知程序。此时,正常的程序控制流程停止运行并开始搜索匹配的异常处理逻辑块(catch 语句块)。当找到一个匹配的 catch 语句块时,将执行该块,异常对象作为参数传递给该块,并在执行 catch 语句块之后继续正常地执行程序。
若在当前方法级别中没有找到 catch 语句块,则在调用者方法级别中继续搜索,直到找到匹配的 catch 语句块。若最终没有发现相匹配的 catch 语句块,则将异常交由 Java 虚拟机处理,通常会终止当前线程,进而可能终止整个 Java 程序。
开发者可以通过提供异常处理逻辑块,在 Java 程序中预置各类潜在异常的处理方案,提升所开发的 Java 程序的健壮性。同时,开发者可以基于 Java 异常机制构建统一的错误报告模型,记录程序运行过程中产生的异常信息,为异常排查和异常修复提供依据,提高异常修复效率。
Java的异常的分类
在 Java 中,所有异常类型都是内置类 java.lang.Throwable 的子类,即 Throwable 是所有异常和错误的超类。Throwable 位于异常类层次结构的顶层,下面的两个异常分支 Exception 和 Error 分别表示异常和错误,如下图所示。
图 1 Java的异常的分类
Exception 代表可控的异常,一般是程序编码错误或外在因素导致的、可以被开发者预见并提供异常解决方案的各类问题。这些问题能够被系统捕获并处理,从而避免应用程序非正常中断,如对负数开平方根、除数为 0 等。
常见的异常类如下表所示。
异常类 | 说 明 |
---|---|
NullPointerException | 空指针异常 |
ClassCastException | 类型转换异常 |
ArraylndexOutOfBoundsException | 数组越界异常 |
ArithmeticException | 算术异常 |
InternalException | Java 系统内部异常 |
lOException | 在一般情况下不能完成输入/输出操作时所产生的异常 |
EOFException | 打开文件没有数据可以读取时所产生的异常 |
FileNotFoundException |
在文件系统中找不到文件名称或路径时所产生的异常 |
ClassNotFoundException | 找不到类或接口时所产生的异常 |
IllegalAccessException | 类定义不明确时所产生的异常 |
NegativeArraySizeException | 指定数组维数为负值时所产生的异常 |
NumberFormatException | 数字格式化时所产生的异常 |
Error 类定义了在通常环境下不希望被程序捕获的问题,通常是与虚拟机相关的问题,如虚拟机错误、堆栈溢出和系统崩溃等,这些错误系统无法捕获并处理。对于这些错误开发者无能为力,将导致应用程序中断。
Java 中的 Exception 类可以进一步细分为运行时异常(也称为非受检异常)和编译时异常(也称为抽检异常)。
运行时异常都是 RuntimeException 类及其子类异常,如 NullPointerException、IndexOutOfBoundsException 等。此类异常是非受检异常,即编译器不会检查开发者是否为可能出现的异常提供了处理逻辑。程序可以选择捕获处理,也可以不处理。此类异常一般是由程序逻辑错误引起的,开发者应该从逻辑角度尽可能避免这类异常的发生。
编译时异常是指 RuntimeException 以外的异常,属于 Exception 类及其子类异常。从程序语法角度讲必须处理的异常如果不处理,程序就不能编译通过,如 IOException、ClassNotFoundException,以及用户自定义的 Exception 异常等。
异常的常用方法
java.lang.Throwable 是所有 Exception 类和 Error 类的父类,其中声明的方法被所有 Exception 类继承。Throwable 类中声明的常用的方法有 getMessage()、getLocalizedMessage()、getCause()和printStackTrace(),下面介绍这些方法:- getMessage():在 Throwable 类中定义了一个使用 private 修饰的 String 类型的属性,名为 detailMessage,用于记录本次异常的详细信息,也可以用来记录产生本次异常的原因。调用 getMessage() 方法可以返回 detailMessage 属性的值,若该异常没有封装详细信息则返回null。
- getLocalizedMessage():返回 Throwable 类的本地化描述,子类可能会覆盖该方法以便产生特定于本地的消息,对于未覆盖该方法的子类,默认返回调用 getMessage() 方法的结果。
- getCause():若当前异常是由另一个异常导致的,则返回该异常对象,反之则返回 null。
- printStackTrace():打印异常信息在程序中出错的位置及原因。System.out.println(e) 方法的作用也是打印出异常,并且输出在哪里出现异常。而 e.printStackTrace() 方法不仅打印出异常,还将显示出更深的调用信息。
接下来通过实例来介绍异常类的常用方法。
【实例】异常类的常用方法。
public class Test { public static void main(String[] args) { // 新建异常对象,传入自定义的消息 RuntimeException el = new RuntimeException("el 的自定义异常原因"); System.out.println("------el.getMessage()-----"); System.out.println(el.getMessage()); System.out.println("------el.getCause()-----\n"); // 当无其他异常导致该异常时,返回null System.out.println(el.getCause()); System.out.println("------el.printStackTrace()-----\n"); // 该方法中包含对打印方法的调用,信息中包含方法调用轨迹 // 该方法默认使用 System.err 打印信息 // 本实例指定使用 System.out 打印信息 el.printStackTrace(System.out); // 创建一个新的异常对象,设置由el导致了该异常 RuntimeException e2 = new RuntimeException("e2 的自定义异常原因", el); System.out.println("-----e2.getCause()-----"); // 返回el的信息 System.out.println(e2.getCause()); System.out.println("-----e2.printStackTrace()-----"); // 信息中会使用"Caused by:"显示导致当前异常的底层异常信息 e2.printStackTrace(System.out); } }运行结果为:
------el.getMessage()-----
el 的自定义异常原因
------el.getCause-----
null
------el.printStackTrace()-----
java.lang.RuntimeException: el 的自定义异常原因
-----e2.getCause()-----
java.lang.RuntimeException: el 的自定义异常原因
-----e2.printStackTrace()-----
java.lang.RuntimeException: e2 的自定义异常原因
Caused by: java.lang.RuntimeException: el 的自定义异常原因
at Test.main(Test.java:23)