尽管人人希望自己身体健康,处理的事情都能顺利进行,但在实际生活中总会遇到各种状况,比如感冒发烧,工作时电脑蓝屏、死机等。同样,在程序运行的过程中,也会发生各种非正常状况,例如,程序运行时磁盘空间不足、网络连接中断、被装载的类不存在等。针对这种情况, Java语言引入了异常,以异常类的形式对这些非正常情况进行封装,通过错误处理机制对程序运行时发生的各种问题进行处理。
Java的错误处理机制是通过异常(Exception)来实现的。异常是在程序执行过程中出现的问题或错误的一种表示,它可以帮助我们识别和处理程序中的异常情况,从而保证程序的稳定性和可靠性。
异常类层次结构:
Java中的异常被组织成一个类层次结构。所有的异常都是Throwable类的子类,Throwable又分为两个子类:Error(错误类)和Exception(异常类)。其中,Error表示严重的系统级问题,通常是由虚拟机或底层资源引起的,例如内存溢出(OutOfMemoryError)。而Exception则表示可以被捕获和处理的异常情况,包括检查异常和运行时异常。
在Exception(异常类)类的众多子类中有一个特殊的子类—RuntimeException类,RuntimeException类及其子类用于表示运行时异常。 Exception类的其他子类都用于表示编译时异常。Java提供了大量的异常类,这些类都继承自java.lang.Throwable类。接下来通过一张图展示Throwable类的继承体系。
Throwable类中的常用方法如下表。
方法声明 | 功能描述 |
String getMessage() | 返回异常的消息字符串 |
String toString() | 返回异常的简单信息描述 |
void printStackTrace() | 获取异常类名和异常信息,以及异常出现在程序中的位置, 把信息输出在控制台。 |
检查异常:
这些异常是在编译时强制检查的异常。它们通常涉及到外部资源的操作,如文件I/O、网络连接等。开发者必须显式地处理或声明抛出这些异常。通过捕获和处理检查异常,可以使代码更加健壮,防止未处理异常导致程序崩溃或不可预测的结果。开发者需要使用 try-catch 块来捕获这些异常,并进行相应的处理。
try…catch具体语法格式如下:
try {// 可能引发异常的代码块
} catch (异常类型1 异常变量1) {// 处理异常类型1的情况
} catch (异常类型2 异常变量2) {// 处理异常类型2的情况
} catch (异常类型3 异常变量3) {// 处理异常类型3的情况
}
注意:catch代码块需要一个参数指明它所能够接收的异常类型,这个参数的类型必须是Exception类或其子类。
运行时异常:
这些异常是不需要显式处理的异常,也不需要在方法签名中声明抛出。它们主要包括空指针引用(NullPointerException)、数组下标越界(ArrayIndexOutOfBoundsException)、类型转换错误(ClassCastException)等。运行时异常通常由程序逻辑错误导致,因此应该通过改进代码逻辑来预防发生这些异常。
下面是一个使用 try-catch 语句模仿数组下标越界的代码示例:
public class myClass {public static void main(String[] args) {try {int[] numbers = {1, 2, 3};System.out.println(numbers[4]); // 数组越界,会引发 ArrayIndexOutOfBoundsException 异常} catch (Exception e) {System.out.println("捕获到异常:" + e);//输出:捕获到异常:java.lang.ArrayIndexOutOfBoundsException: 4}}
}
异常处理语句:
Java提供了try-catch-finally的异常处理语句来捕获异常并进行相应的处理。在try块中编写可能会抛出异常的代码,如果发生了对应的异常,就会跳转到与之匹配的catch块中进行处理。catch块可以捕获特定类型的异常,也可以使用多个catch块按顺序处理不同类型的异常。finally块用于定义无论是否发生异常都要执行的代码,例如释放资源、关闭文件等。无论是否有异常发生,finally块中的代码都会执行。这样可以进行必要的清理工作,避免资源泄漏等问题。
注意:finally中的代码块在一种情况下是不会执行的,那就是在try...catch中执行了System.exit(0)语句。System.exit(0)表示退出当前的Java虚拟机,Java虚拟机停止了,任何代码都不能再执行了。
下面是一个使用 try-catch-finally 异常处理语句的代码示例:
public class myClass {public static void main(String[] args) {try {int result = divideNumbers(10, 0); // 调用自定义方法,可能触发 ArithmeticException 异常System.out.println("结果:" + result);} catch (ArithmeticException e) {System.out.println("捕获到异常:" + e);} finally {System.out.println("无论是否发生异常,finally 块中的代码都会执行");}System.out.println("try-catch-finally 语句块之外的代码");}public static int divideNumbers(int a, int b) {return a / b; // 若除数为 0,则会触发 ArithmeticException 异常}
}
运行结果:
捕获到异常:java.lang.ArithmeticException: / by zero
无论是否发生异常,finally 块中的代码都会执行
try-catch-finally 语句块之外的代码
抛出异常:
在方法中,我们可以使用throw关键字主动抛出异常。通过throw语句,我们可以在任何需要的地方抛出异常,并将其传递给上层调用者来处理。抛出异常的过程中,当前方法的执行将被中断,然后异常被传递给调用堆栈中的上一级调用方法。
throws关键字声明抛出异常的语法格式如下:
修饰符 返回类型 方法名(参数列表) throws 异常类型1, 异常类型2, ... {// 方法体
}
其中:
- 修饰符:表示方法的可见性和其他修饰符(例如 public、private、static 等)。
- 返回类型:表示方法返回值的类型。
- 方法名:表示方法的名称。
- 参数列表:表示方法接收的参数。
- 异常类型1, 异常类型2, ...:表示方法可能抛出的异常类型。多个异常类型之间使用逗号分隔。
下面是一个使用 throw 关键字的代码示例:
public class myClass {public static void main(String[] args) {try {int result = divideNumbers(10, 0); // 调用自定义方法,可能触发 ArithmeticException 异常System.out.println("结果:" + result);} catch (ArithmeticException e) {System.out.println("捕获到异常:" + e);//输出:捕获到异常:java.lang.ArithmeticException: / by zero} finally {System.out.println("无论是否发生异常,finally 块中的代码都会执行");//输出:无论是否发生异常,finally 块中的代码都会执行}System.out.println("try-catch-finally 语句块之外的代码");}public static int divideNumbers(int a, int b) throws ArithmeticException{if (b == 0) {throw new ArithmeticException("除数不能为零"); // 抛出 ArithmeticException 异常对象} else {int result = a / b;System.out.println("结果:" + result);}return a;}
}
运行结果:
捕获到异常:java.lang.ArithmeticException: 除数不能为零
无论是否发生异常,finally 块中的代码都会执行
try-catch-finally 语句块之外的代码
自定义异常:
除了Java内置的异常类,开发者还可以根据需要自定义异常类。自定义异常类需要继承Exception或其子类,并根据具体需求添加自己所需的字段和方法。自定义异常可以更好地满足业务需求,提供更准确的异常信息和处理方式。
在实际开发中,如果没有特殊的要求,自定义的异常类只需继承Exception类,在构造方法中使用super()语句调用Exception的构造方法即可。
自定义异常类中使用throw关键字在方法中声明异常的实例对象,格式如下:
throw Exception异常对象
下面是一个简单的自定义异常类的示例:
// 自定义异常类
class NegativeNumberException extends Exception {public NegativeNumberException(String message) {super(message);}
}// 示例类
public class myClass {// 计算平方根的方法public static double calculateSquareRoot(double number) throws NegativeNumberException {if (number < 0) {throw new NegativeNumberException("输入不能为负数");} else {// 使用 Math.sqrt() 方法计算平方根double squareRoot = Math.sqrt(number);return squareRoot;}}// 主方法public static void main(String[] args) {double input = -5.0;try {// 调用 calculateSquareRoot 方法double result = calculateSquareRoot(input);System.out.println("平方根结果:" + result);} catch (NegativeNumberException e) {System.out.println("捕获到自定义异常:" + e.getMessage());// 其他异常处理逻辑// ...}}
}