Java 异常机制
Java异常机制主要依赖 try, catch, finally, throw, throws 五个关键字。
使用 try catch捕获异常。
try块中业务代码出现异常, 系统自动生成一个异常对象,运行时会抛出(throw)这个异常。
当Java运行环境接收到异常对象时,寻找能处理该异常的catch块,找到则捕获(catch)异常, 找不到时运行环境终止。
异常类Exception
Java把非正常情况分为两种:
异常(Exception)和错误(Error)。都继承Throwable类。
Error错误, 一般指与虚拟机相关的问题, 如系统崩溃,虚拟机错误,动态链接失败等,这种错误无法恢复或不可能捕获,将导致应用程序中断,通常应用程序无法处理这些错误。
异常类的继承关系:
try块后可以跟多个catch块。捕获异常时,会依次判断对象是否是catch块后异常类或其子类的实例。找到则处理。
Date date = null;
try {
System.out.println(date.after(new Date()));
}
catch (NullPointerException e){
System.out.println("null");
}
catch (Exception e){
System.out.println("exception");
}
捕获异常时, 先处理小异常,再处理大异常。
父类异常应该排在子类异常后面, Exception 应该放在最后。
try {
System.out.println(date.after(new Date()));
}
catch (RuntimeException e){
}
//编译报错, NullPointerException是RuntimeException 子类
catch (NullPointerException e){
System.out.println("null");
}
捕获多异常
catch可以捕获多种类型异常。
多种类型异常用 | 隔开。
捕获多种类型异常时, 异常变量有隐式的 final修饰,不能重新赋值
try {
System.out.println(date.after(new Date()));
}
catch (NullPointerException | NumberFormatException ne){
System.out.println("null");
ne = new NumberFormatException("test"); //报错,不能重新赋值
}
catch (Exception e){
System.out.println("exception");
e = new RuntimeException("test");
}
访问异常信息
catch 异常后,通过下面方法获取异常信息:
- getMessage(): 获取异常描述信息
- printStackTrace(): 异常跟踪栈信息输出到标准错误
- printStackTrace(PrintStream s): 输出到指定流
- getStackTrace(): 返回异常跟踪栈信息
Date date = null;
try {
System.out.println(date.after(new Date()));
}
catch (NullPointerException | NumberFormatException ne){
System.out.println(ne.getMessage());
ne.printStackTrace();
}
finally
Java的垃圾回收机制不会回收任何物理资源,只能回收堆内存中的对象所占用的内存。
try块里打开的一些物理资源,如数据库连接,网络连接,磁盘文件等。需要显式的回收。
可以放在finally中回收。
try块是必须的, catch和 finally块都是可选的,至少出现其中之一,也可同时出现。finally必须在catch后面。
finally块总是在执行完 try, 和 catch后执行, 即使 try和 catch中有 return, finally还是会执行。 如果调用 System.exit() 退出虚拟机,则不执行。
通常情况下, finally中不要使用 return 和 throw导致方法终止的语句, 会使try, catch中语句失效。
public class ExceptionTest {
public static void main(String[] args){
boolean a = test();
System.out.println(a);
}
public static boolean test(){
try {
return true;
}
finally {
return false;
}
}
//Output
false
try关闭资源
try关键词后面可以跟(),
里面可以声明,初始化一个或多个资源, 比如数据库连接,网络连接,本地磁盘等, try执行完会自动关闭这些资源。
这些资源实现类必须实现 AutoCloseable 或 Closeable接口, 实现这两个接口,就必须实现 close()方法。
public interface Closeable extends AutoCloseable {
public void close() throws IOException;
}
下面IO流例子
public class ExceptionTest {
public static void main(String[] args) throws IOException{
try (
BufferedReader br = new BufferedReader(new FileReader("abc.txt"));
PrintStream ps = new PrintStream(new FileOutputStream("abc2.txt"));
){
System.out.println(br.readLine());
ps.println("hhhh");
}
}
}
这种 try 会自动关闭资源, 因此可以没有 catch块和 finally块。
Java 7 几乎所有资源类, 文件IO类, JDBC编程的 Connection 等,都实现了 AutoCloseable 或 Closeable 接口。
Checked 和 Runtime 异常
Java异常分为两大类, Checked异常和Runtime异常,
所有 RuntimeException 异常及其子类,都称为 Runtime异常, 其他称为Checked异常。
Checked异常必须处理,否则编译不通过。
Checked异常处理方式有两种:
- try - catch 处理异常
- 方法声明时 throws 异常
throws
当前方法不知道如何处理异常时, 使用 throws 声明抛出异常。
throws 只能在方法前面之后。
多个异常逗号分隔。
public class ExceptionTest {
public static void main(String[] args) throws IOException{
file();
}
public static void file() throws IOException{
FileInputStream fileInputStream = new FileInputStream("a.txt");
}
}
方法调用者要么try块中捕获, 要么也使用 throws 抛出。
子类方法声明抛出的异常应该是父类方法声明抛出异常的子类或相同,子类方法声明抛出的异常不允许比父类抛出的异常多。
public class ExceptionTest {
public void info() throws IOException{
FileInputStream fileInputStream = new FileInputStream("a.txt");
}
}
class Sub extends ExceptionTest{
@Override
public void info() throws Exception { //报错,子类异常比父类异常大
}
}
throw
程序自行抛出异常, throw Exception
public void info() throws IOException{
throw new IOException("file empty");
}
public void t1(int a){
if(a > 0){
throw new RuntimeException("a > 0");
}
}
Runtime异常比Checked异常灵活性更好。
还可以在catch块中throw。
public class ExceptionTest {
public static void main(String[] args) throws FileNotFoundException{
try{
new FileOutputStream("a.txt");
}
catch (Exception e){
e.printStackTrace();
throw new FileNotFoundException("a");
throw e; //编译器检查,这儿抛出FileNotFoundException
}
}
}
自定义异常类
用户自定义异常类都应该继承 Exception 基类,如果自定义Runtime异常, 应该继承 RuntimeException。
定义时,需要提供两个构造器: 一个无参构造器; 另一个带一个字符串构造器,描述信息,也是 getMessage() 的返回值。
public class TestException extends Exception {
public TestException() {
}
public TestException(String message) {
super(message);
}
//创建接收Throwable的参数
public TestException(Throwable cause) {
super(cause);
}
}