Java7 忽略异常

无论何时使用输入或者输出,在异常产生后如何关闭资源都是一个麻烦的问题。假设产生了一个 IOException,接下来在关闭资源时,close 方法又抛出了另一个异常。

那么实际上会捕获哪个异常呢?在 Java 中,finally 分支中抛出的异常会丢弃掉之前的异常。这不仅听上去不合理,实际上也确实不太合理。毕竟,用户对原始的异常会更感兴趣。

我们通过下面示例来验证:

package com.hxstrive.jdk7.exception;

import java.io.*;

/**
 * 异常处理
 * @author hxstrive.com
 */
public class ExceptionDemo1 {

    public static void main(String[] args) throws Exception {
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            // input.txt 是一个不存在的文件
            inputStream = new FileInputStream(new File("D:\\input.txt")); // 这里会抛出 FileNotFoundException
            int read = inputStream.read();
            System.out.println("read:" + read);

            outputStream = new FileOutputStream(new File("D:\\output.txt")); // 这里不会执行
        } finally {
//            outputStream.close();
//            inputStream.close();
        }
    }

}

执行上面代码,将会抛出 “FileNotFoundException”,如下:

Exception in thread "main" java.io.FileNotFoundException: D:\input.txt (系统找不到指定的文件。)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(FileInputStream.java:146)
    at com.hxstrive.jdk7.exception.ExceptionDemo1.main(ExceptionDemo1.java:16)

如果我们将 finally 子语句中的注释去掉,尝试关闭 outputStream 资源,此时会出现空指针异常,如下:

Exception in thread "main" java.lang.NullPointerException
    at com.hxstrive.jdk7.exception.ExceptionDemo1.main(ExceptionDemo1.java:22)

通过异常信息,你会发现 FileNotFoundException 异常不存在了。

在 Java7 中,try-with-resources 语句修正了这个行为。当 Autocloseable 对象的 close 方法抛出异常时,原来的异常会被重新抛出,而调用 close() 方法产生的异常会被捕获,并被标注为 “被忽略” 的异常。

当捕获到首次异常时,你可以通过调用 getSuppressed() 方法来获取那些二次异常:

package com.hxstrive.jdk7.exception;

import java.io.*;
import java.util.Arrays;

/**
 * 异常处理
 * @author hxstrive.com
 */
public class ExceptionDemo2 {

    public static void main(String[] args) throws Exception {
        OutputStream outputStream = null;
        try (
                InputStream inputStream = new FileInputStream(new File("D:\\input.txt")) // 抛出文件不存在异常
        ) {
            int read = inputStream.read();
            System.out.println("read:" + read);
            outputStream = new FileOutputStream(new File("D:\\output.txt")); // 不会执行
        } catch (Exception e) {
            Throwable[] suppressed = e.getSuppressed();
            System.out.println(Arrays.toString(suppressed)); // 打印异常链
            e.printStackTrace();
        } finally {
            outputStream.close(); // 抛出空指针异常
        }
    }

}

执行上面代码,抛出异常如下:

[]
java.io.FileNotFoundException: D:\input.txt (系统找不到指定的文件。)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(FileInputStream.java:146)
    at com.hxstrive.jdk7.exception.ExceptionDemo2.main(ExceptionDemo2.java:16)
Exception in thread "main" java.lang.NullPointerException
    at com.hxstrive.jdk7.exception.ExceptionDemo2.main(ExceptionDemo2.java:26)

如果不能使用 try-with-resources 语句,又希望自己实现这样的机制,你可以调用:

ex.addSuppressed(secondaryException);

注意:Throwable、Exception、RuntimeException 和 Error 类的构造参数都可以接受两个参数,分别用来禁用忽略异常和禁用堆栈跟踪。Throwable 构造方法签名:

protected Throwable(String message, Throwable cause,
                        boolean enableSuppression, // 禁用忽略异常
                        boolean writableStackTrace) {
    //...
}

⚠️注意:

(1)当忽略异常被禁用时,调用 addSuppressed() 就不会有效果,并且 getSuppressed() 方法会返回一个长度为 0 的数组。当禁用堆栈跟踪时,调用 fillInStackTrace() 不会有效果,并且getStackTrace() 方法会返回一个长度为 0 的数组。这可以用于因内存不够而产生的 VM 错误,或者 VM 上使用异常来中断嵌套方法调用的编程语言。

(2)只有当二次异常没有被主动破坏时,你才能检测它们。尤其是,如果你使用了一个 Scanner 并且当输入失败且随后的关闭也失败时,Scanner 类会捕获输入异常,关闭资源并捕获关闭异常,然后抛出一个与之前所忽略异常无关且完全不同的异常。

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号