目录
Java的异常机制主要依赖于 try、catch、finally、throw、throws 五个关键字。
Java异常分为两种: Checked异常 和 Runtime异常。
异常处理机制
如果执行try块里的业务逻辑代码时出现异常,系统自动生成一个异常对象,该异常对象被提交给Java运行时环境。这个过程被称为 抛出(throw)异常
当Java运行时环境收到异常对象时,会寻找能处理该异常对象的catch块,如果找到合适的catch块,则把该异常对象交给该catch块处理。这个过程被称为捕获(catch)异常
注意:
不管程序代码块是否处于try块中,甚至包括catch块中的代码,只要执行该代码块时出现异常,系统总会自动生成一个异常对象。
如果程序没有为这段代码定义任何的catch块,则Java运行时环境无法找到处理该异常的catch块,程序就此退出。
异常类的继承体系
-
当Java运行时环境接收到异常对象后,会依次判断该异常对象是否是catch块后异常类或其子类的实例。
如果是,Java运行时环境将调用该catch块来处理异常;
否则再次拿该异常对象和下一个catch块里的异常类进行比较。 -
在通常情况下,如果try块被执行一次,则try块后只有一个catch块会被执行,绝不可能有多个catch块被执行。
除非在循环中使用了continue 开始下一次循环,下一次循环又重新运行try块,这才会导致多次catch块被执行。 -
注意:
try 和 if 语句不一样。
try后的花括号 ({…})不可省略,即使try块中只有一行代码。
与之类似的是,catch块后的 {} 也不可以省略。
try 块里声明的变量是代码块内局部变量,它只在try块内有效,在catch块中不能访问该变量。 -
Java常见的异常类的继承关系
异常(Exception)和 错误(Error),它们都继承Throwable父类
Error错误,一般指与虚拟机相关的问题,如系统崩溃,虚拟机错误,动态链接失败等,这种错误无法恢复或不可能捕获,将导致应用程序中断。 -
注意: 异常捕获时,一定要记住,先捕获小异常,再捕获大异常。
Java 7 提供的多异常捕获
- 在Java 7 之前,每个catch块只能捕获一种类型的异常;但从Java 7之后,一个catch块可以捕获多种类型的异常。
注意:
捕获多种类型的异常时,多个异常类型之间用“|” 隔开。
捕获多种类型的异常时,异常变量有隐式的final修饰,因此程序不能对异常变量赋值。
public static void main(String[] args) {
try {
int a = Integer.parseInt(args[0]);
int b = Integer.parseInt(args[1]);
int c = a / b;
System.out.println("Result = " + c);
} catch (NumberFormatException | IndexOutOfBoundsException | ArithmeticException e) {
System.out.println("The program occur NumberFormatException, IndexOutOfBoundsException and ArithmeticException");
//捕获多异常时,异常变量默认有final修饰。
//下面代码报错。
//e = new ArithmeticException("Test");
} catch (Exception e) {
System.out.println("The unknow exception");
//捕获一种异常时,异常变量没有final修饰。
//下面代码正确。
e = new RuntimeException("Test");
}
}
访问异常信息
所有的异常对象,都包含如下几个常用方法:
getMessage(); 返回该异常的详细描述字符串。
printStackTrace(); 将该异常的跟踪栈信息输出到标准错误输出。
printStackTrace(PrintStream s); 将该异常的跟踪栈信息输出到指定输出流。
getStackTrace(); 返回该异常的跟踪栈信息
使用finally回收资源
注意:
除非在try块, catch块中调用了退出虚拟机的方法。否则不管在try块, catch块中执行怎样的代码,出现怎样的情况,异常处理的finally块总会被执行
Java7 的自动关闭资源的try语句
Java7增强了try语句的功能------它允许在try关键字后紧跟一对圆括号,圆括号可以声明、初始化一个或多个资源。此处的资源,指的是那些必须在程序结束时显示关闭的资源(比如数据库连接,网络连接等),try语句在该语句结束时自动关闭这些资源。
注意:
为了保证try语句可以正常关闭资源,这些资源实现类必须实现 AutoCloseable或 Closeable接口,实现这两个接口就必须实现close()方法。
Checked异常和Runtime异常
Java的异常分为两大类:Checked异常和Runtime异常。
所有的RuntimeException类及其子类的实例被称为 Runtime异常。
不是RuntimeException类及其子类的异常实例被称为 Checked异常。
类别 | 常用异常类 |
---|---|
Error | AssertionError、OutOfMemoryError、StackOverflowError |
RuntimeException | AlreadyBoundException、ClassCastException、ConcurrentModificationException、IllegalArgumentException、IllegalStateException、IndexOutOfBoundsException、JSONException、NullPointerException、SecurityException、UnsupportedOperationException |
CheckedException | ClassNotFoundException、CloneNotSupportedException、FileAlreadyExistsException、FileNotFoundException、InterruptedException、IOException、SQLException、TimeoutException、UnknownHostException |
使用throws声明抛出异常
使用throws声明抛出异常的思路是:
当前方法不知道如何处理这种类型的异常,该异常应该由上一级调用者处理; 如果mian方法也不知道如何处理这种类型的异常,也可以使用throws声明抛出异常,该异常将交给JVM处理。
JVM对异常的处理方法是,打印异常的跟踪栈信息,并终止程序运行。
注意:
-
throws声明抛出只能在方法签名中使用,throws可以声明抛出多个异常类,多个异常类之间以逗号隔开。
-
限制: 方法重写时,两小中的一条。
子类方法声明抛出的异常类型应该是父类方法声明抛出的异常类型的子类或相同,子类方法声明抛出的异常不允许比父类方法声明抛出的异常多。
使用throw抛出异常
-
throw 语句可以单独使用,throw语句抛出的不是异常类,而是一个异常实例,而且每次只能抛出一个异常实例。
-
注意下面例子中的注释:
public class ThrowTest {
public static void main(String[] args) {
try {
// 调用声明抛出 Checked异常的方法,要么显示捕获该异常
// 要么在main()方法中再次声明抛出
throwChecked(3);
} catch (Exception e) {
System.out.println(e.getMessage());
}
// 调用声明抛出Runtime异常的方法,可以显示捕获异常,也可以不理会该异常
throwRuntime(3);
}
public static void throwChecked(int a) throws Exception {
if (a > 0) {
// 自行抛出Exception异常
// 该代码必须处于 try块中,或者处于带有throws声明的方法中
throw new Exception("a 的值大于零,不符合要求");
}
}
public static void throwRuntime(int a) {
if (a > 0) {
// 自行抛出RuntimeException异常
// 既可以显示捕获异常,也可以完全不理会该异常,把该异常交给调用者处理
throw new RuntimeException("a 的值大于零,不符合要求");
}
}
}