异常处理可以提高程序的健壮性、呈现功能给用户更加友好。Java异常机制主要关键字:try、catch、finally、throw、throws。throws关键字在声明方法时候使用,用于声明该方法可能抛出的异常。而throw关键字用于抛出一个实际的异常。Java把异常分为两种,Checked异常(可以在编译阶段被处理的异常)和Runtime异常,强制程序处理所有的Checked异常。
异常处理机制
try…catch捕获异常
下面代码中,try块里面如果有异常,系统就生成一个异常对象,交给Java运行时环境,这就时抛出异常。Java运行时环境收到异常对象后,去找能处理该异常的catch块,交给相应的catch块处理异常,这就是捕获异常。如果无法找到处理该异常的catch块,运行时环境终止,Java程序退出。
try{
//业务代码
}catch(Exception e){
//处理异常
}
注意:try块和catch块仅有一句代码时花括号也不可以省略。try块里定义变量是try块的局部变量,其他部分的程序无法访问。
Java异常类的继承关系
Error错误一般是指与虚拟机有关的问题,如系统崩溃、虚拟机错误、动态链接失败等,这种错误无法恢复或不可捕获,将会导致程序中断。try…catch捕获的异常是Exception类的。
【捕获处理异常示例代码】
注意捕获异常的catch块的顺序,先捕获小异常,再捕获大异常,否则编译报错。
public class Hello{
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("您输入的两整数相除的商是: "+c);
}catch(IndexOutOfBoundsException ie){
System.out.println("输入的参数个数小于2,数组越界!");
}catch(NumberFormatException ne){
System.out.println("数字格式不正确,输入的参数应该是整数");
}catch(ArithmeticException ae){
System.out.println("算术异常");
}catch(Exception e){
System.out.println("未知异常");
}
}
}
Java7新增的多异常捕获
捕获多种异常时,用 | 隔开,异常变量默认用了隐式的final修饰,不能对异常变量重新赋值。
public class Hello{
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("您输入的两整数相除的商是: "+c);
}catch(IndexOutOfBoundsException | NumberFormatException | ArithmeticException e){
System.out.println("数组越界、数字格式不对或算术异常");
//编译报错,异常变量默认有final修饰,不可重新赋值
//e = new IndexOutOfBoundsException();
}catch(Exception e){
System.out.println("未知异常");
e = new Exception();//这是正确的,不是多异常捕获,异常变量没有final隐式修饰
}
}
}
访问异常信息
所有的异常都有获取或打印异常信息的方法,查看他们继承的父类Throwable就知道了,查看官方文档如下:
public class Hello{
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("您输入的两整数相除的商是: "+c);
}catch(IndexOutOfBoundsException | NumberFormatException | ArithmeticException e){
System.out.println("数组越界、数字格式不对或算术异常");
System.out.println(e.getMessage());
e.printStackTrace();
}catch(Exception e){
System.out.println("未知异常");
}
}
}
使用finally回收资源
- try块里如果打开了物理资源(比如数据库连接、网络连接、磁盘文件等),需要显式回收,Java的垃圾回收机制不回收物理资源,只回收堆内存中的对象占用的内存。
- 不管try块的代码是否有异常,不管catch块是否被执行,甚至try块、catch块中执行了return语句,finally块总会被执行(有一种特例:在catch块执行System.exit(1)退出虚拟机,finally块就不执行了)。有try块的话,catch块和finally块必须至少有其一。示例代码见该书375页。
- 一般不要在finally块中使用return语句或throw语句,一旦finally块中使用了return语句或throw语句,会导致try块、catch块的return、throw语句失效。因为,try块、catch块遇到return、throw语句的时候就会找finally块,如果没有finally块,就执行return或throw语句结束方法,如果有finally块,就先去执行finally块,再跳回到执行try块、catch块代码,而如果finally块里有return语句或throw语句把方法结束掉了,那就跳不回去了!示例代码见该书376页。
异常处理嵌套
try、catch、finally三个块中都可以进行try…catch…finally嵌套,没有特别限制。
Java7自动关闭资源的try语句
与375页关闭资源语句对比,377页Java7自动关闭资源的方法更加简洁。具体看377页讲解部分及示例代码。
Checked异常与Runtime异常
- Runtime异常:所有Runtime类及其子类的实例
- Checked异常:非Runtime异常
对于程序中的Checked异常,Java要求必须显式捕获或者显式声明抛出该异常。
使用throws声明抛出异常
使用throws声明抛出异常的思路是:当前方法不处理异常,交给上一级调用者处理;如果main方法也不知道怎么处理异常,交给JVM处理。JVM处理异常的方式是:打印异常的跟踪栈信息,终止程序运行。
程序中使用了throws声明抛出异常,就表明本级方法不处理该异常,让上一级调用者处理,就不需要再用显示的try…catch捕获异常了(笔者试了一下,同时用也行!!)。
方法重写时,子类方法声明抛出的异常应该比父类声明抛出的异常类型小或相同。
使用throw抛出异常
程序出错的时候,系统会自动抛出异常;除此之外,系统也允许程序用throw语句自行抛出异常。注意throw抛出的异常类实例,与throws声明抛出异常类不同。
如果throw语句抛出的时Checked异常,该throw语句要么用try块显式处理,要么放在一个带throws声明抛出的方法中。如果throws语句抛出的是Runtime异常,该throw语句可以放在try块或声明抛出的方法中,也可以完全不理会该异常。示例代码见381页。