您的位置:首页 > 编程语言 > Java开发

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

2017-12-09 03:11 543 查看
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关键字后的括号里创建了两个资源——
reader
writer
。当程序运行离开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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息