The I / O stream using try with resource is automatically closed

Time:2021-9-14

The input / output stream of try with resource is automatically closed

Recently, during code review, the review tool prompted me to replace try catch finally, and according to the relevant requirements of the company, the level of the prompt is not low, so it will not pass if it is not changed.

Let’s look at the code first:


FileReader fr = null;  
BufferedReader br = null;
try {
    fr = new FileReader(fileName);
    br = new BufferedReader(fr);
    return br.readLine();
} catch (Exception e) {
    log.error("error:{}", e);
} finally {
  if (br != null) {
    try {
      br.close();
    } catch(IOException e){
      log.error("error:{}", e);
    }
  }
  if (fr != null ) {
    try {
      br.close();
    } catch(IOException e){
      log.error("error:{}", e);
    }
  }
}

The comment given by the audit tool is to replace with:


try (
    FileReader fr = new FileReader(fileName);
    BufferedReader br = new BufferedReader(fr)
  ) {
    return br.readLine();
}catch (Exception e) {
    log.error("error:{}", e);
}

Or:


try (
    BufferedReader br = new BufferedReader(new FileReader(fileName))
  ) { 
    // no need to name intermediate resources if you don't want to
    return br.readLine();
}
catch (Exception e) { 
    log.error("error:{}", e);
}

Comparing the codes, it is not difficult to find that there are differences in the closing of input and output streams. Don’t I / O streams have to be closed?

Take a look at the source code with this problem and find


public class FileInputStream extends InputStream{}
public abstract class InputStream implements Closeable {}
/**
 * A {@code Closeable} is a source or destination of data that can be closed.
 * The close method is invoked to release resources that the object is
 * holding (such as open files).
 *
 * @since 1.5
 */
public interface Closeable extends AutoCloseable {}
/**
 * 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.
 *
 * @apiNote
 * <p>It is possible, and in fact common, for a base class to
 * implement AutoCloseable even though not all of its subclasses or
 * instances will hold releasable resources.  For code that must operate
 * in complete generality, or when it is known that the {@code AutoCloseable}
 * instance requires resource release, it is recommended to use {@code
 * try}-with-resources constructions. However, when using facilities such as
 * {@link java.util.stream.Stream} that support both I/O-based and
 * non-I/O-based forms, {@code try}-with-resources blocks are in
 * general unnecessary when using non-I/O-based forms.
 *
 * @author Josh Bloch
 * @since 1.7
 */
public interface AutoCloseable {}

Autoclosable, as its name implies, automatically closes the flow. From the comments, we can find that the object that implements autoclosable and declares in try() will automatically call the close() method when the try with resource code block is executed.

be careful:

A try with resources statement can have catch and finally blocks like an ordinary try statement. In the try with resources statement, any catch or finally block is run after the declared resource is closed.

What to pay attention to when using try with resource

Try with resource is a syntax sugar introduced by JDK7, which can simplify the closing process of autoclosable resource class,

For example, JDK7 used the following code:


 File file = new File("d:/tmp/1.txt");
  FileInputStream fis = null;
  try {
   fis = new FileInputStream(file);
   xxxxx
            xxxxx
  } catch (IOException e) {
   e.printStackTrace();
  }finally{
   if(fis != null){
    try {
     fis.close();
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
  }

The above is a schematic code for reading the file content. In order to prevent resource leakage caused by exceptions in the try code block, the closing of resources is generally handled in the finally code block.

After JDK, the above code can be simplified to the following writing method:


  File file = new File("d:/tmp/1.txt");
  try(FileInputStream fis = new FileInputStream(file);) {
   fis.read();
  } catch (IOException e) {
   e.printStackTrace();
  }finally{
  }

It can be seen that it has been simplified a lot. It is called syntax sugar because the actual code is not like this after being compiled into a class file. The closing processing of resources will be automatically added during the compilation process.

The class file compiled by the above code is decompiled using javap and is as follows


File file = new File("d:/tmp/1.txt"); 
  try {
   Throwable var2 = null;
   Object var3 = null;
 
   try {
    FileInputStream fis = new FileInputStream(file);
                xxx
                xxxx
   } catch (Throwable var12) {
    if (var2 == null) {
     var2 = var12;
    } else if (var2 != var12) {
     var2.addSuppressed(var12);
    } 
    throw var2;
   }
  } catch (IOException var13) {
   var13.printStackTrace();
  }

Well, today’s theme, try with resource, has been introduced above, but there are still areas to pay attention to.

For example, the following code:

private static class MyResource implements AutoCloseable{ 
  private MyResource1 res;  
  public MyResource(MyResource1 res){
   this.res = res;
  }
  
  @Override
  public void close() throws Exception {
   System.out.println ("myresource automatically closes");
   Integer a = null;
   a.toString();
   this.res.close();
  }
 }
 
 private static class MyResource1 implements AutoCloseable{ 
  @Override
  public void close() throws Exception {
   System.out.println ("myresource1 automatically closes");
  }
 } 
 
 @Test
 public void test() throws Exception{
  try(
    MyResource r = new MyResource(new MyResource1())){
   Integer a = null ;
   a.toString();
  }
 }

After executing the above code, because there is an exception in the close method of myresource, the created myresource1 will not be closed, resulting in resource leakage. In order to avoid this problem, we need to create the object implementing the autoclosable interface separately.

As shown below:


  try(
    MyResource1 res= new MyResource1();
    MyResource r = new MyResource(res)){
   Integer a = null ;
   a.toString();
  }

The above is my personal experience. I hope I can give you a reference, and I hope you can support developpaer.