zoukankan      html  css  js  c++  java
  • 如何优雅地关闭资源

    很多时候我们都会用到io资源,比如文件、网络、各种连接等。比如有时候我们需要从一个文本文件中读取数据,一般的步骤是:

    1. 用FileReader打开文件
    2. 包装成BufferReader
    3. 循环地从BufferReader中读取内容,直接读出来的内容为空
    4. 关闭BufferReader和FileReader

    用代码实现如下:

    FileReader fileReader = null;
    BufferedReader bufferedReader = null;
    try {
        fileReader = new FileReader("test.txt");
        bufferedReader = new BufferedReader(fileReader);
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            System.out.println(line);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (bufferedReader != null) {
            try {
                bufferedReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        if (fileReader != null) {
            try {
                fileReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    当然做法不止这一种,也可以通过字节流的方式读入,再包装成可以直接读取的方式。但是无论怎么样,我们都需要在finally块中一个个地把各种资源关闭,因为变量作用域的原因,在关闭之前还得判空,于是我们的代码就变成了上面的那样。

    太繁琐太不优雅了,你可能会想到,直接把异常一股脑地抛出去不就行了吗?不行,因为调用方只是想读个文件而已,你却要它来处理一大堆异常?而且,就算调用方来处理异常,那么调用方的代码会变得更不优雅,因为还要针对各种不同类型的异常来处理。

    下面介绍一种非常优雅的做法,直接上代码:

    try (
            FileReader fileReader = new FileReader("test.txt");
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            ) {
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            System.out.println(line);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    

    结构就是

    try(){...}catch(...) {...}
    

    和第一种写法最大的区别就是try关键字后多了一对括号。具体用法是,在try后的括号内声明并且定义各种资源,可以是一个也可以是多个,然后别的东西不用变,资源也不用你来关闭了。

    可能你会有疑问,fileReader和bufferedReader是真的关闭了吗?首先告诉你,是的。但是怎么验证呢?

    我们先关注另一个问题,try后面的括号内是用来定义各种资源的,也就是初始化,是不是所有的东西都可以在里面初始化?比如下面的String line可不可以?

    代码编译出错了:需要AutoCloseable类型但是提供了String类型,AutoCloseable是一个接口,最早出现在jdk1.7中。直接贴一段该接口的说明

    An object that may hold resources (such as file or socket handles)
    until it is closed. The {@link #close()} method of an {@code AutoCloseable}
    object is called automatically when exiting a {@code
    try}-with-resources block for which the object has been declared in
    the resource specification header. This construction ensures prompt
    release, avoiding resource exhaustion exceptions and errors that
    may otherwise occur.
    

    最核心的一点就是当退出try块,这些资源会自动调用close方法。这样就达到了自动关闭资源的目的,如果去看FileReader和BufferedReader的源码,它们都间接实现了AutoCloseable接口。

    为了验证这一点,我们自己写一个实现AutoCloseable的类:

    public class Auto implements AutoCloseable {
    
        public void run() {
            System.out.println("run");
        }
    
        @Override
        public void close() throws Exception {
            System.out.println("close");
        }
    }
    

    然后在测试类中生成并且调用run方法

    public class Main {
    
        public static void main(String[] args) {
            try (Auto auto = new Auto()){
                auto.run();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    }
    

    运行后的结果如下:

    run
    close
    

    上面的测试类并没有显式调用close方法,但是最后这个方法被调用了。到这里我们可以猜测是在编译的时候,javac命令已经帮我们把调用close方法的指令自动生成了,我们用javap命令反编译来看一下:

    C:>javap -c Main.class
    Compiled from "Main.java"
    public class source.Main {
      public source.Main();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return
    
      public static void main(java.lang.String[]);
        Code:
           0: new           #2                  // class source/Auto
           3: dup
           4: invokespecial #3                  // Method source/Auto."<init>":()V
           7: astore_1
           8: aconst_null
           9: astore_2
          10: aload_1
          11: invokevirtual #4                  // Method source/Auto.run:()V
          14: aload_1
          15: ifnull        85
          18: aload_2
          19: ifnull        38
          22: aload_1
          23: invokevirtual #5                  // Method source/Auto.close:()V
          26: goto          85
          29: astore_3
          30: aload_2
          31: aload_3
          32: invokevirtual #7                  // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
          35: goto          85
          38: aload_1
          39: invokevirtual #5                  // Method source/Auto.close:()V
          42: goto          85
          45: astore_3
          46: aload_3
          47: astore_2
          48: aload_3
          49: athrow
          50: astore        4
          52: aload_1
          53: ifnull        82
          56: aload_2
          57: ifnull        78
          60: aload_1
          61: invokevirtual #5                  // Method source/Auto.close:()V
          64: goto          82
          67: astore        5
          69: aload_2
          70: aload         5
          72: invokevirtual #7                  // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
          75: goto          82
          78: aload_1
          79: invokevirtual #5                  // Method source/Auto.close:()V
          82: aload         4
          84: athrow
          85: goto          93
          88: astore_1
          89: aload_1
          90: invokevirtual #9                  // Method java/lang/Exception.printStackTrace:()V
          93: return
        Exception table:
           from    to  target type
              22    26    29   Class java/lang/Throwable
              10    14    45   Class java/lang/Throwable
              10    14    50   any
              60    64    67   Class java/lang/Throwable
              45    52    50   any
               0    85    88   Class java/lang/Exception
    }
    

    果不其然,在run方法后面调用了Auto.close方法。这里值得说的是出现了多个close方法的调用,原因就是,这里的处理其实相当于在一个finally块中调用close方法,为了保证finally中的语句会执行,会在抛出异常的后面再执行finally中的语句,也就是close方法,最后才return。

  • 相关阅读:
    SGU 499 Greatest Greatest Common Divisor
    pku 3468 A Simple Problem with Integers
    pku2226 Muddy Fields
    pku3041 Asteroids
    java基础string操作
    PowerDesigner(7)转载
    java基础2
    PowerDesigner(6)转载
    java基础3
    java基础(1)
  • 原文地址:https://www.cnblogs.com/spareyaya/p/13129710.html
Copyright © 2011-2022 走看看