目录
一、运行时异常
Java API中预定义了一系列运行时异常类,包括但不局限于以下几种:
- ArithmeticException:算术运算异常,当执行数学运算时发生异常时抛出,例如除以零。
- ArrayIndexOutOfBoundsException:数组下标越界访问异常,当访问数组时索引超出范围时抛出。
- StringIndexOutOfBoundsException:字符串截取下标越界异常,当从字符串中提取子字符串时索引超出范围时抛出。
- ArrayStoreException:数据存储异常,写数组操作时,对象或数据类型不兼容时抛出。
- ClassCastException:类型转换异常,当试图将对象强制转换为不兼容的类型时抛出。
- IllegalArgumentException:方法参数无效异常,当方法参数无效或非法时抛出。
- IllegalThreadStateException:非法线程状态异常,比如线程调用两次start方法时抛出。
- IllegalMonitorStateException:某一对象试图等待对象的监视器,然而本身没有指定的监视器的线程时抛出。
- IllegalStateException:无效状态异常,用于指示虚拟机被破坏或者继续执行操作所需的资源不足的情况,如整数除零等。
它们通常用于处理各种标准的程序错误条件,例如输入/输出错误、空指针访问等。并为开发人员提供了处理常见错误的统一方式。
二、JVM的异常处理机制
我们知道,程序运行过程中,当代码中对一个null对象进行操作的时候,尽管代码中并未声明有关异常对象,也会抛出空指针异常,因为这个异常类对象是非受检的(即非Java编译器检测和处理),因此不用声明。
然而,我们看到空指针异常最终抛出来并输出有关堆栈信息,这到底是谁的手笔呢?
答案是:JVM(Java虚拟机)
Java程序是运行在JVM上的,是由JVM来检测和处理运行时异常的,那是因为JVM规范定义了一套完整的异常处理机制。当程序运行时发生异常情况,JVM会负责捕获这些异常并根据异常的类型和发生位置,执行相应的异常处理逻辑,如抛出异常、终止程序等。
那程序员是否可以干预JVM处理异常呢?
答案是可以的,可以使用 try-catch 块来捕获异常,值得注意的是,try-catch语句块是由JVM执行的。接着按照开发人员自己的方式来处理它。例如:
try {
// Code that may throw an exception
} catch (Exception e) {
// Handle the exception here
}
三、自定义异常类
代码的实现
自定义异常类的实现可以通过继承Exception或其子类来创建和使用。以下是一个简单的例子:
// 自定义异常类
class MyException extends Exception {
public MyException(String message) {
super(message);
}
}
public class Main {
public static void main(String[] args) {
try {
// 一些可能会抛出异常的代码
if(){
throw new MyException("这是一个自定义异常");
}
} catch (MyException e) {
// 处理自定义异常
System.out.println("捕获到自定义异常: " + e.getMessage());
} catch (Exception e) {
// 处理其他异常
System.out.println("捕获到其他异常: " + e.getMessage());
}
}
}
自定义异常类的意义
自定义异常类比标准异常类有什么好处呢?
- 统一风格:通过自定义异常类,你可以统一项目中异常的展示和处理风格,使得整个项目的异常处理更加规范、统一和优雅。
- 隐藏底层异常信息:自定义异常可以提供更具体或更符合特定应用程序需求的错误信息。比如空指针异常NullPointException,可以抛出信息为“xxx为空”定位异常位置,而不用输出堆栈信息。
然而,自定义异常也有其缺点。例如,发现异常、抛出异常以及处理异常的工作必须靠编程人员在代码中利用异常处理机制完成,这会增加一些开发成本和工作量。