使用try-with-resources替代try finally释放资源

一、旧社会

Java里,对于文件操做IO流、数据库链接等开销很是昂贵的资源,用完以后必须及时经过close方法将其关闭,不然资源会一直处于打开状态,直至程序中止,增长系统负担。java

关闭资源的经常使用方式就是在finally块里是释放,即调用close方法。好比,咱们常常会写这样的代码:数据库

public static void main(String[] args) {
    BufferedReader br = null;
    try {
        String line;
        br = new BufferedReader(new FileReader("d:\\testing.txt"));
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
    } catch (IOException e) {
        // handle exception
    } finally {
        try {
            if (br != null) {
                br.close();
            }
        } catch (IOException ex) {
            // handle exception
        }
    }
}

能够看出,为了关闭资源以及处理关闭资源时可能出现的异常,不得不写一大推代码。网络

二、新时代

2.1 使用新写法

从Java 7开始,jdk提供了一种更好的方式关闭资源,使用try-with-resources语句,改写一下上面的代码,效果以下:ide

public static void main(String[] args) {
    try(BufferedReader br = new BufferedReader(new FileReader("d:\\testing.txt"))) {
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
    } catch (IOException e) {
        // handle exception
    }
}

清爽了不少是否是? 可是,有没有一点不安呢?测试

2.2 新问题

原来释放资源的时候若是发生异常,咱们能够在finally块中catch新异常,而后继续处理。可是新方式没有了finally块,异常是如何抛出的?若是关闭资源时发生异常怎么办?咱们怎么处理?code

从文档上能够找到这样一段描述:对象

If an exception is thrown from the try block and one or more exceptions are thrown from the try-with-resources statement, then those exceptions thrown from the try-with-resources statement are suppressed. You can retrieve these suppressed exceptions by calling the Throwable.getSuppressed method from the exception thrown by the try block.接口

意思是:若是 try 块抛出异常而且 try-with-resources 语句抛出一个或多个异常,那么从 try-with-resources 语句中抛出的异常将会被忽略。你能够经过调用由 try块抛出的异常的Throwable.getSuppressed 方法检索这些被忽略的异常信息。资源

在明确一点说就是:文档

  • 若是try块异常,catch到的是try块抛出的异常;
  • 若是try块正常,close异常,catch到的是close抛出的异常;
  • 若是try块异常,close也异常,catch到的是try块抛出的异常,close异常被忽略。

基于这几种状况,咱们作几个测试分别验证一下:

2.2.1 try块异常

你们都懂,略。

2.2.2 try块正常,close异常

public class TestTryWithResources {

    public static void main(String[] args) {
        try (MyResource resource = new MyResource()) {
            
        } catch (Exception e) {
            System.out.println("捕获异常: " + e.getMessage());
        }
    }
}

/**
 * 自定义一个资源类,close时抛出异常
 *
 * ps:只有实现AutoCloseable或Closeable接口的对象才能用try-with-resources
 */
class MyResource implements AutoCloseable {
    @Override
    public void close() throws Exception {
        System.out.println("执行close方法,释放资源");
        throw new Exception("释放资源异常");
    }
}

执行结果为: 执行close方法,释放资源 捕获异常: 释放资源异常

即,catch到的是close方法抛出的异常

2.2.3 try块异常,close也异常

public class TestTryWithResources {

    public static void main(String[] args) {
        try (MyResource resource = new MyResource()) {
            throw new Exception("try块异常");
        } catch (Exception e) {
            System.out.println("捕获异常: " + e.getMessage());

            // 找到被忽略的异常
            Throwable[] ts = e.getSuppressed();
            for(Throwable t : ts) {
                System.out.println("被忽略的异常"+ t.getMessage());
            }
        }
    }
}

/**
 * 自定义一个资源类,close时抛出异常
 */
class MyResource implements AutoCloseable {
    @Override
    public void close() throws Exception {
        System.out.println("执行close方法,释放资源");
        throw new Exception("释放资源异常");
    }
}

执行结果: 执行close方法,释放资源 捕获异常: try块异常 被忽略的异常: 释放资源异常

即,catch到的是try块中的异常,释放资源时产生的异常被忽略了。

2.3 实践中的问题

实际上,不少时候try块中的异常和close方法抛出的异常是同一类型的。好比流、网络等不管是try块仍是close方法都是抛出IOException,咱们该怎么办? 最佳实践:按异常栈最底层的方法名判断。若是是close方法抛出的异常,就是关闭资源时产生的。 注:咱们的方法名不能叫close

public class TestTryWithResources {

    public static void main(String[] args) {
        try (MyResource resource = new MyResource()) {
            throw new Exception("try块异常");
        } catch (Exception e) {
            if(!"close".equals(e.getStackTrace()[0].getMethodName())){
                System.out.println("处理业务异常");
            }
        }
    }
}

/**
 * 自定义一个资源类,close时抛出异常
 */
class MyResource implements AutoCloseable {
    @Override
    public void close() throws Exception {
        System.out.println("执行close方法,释放资源");
        throw new Exception("释放资源异常");
    }
}
相关文章
相关标签/搜索