Java将异常分为两类:Checked
异常和Runtime
异常。Java认为前者都是可以在编译阶段被处理的异常,所以它强制程序处理所有的Checked
异常;而Runtime
异常则无需处理。
一、异常概述
try{
//业务实现代码
}
catch (Exception e){
}
finally {
}
try
块里面的业务逻辑代码出现异常,则系统自动生成一个异常对象,该异常对象被提交给Java运行时环境,这个过程成为抛出(throw
)异常。Java运行时环境去寻找处理该异常对象的catch
块,找到合适的则把该异常对象交给该块处理,成为捕获(catch
)异常;找不到则运行时环境终止。
Java异常类继承关系
try
块后面的花括号不能省略,它定义的变量在catch
块中不能访问。catch
也不能省略花括号。
二、异常处理机制
多异常捕获
Java7开始,一个catch可以捕获多种类型的异常。需要注意:
- 多种异常类型之间用竖线隔开
- 异常变量有隐式的final修饰,程序不能对异常变量重新赋值。
public class Test{
public static void main(String[] args) {
try{
int a = Integer.parseInt(args[0]);
int b = Integer.parseInt(args[1]);
int c = a/b;
}
catch (IndexOutOfBoundsException|NumberFormatException|ArithmeticException e){
//下面报错
e = new ArithmeticException("test");
}
catch (Exception e){
e = new RuntimeException("test");
}
}
}
访问异常信息
- getMessage()
- printStackTrace()
- printStackTrace(PrintStream s)
- getStackTrace()
使用finally回收资源
Java回收机制不会回收物理资源,只能回收对内存中对象所占用的内存。
即使是try
或者catch
语句里面有return
语句,finally
块也会执行。
异常处理语法结构中只有try
块是必须的,catch
和finally
块至少出现一个,也可以同时出现。
import java.io.FileInputStream;
import java.io.IOException;
public class Test{
public static void main(String[] args) {
FileInputStream fis = null;
try{
fis = new FileInputStream("a.txt");
}
catch(IOException e){
System.out.println(e.getMessage());
//下面方法继续执行finally
//return;
//下面方法退出虚拟机,不会执行finally
//System.exit(1);
}
finally{
if(fis!=null){
try{
fis.close();
}
catch(IOException e){
System.out.println(e.getMessage());
}
}
}
}
}
一旦在finally
块中使用throw
和return
语句,try
、catch
语句中的相应语句将会失效。
自动关闭资源的try语句
Java7开始,允许将那些必须在程序结束时显式关闭的资源放在try
后面的圆括号里面。
这些资源实现类必须实现
AutoCloseable
或Closeable
接口,这两个接口有close
方法。
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.PrintStream;
public class Test{
public static void main(String[] args) {
try(
BufferedReader br = new BufferedReader(new FileReader("test.txt"));
PrintStream ps = new PrintStream(new FileOutputStream("a.txt"));
){
ps.println("this is nothing");
}
}
}
Java7几乎把所有的资源类进行了改写,都实现了
AutoCloseable
或Closeable
接口
三、Checked异常和Runtime异常
所有RuntimeException
类及其子类的实例被称为Runtime
异常,其他的被称为Checked
异常。
Checked
异常体现了Java的设计哲学--没有完善错误处理的代码根本就不会被执行。该类异常的处理方式:
- 当前方法明确知道该怎么处理,try,catch
- 当前方法不知道该怎么处理,应该在定义该方法时抛出该异常
使用throws
声明抛出异常
如果main
方法也不知道怎么处理异常,则交给jvm处理。jvm打印异常的跟踪栈信息,并终止程序运行。
throws ExceptionClass1, ExceptionClass2...
子类方法声明抛出的异常应该是父类方法声明抛出的异常类型的子类或相同,子类方法声明的异常不允许比父类方法声明抛出的异常多。
在大部分时候推荐使用Runtime异常
四、使用throw抛出异常
throw
语句抛出的不是异常类,而是异常实例,而且每次只能抛出一个异常实例。
throw ExceptionInstance;
规则:
- throw语句抛出的是Checked异常,该throw语句要么处于try块里,显式捕获该异常,要么放在一个带throws声明抛出的方法中,把该异常交给该方法的调用者处理
- throw语句抛出的是Runtime异常,则该语句无需放在try块里,也无需放在throws方法中;
- 程序既可以显式使用try...catch捕获并处理该异常,也可以完全不理会,把该异常交给该方法的调用者。
自定义异常类
用户自定义异常应该继承Exception
类。
通常需要实现两个构造器,如下:
public class AuctionException extends Exception{
public AuctionException(){}
public AuctionException(String msg){
super(msg);
}
}
catch和throw同时使用
在实际应用中往往需要更复杂的处理方式:
当一个异常出现时,必须由几个方法协作才能完全处理该异常。
异常链
自定义异常类需要多实现一个方法。
public class TestException extends Exception{
public TestException(){}
public TestException(String msg){
super(msg);
}
public TestException(Throwable t){
super(t);
}
}
五、Java的异常跟踪栈
六、异常处理规则
- 使程序代码混乱最小化
- 捕获并保留诊断信息
- 通知合适的人员
- 采用合适的方式结束异常活动
不要过度使用异常
对于普通的错误,应该编写处理错误的代码,对于外部的、不能确定和预知的运行时错误才使用异常。
避免过大的try语句
避免使用catchAll语句
不要忽略捕获到的异常
- 处理异常
- 重新抛出异常
- 在合适的层处理异常
网友评论