异常分类
Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: Error 和 Exception。
Error
编译时程序无法处理的系统错误,如Java虚拟机运行错误(Virtual MachineError)、虚拟机
内存不够错误(OutOfMemoryError)、类定义错误(NoClassDefFoundError)等。这些错误发生
时,Java虚拟机(JVM)一般会选择线程终止。
Exception
受检异常(Checked 异常)
除RuntimeException 及其子类外,其它的 Exception 及其子类,如
ParseException
(字符串转换成 Data 类型异常)- InterruptedException(中断异常)
IOException
(输入输出异常)ClassNotFoundException
(找不到对应的类)SQLException
(提供有关数据库访问错误或其他错误的信息的异常)
编译器要求必须处理的异常,因此程序中一旦出现这类异常,必须显式处理(捕获或抛
出),否则编译无法通过。
不受检异常(Runtime 异常 / UnChecked 异常)
RuntimeException 及其子类,是程序运行时错误,如
NullPointerException
(空指针错误)IllegalArgumentException
(参数错误比如方法入参类型错误)NumberFormatException
(字符串转换为数字格式错误,IllegalArgumentException
的子类)ArrayIndexOutOfBoundsException
(数组越界错误)ClassCastException
(类型转换错误)ArithmeticException
(算术错误)SecurityException
(安全错误比如权限不够)UnsupportedOperationException
(不支持的操作错误比如重复创建同一用户)
编译器不要求强制处理的异常,程序中出现这类异常时,可以不处理。但此时程序奔溃并且
无法恢复。Java 类库中定义的运行时异常类应由程序员预检查来规避,而不是捕获。
异常类常用方法
1、String getMessage() :返回该异常的描述信息(提示给用户)
2、String toString() :返回该异常的类型和描述信息(不用)
3、String getLocalizedMessage() :返回异常对象的本地化信息。用 Throwable 的子类覆盖
这个方法,可生成本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与 getMessage()
返回的结果相同。
4 、void printStackTrace() :打印异常的跟踪栈信息到控制台,包括异常的类型、异常的原
因、异常出现的位置(开发和调试)
异常处理
捕获异常
try...catch...
catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch
块就会被检查。如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参
数的方法是一样。
try {
// 可能会出现异常的代码
} catch (要捕获的异常类型A e) {
// 处理异常的代码:记录日志/打印异常信息/继续抛出异常等
}
多重捕获块:可以在 try 语句后面添加任意数量的 catch 块。如果保护代码中发生异常,异常
被抛给第一个 catch 块。如果抛出异常的数据类型与 ExceptionType1 匹配,它在这里就会被捕
获。如果不匹配,它会被传递给第二个 catch 块,直到异常被捕获或者通过所有的 catch 块。
//多重捕获块
try {
// 可能会出现异常的代码
} catch (要捕获的异常类型A e) {
// 处理异常的代码:记录日志/打印异常信息/继续抛出异常等
} catch (要捕获的异常类型B e) {
// 处理异常的代码:记录日志/打印异常信息/继续抛出异常等
}
try...catch... finall...
try 后可接多个 catch 块,如果没有 catch 块,则必须跟一个 finall 块,无论是否捕获或者处理
异常,finally 的语句都会被执行,在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}finally{
// 程序代码
}
注:catch 不能独立于 try 存在; finally 块并非强制性要求;try 代码后不能既没 catch 块也没
finally 块。
try-with-resource/try-with-resources
JDK7 之后,Java 新增的 try-with-resource 语法糖来打开资源,并且可以在语句执行完毕后
确保每个资源都被自动关闭 。
try 用于声明和实例化资源,catch 用于处理关闭资源时可能引发的所有异常。
try (resource declaration) {
// 使用的资源
} catch (ExceptionType e1) {
// 异常块
}
try-with-resources 语句中可以声明多个资源,方法是使用分号 ; 分隔各个资源:
import java.io.*;
import java.util.*;
class RunoobTest {
public static void main(String[] args) throws IOException{
try (Scanner scanner = new Scanner(new File("testRead.txt"));
PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
while (scanner.hasNext()) {
writer.print(scanner.nextLine());
}
}
}
}
多个声明资源时,try-with-resources 语句以相反的顺序关闭这些资源。 在本例中,
PrintWriter 对象先关闭,然后 Scanner 对象关闭。
抛出异常
throw 关键字用于在代码中抛出异常,而 throws 关键字用于在方法声明中指定可能会抛出的
异常类型。
throw
throw 关键字用于在当前方法中抛出一个异常。以告知调用者当前代码的执行状态。
throw new 异常类("异常信息"); // 终止方法
public void checkNumber(int num) {
if (num < 0) {
throw new IllegalArgumentException("Number must be positive");
}
}
throws
throws 关键字用于在方法声明中指定该方法可能抛出的异常。当方法内部抛出指定类型的异
常时,该异常会被传递给调用该方法的代码,并在该代码中处理异常。
public void readFile(String filePath) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(filePath));
String line = reader.readLine();
while (line != null) {
System.out.println(line);
line = reader.readLine();
}
reader.close();
}
注:一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。
自定义异常
编写自己的异常类注意以下几点:
- 所有的异常类应为 Throwable 的子类;
- 写一个检查性异常类,需继承Exception 类;
- 写一个运行时异常类,需继承 RuntimeException 类。
- 一个异常类和其它任何类一样,包含有变量和方法。
注意事项
- 不要把异常定义为静态变量,因为这样会导致异常栈信息错乱。每次手动抛出异常,我们都需要手动 new 一个异常对象抛出。
- 抛出的异常信息一定要有意义。
- 建议抛出更加具体的异常比如字符串转换为数字格式错误的时候应该抛出
NumberFormatException
而不是其父类IllegalArgumentException
。 - 使用日志打印异常之后就不要再抛出异常了(两者不要同时存在一段代码逻辑中)
- 不要过度使用异常:
对于完全已知的错误,应该编写处理这种错误的代码,增加程序的健壮性
对外部的、不能确定和预知的运行时错误才使用异常
- 不要使用过于庞大的 try 块
- 避免使用 Catch All 语句
- 不要忽略捕获到的异常
面试问题
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");
}
此外,在以下 2 种特殊情况下,finally
块的代码也不会被执行:
- 程序所在线程死亡
- 关闭CPU
如何用 try-with-resources 代替 try...catch... finall... ?
try - catch - finally:
//try - catch - finally
//读取文本文件的内容
Scanner scanner = null;
try {
scanner = new Scanner(new File("D://read.txt"));
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (scanner != null) {
scanner.close();
}
}
try - with - resource:
//try - with - resource
try (Scanner scanner = new Scanner(new File("test.txt"))) {
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
}