JDK7新特性 try-with-resources 详细分析

Try-with-resources是JDK1.7中一个新的异常处理机制,它能够很容易地关闭在try-catch语句块中使用的资源。

利用Try-Catch-Finally管理资源的释放

在java7以前,程序中使用的资源需要被明确地关闭,我们需要在finally块中编写一大堆繁琐的代码,比如下面这种:

        BufferedReader reader = null;
        PrintWriter writer = null;
        try {
            reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            writer = new PrintWriter(socket.getOutputStream());
            //TODO
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (reader != null){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (writer != null){
                writer.close();
            }
        }

try语句块中有可能抛出异常,finally语句块中有一个地方会能出异常。

不论try语句块中是否有异常抛出,finally语句块始终会被执行。这意味着,不论try语句块中发生什么,reader都会被关闭,或者说都会试图被关闭。如果关闭失败,reader.close()方法也可能会抛出异常。

假设try语句块抛出一个异常,然后finally语句块被执行。同样假设finally语句块也抛出了一个异常。那么哪个异常会根据调用栈往外传播?

即使try语句块中抛出的异常与异常传播更相关,最终还是finally语句块中抛出的异常会根据调用栈向外传播。

在JDK1.7中,对于上面的例子可以用try-with-resource 结构这样写:

try(BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter writer = new PrintWriter(socket.getOutputStream(), true)
            ){
            String body;
            while ((body = reader.readLine()) != null){
                String res = body.equalsIgnoreCase("query time") ? new Date().toString() : "Bad order!";
                writer.println(res);
            }
        }catch (IOException e){
            e.printStackTrace();
        }

上面的例子在try关键字后的括号里创建了两个资源——readerwriter。当程序运行离开try语句块时,这两个资源都会被自动关闭。

这些资源将按照他们被创建顺序的逆序来关闭。首先writer会被关闭,然后reader会被关闭。

资源被自动释放的关键:实现Closeable接口

这个try-with-resources结构里不仅能够操作java内置的类。你也可以在自己的类中实现java.lang.AutoCloseable接口,然后在try-with-resources结构里使用这个类。

AutoClosable 接口仅仅有一个方法,接口定义如下:

public interface AutoClosable {
    public void close() throws Exception;
}

任何实现了这个接口的方法都可以在try-with-resources结构中使用。
其实try-with-resources就是一个语法糖,编译器自动为我们做了一下工作:

  • 当try语句块在执行中遇到异常时,catch语句会捕获到抛出的异常,但并不会马上处理这些异常,编译器会调用实现了AutoCloseable接口的待释放对象的close方法,执行资源的释放工作

  • 当对象的close方法执行时遇到到异常,此时编译器不会直接抛出,而是把产生的异常都记录下来。这么做的好处是不会丢失任何异常。在java7之前,这种做法需要实现自己的异常类,而在java7中,已经对Throwable类进行了修改以支持这种情况。在java7中为Throwable类增加addSuppressed方法。当一个异常被抛出的时候,可能有其他异常因为该异常而被抑制住,从而无法正常抛出。这时可以通过addSuppressed方法把这些被抑制的方法记录下来。被抑制的异常会出现在抛出的异常的堆栈信息中,也可以通过getSuppressed方法来获取这些异常。这样做的好处是不会丢失任何异常,方便开发人员进行调试。

编译器为我们做的工作大体如下所示

 BufferedReader reader = null;
        PrintWriter writer = null;
        try {
            reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            writer = new PrintWriter(socket.getOutputStream());
            //TODO

        }catch (Exception e){
            if (reader != null){
                try {
                    reader.close();
                } catch (IOException e1) {
                    e.addSuppressed(e1);
                }
            }

            if (writer != null){
                writer.close();
            }
            e.printStackTrace();
        }
    }

我编写了一个自定义的对象来模拟了一下资源关闭的流程,简单明了

package chp2.bio;

import java.io.Closeable;
import java.io.IOException;

/**
 * Created by ThinkPad on 2017/12/9.
 */
public class CloseDemo implements AutoCloseable {

    public static void main(String[] args) {
        try(CloseDemo demo = new CloseDemo()) {
            System.out.println("代码执行出现ClassNotFound异常");
            throw new ClassNotFoundException("ClassDemo NotFound exception");

        }catch (Exception e){
            System.out.println("捕获到异常:" + e.getLocalizedMessage());
            System.out.println("异常类型:" + e.getClass().getName());

            Throwable[] throwables = e.getSuppressed();
            for (Throwable throwable : throwables) {
                System.out.println("被抑制的异常:" + throwable.getMessage());
                System.out.println("被抑制的异常名称:" + throwable.getClass().getName());
            }
        }
    }

    @Override
    public void close()  {
        System.out.println("CloseDemo 开始关闭");
        throw new ArrayIndexOutOfBoundsException("关闭demo出现异常");
    }
}

输出结果:

代码执行出现ClassNotFound异常
CloseDemo 开始关闭
捕获到异常:ClassDemo NotFound exception
异常类型:java.lang.ClassNotFoundException
被抑制的异常:关闭demo出现异常
被抑制的异常名称:java.lang.ArrayIndexOutOfBoundsException
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值