第12章 通过异常处理错误

异常处理机制

Java提出一种假设,如果程序可以顺利执行,那就一切正常,把系统的业务实现代码放在 try 块中定义,所有的异常处理逻辑放到 catch 块中处理。

try{
//业务实现代码
......
}
catch (Exception e){
//异常处理
......
}

如果执行 try 块里的业务逻辑代码出现异常,系统自动生成一个异常对象,该异常对象被提交给Java运行时环境,这个过程被称为抛出(throw)异常。当Java运行时环境收到异常对象时,会寻找处理该异常对象的catch块,如果找到合适的catch块并把该异常对象交给catch块处理,那个过程被称为捕获(catch)异常;==如果Java运行环境找不到捕获异常的catch块,则运行时环境终止,Java程序也将退出。==当捕获异常以后,可以处理这个异常,使得程序继续执行,也可以退出程序。
在这里插入图片描述

异常类的继承体系

在这里插入图片描述

  • Java把所有非正常情况分为两种:异常(Exception)和错误(Error)。
    Error错误,一般是指虚拟机相关的问题,如系统奔溃、动态连接失败、虚拟机出现错误等。这种错误无法恢复或不可能捕获,将导致应用程序中断。通常,应用程序无法处理这些错误,因此,应用程序不应该使用catch块来捕获Error对象。在定义该方法时,也无须在其throws子句中声明该方法可能抛出Error及其任何子类。

  • Checked 异常和 Unckecked 异常
    将派生于Error或者RuntimeException的异常称为unchecked异常,所有其他异常称为 Checked 异常。

捕获异常时,一定要先捕获小异常,再捕获大异常。

package com.chao.chapterSixteen;

import java.util.Date;

public class TestNull {
    public static void main(String[] args) {
        Date d = null;
        try {
            System.out.println(d.after(new Date()));
        } catch (NullPointerException ne) {
            System.out.println("空指针异常"); //小异常
        } catch (Exception e) {
            System.out.println("未知异常"); //大异常
        }
    }
}


访问异常信息

如果程序需要再 catch 块中访问异常对象的相关信息,可以通过调用 catch 块后异常形参的方法来获得。
所有异常对象都包含如下几个常用方法:

  • getMessage():返回该异常的详细描述字符串
  • printStackTrace():将该异常的跟踪栈信息输出到标准输出
  • printStackTrace(PrintStream s):将该异常的跟踪栈信息输出到指定输出流
  • getStackTrack():返回该异常的跟踪栈信息
package com.chao.chapterSixteen;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class AccessExceptionMsg {
    public static void main(String[] args) {
        try {
            FileInputStream fis = new FileInputStream("a.txt");
        } catch (IOException ioe) {
            System.out.println(ioe.getMessage());
            ioe.printStackTrace();
        }
    }
}

上面程序调用了 Exception 对象的 getMessage 方法来得到异常对象的详细信息,也使用了 printStackTrace 来打印该异常的跟踪信息。

使用 finally 回收资源

有些时候,程序再 try 块中打开了一些物理资源(例如数据库的连接、网络的连接和磁盘文件等),这些物理资源都必须显示地回收。Java垃圾回收机制不会回收任何物理资源,垃圾回收机制只堆内存中对象所占用的内存。为了保证 try 块中打开的物理资源一定得到回收,异常处理机制提供了 finally 块。不管 try 块中的代码是否出现异常,也不管哪一个 catch 块被执行,finally 块总会被执行。


Checked 异常和 Runtime 异常体系

什么是 Checked 异常?
什么是 Runtime 异常?

  1. 对于Checked异常的处理方式有两种:
    • 当前方法明确知道如何处理该异常,程序应该使用 try … catch 块来捕获该异常
    • 当前方法不知道如何处理该异常,应该在定义方法的时声明抛出该异常

使用throws 声明抛出异常

  • 使用 throws 声明抛出异常的思路是:当前方法不知道应该如何处理这种类型的异常,该异常应该由上一级调用者处理。如果 main 方法也不知道如何处理这种类型的异常,也可以使用 throws 声明抛出异常,该异常将交给 JVM 处理。JVM 对异常的处理方法是:打印异常跟踪栈信息,并终止程序运行,这就是前面程序遇到异常自动结束的原因。
  • 如果某段代码中调用了一个带 throws 声明的方法,该方法声明抛出了 Checked 异常,这表明该方法希望它的调用者来处理该异常。这表明,这段代码要么放在 try 块中显式地捕获该异常;要么这段代码处在另一个带 throws 声明的抛出的方法中。
  • 使用 throws 声明抛出异常时有一个小限制:子类方法中声明抛出的异常类型应该是父类方法中声明抛出的异常类型的子类或相等
package com.chao.chapterSixteen;

import java.io.IOException;

public class OverrideThrows {
    public void test() throws IOException {

    }
}

class Sub extends OverrideThrows {
    //下面代码无法同构编译:子类方法声明抛出了比父类方法更大的异常
    public void test() throws Exception {

    }
}

使用 throw 抛出异常

当程序出现错误时,系统会自动抛出异常;除此之外 Java 也允许程序自行抛出异常,自行抛出异常使用 throw 语句完成。
这里的“异常”时一种很主观的做法,是否抛出异常,可能根据业务需求来决定。如果需要程序自行抛出异常,应该使用 throw 语句,throw 语句抛出的不是异常类,而是一个异常实例,而且每次只抛出一个异常实例。

自定义异常类

用户自定义异常类都应该继承 Exception 基类,如果希望自定义 Runtime 异常,则应该继承 RuntimeException 基类。定义异常时通常需要提供两种构造器:一个是无参数的构造器;另一个是带有一个字符串的构造器,这个字符串将作为该异常对象的详细说明。

package com.chao.chapterSixteen;

public class AuctionException extends Exception {
    public AuctionException() {}

    public AuctionException(String message) {
        super(message);
    }
}

catch 和 throw 同时使用

前面介绍异常的处理方法有两种:

  • 在出现异常的方法内捕获并处理异常,该方法的调用者将不能再次捕获该异常
  • 该方法签名中声明抛出该异常,将该异常完全交给方法调用者处理
    但是实际应用中有着更复杂的处理方式:当一个异常出现时,单靠某个方法无法完全处理该异常,必须由几个方法协作才能完全处理该异常。也就是说,异常出现的当前方法中,程序只对异常进行部分处理,还有些处理需要在该方法的调用者中才能完成,所以应当再次抛出异常,这样可以让方法的调用者也能捕获到异常。

猜你喜欢

转载自blog.csdn.net/qq_32682177/article/details/82990175