Vineet Manohar is CTO / Co-Founder of online Car Search Engine, iSeeCars.com. Vineet is a DZone MVB and is not an employee of DZone and has posted 20 posts at DZone. You can read more from them at their website. View Full User Profile

Java 7: New Feature – Automatically Close Files and Resources in try-catch-finally

03.24.2011
| 21713 views |
  • submit to reddit

Try with resources is a new feature in Java 7 which lets us write more elegant code by automatically closing resources like FileInputStream at the end of the try-block.

Old Try Catch Finally

Dealing with resources like InputStreams is painful when it comes to the try-catch-finally blocks. You need to declare the resources outside the try so that they are is accessible from finally, then you must initialize the variable to null and check for non-null when closing the resource in finally.

  File file = new File("input.txt");

InputStream is = null;

try {
is = new FileInputStream(file);

// do something with this input stream
// ...

}
catch (FileNotFoundException ex) {
System.err.println("Missing file " + file.getAbsolutePath());
}
finally {
if (is != null) {
is.close();
}
}

Java 7: Try with resources

With Java 7, you can create one or more “resources” in the try statement. A “resources” is something that implements the java.lang.AutoCloseable interface. This resource would be automatically closed and the end of the try block.

  File file = new File("input.txt");  

try (InputStream is = new FileInputStream(file)) {
// do something with this input stream
// ...
}
catch (FileNotFoundException ex) {
System.err.println("Missing file " + file.getAbsolutePath());
}

Exception handling

If both the (explicit) try block and the (implicit) resource handling code throw an exception, then the try block exception is the one which will be thrown. The resource handling exception will be made available via the Throwable.getSupressed() method of the thrown exception. Throwable.getSupressed() is a new method added to the Throwable class since 1.7 specifically for this purpose. If there were no suppressed exceptions then this will return an empty array.

Reference

http://download.java.net/jdk7/docs/technotes/guides/language/try-with-resources.html

 

From http://www.vineetmanohar.com/2011/03/java-7-try-with-auto-closable-resources/

Published at DZone with permission of Vineet Manohar, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Mileta Cekovic replied on Thu, 2011/03/24 - 4:10am

Pre-Java7 example is far from optimal, so it gives false picture how big improvement this is. It is big, but not as big: it does save 4 lines of code, but not 7 as presented in the article!
More concize Pre-Java 7 example:


File file = new File("input.txt");
InputStream is = new FileInputStream(file);
try {
  // do something with this input stream
  // ...
}
catch (FileNotFoundException ex) {
  System.err.println("Missing file " + file.getAbsolutePath());
}
finally {
  is.close();
}

shainnif ismail replied on Thu, 2011/03/24 - 4:29am

I think that FileInputStream throws a FileNotFoundException so it actually needs to be inside the try catch block. http://download.oracle.com/javase/6/docs/api/java/io/FileInputStream.html

Blanc Frederic replied on Thu, 2011/03/24 - 5:05am

In pre Java 7, if you don't care to lost some closing errors, you can use the following class:

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class WithClosable {

	private List<Object> objectsToClose = new ArrayList<Object>();
	
	public <T> T add(T objectToClose){
		this.objectsToClose.add(objectToClose);
		return objectToClose;
	}
	public void closeQuiet(){
		for(Object o : objectsToClose){
			if(o!=null){
				try{
					Method m = o.getClass().getMethod("close");
					m.invoke(o);
				}catch(Exception e){}
		}	
	}
	
}

 then you can have a very simpler code:

WithClosable wc = new WithClosable();
try {
    InputStream is = wc.add(new FileInputStream(file));
    // do something with this input stream
    // ...
    OutputStream os = wc.add(new FileOutputStream(.....
}catch (FileNotFoundException ex) {
   System.err.println("Missing file " + file.getAbsolutePath());
}finally {
   wc.closeQuiet();    
}

If you really need to catch closing exception, the WithClosable class is quite easy to modify.

 

Mileta Cekovic replied on Thu, 2011/03/24 - 8:41am in response to: shainnif ismail

I do not see how FileInputStream throwing FileNotFoundException implies that it must be in try(-catch)-finally block that is primarely used for resource cleansing.
You can either declare the enclosing method throws FileNotFoundExceptionor or add another try-catch block with exception chaining.
Initializing resources inside try block and checking null equality inside finally block is a bad practice IMHO that only makes code less readable and even less understandable.

But thanks for spotting this. So the correct example in pre-Java 7 would be:

File file = new File("input.txt");
try {
  InputStream is = new FileInputStream(file);
  try {
    // do something with this input stream
    // ...
  finally {
    is.close();
  }
}
catch (FileNotFoundException ex) {
  System.err.println("Missing file " + file.getAbsolutePath());
}

 

matt inger replied on Thu, 2011/03/24 - 9:46am in response to: Mileta Cekovic

Except that your code is:

  1. probably not compilable, as no FileNotFoundException is thrown inside of the try block, as that exception is in the signature of the FileInputStream constructor.
  2. can lose an exception very easily if the close() method fails for some reason
The original post is very close to the boilerplate code for file resource handling, except that the finally block should include a try-catch in it. Otherwise, and thrown IO exception could potentially be lost if the close() method throws an exception (hence the need for the "suppressed" exception which was added to Java 7.
...
finally {
  try {
    if (is != null) {
      is.close();
    }
  }
  catch (IOException e) {
    ; //swallow the exception, which is why the suppressed exception was added
  }
}

matt inger replied on Thu, 2011/03/24 - 9:50am in response to: Mileta Cekovic

as opposed to 3 nested try-catch blocks? (you need a try-catch around the .close() or you could lose the original exception which was thrown. As a general rule, a finally block should NEVER generate an exception.)

Christian Schli... replied on Thu, 2011/03/24 - 10:32am

Mileta's second post is right except that it needs to catch an IOException.

try {
  FileInputStream in = new FileInputStream("file.txt"); // may throw FileNotFoundException
  try {
    // Do I/O here - may throw IOException.
  } finally {
    in.close(); // ALWAYS close the stream - may throw another IOException
  }
} catch (IOException ex) { // catch any IOException regardless of its origin
  ex.printStacktrace(); // print stacktrace or log exception and/or finally rethrow - whatever is appropriate
}

Zoldano Martin replied on Thu, 2011/03/24 - 11:04am

Scala gives developers more power and elegance http://bit.ly/hUOIJO

Jacek Furmankiewicz replied on Thu, 2011/03/24 - 4:20pm in response to: Zoldano Martin

Sure...but it can't give them a half-decent IDE for the life of it. Emacs may be great for Martin Odersky, not so for anyone used to Eclipse.

Rickard Oberg replied on Fri, 2011/03/25 - 12:46am

I had so much problems with this that I created a simple I/O API that deals with properly opening/reading/writing/closing files/resources, both on the read and write side. In effect you get a sort of piping API where the error handling can be written once, and then you just put things together in application code. Copying a file becomes:

Inputs.byteBuffer(inFile, BUFSIZE).transferTo(Outputs.byteBuffer(outFile));

You can put a try/catch around it to log or deal with the exceptions that can be thrown, but at least the resource handling is done already. If either side throws an exception the other gets to know about it, and so can do proper cleanup.

Because of the piping you can insert transfer logging easily:

Inputs.text(textFile).transferTo(Transforms.map(new ProgressLog(logger, "Copied {0} lines", 100), Outputs.text(outFile));

 which will copy a textfile and output "Copied nnn lines" every  100 lines. More details here:

http://www.jroller.com/rickard/entry/a_generic_input_output_api

We have started using this extensively in the Qi4j project, and it simplifies things quite a lot. For example, performing a backup from an EntityStore is now:

entityStore.backup().transferTo(Outputs.text(backupFile));

And restore becomes:

Inputs.text(backupFile).transferTo(entityStore.restore());

Nifty. Problemo solved.

Carlos Hoces replied on Sat, 2011/03/26 - 2:18am

Maybe this code snippet could better ilustrate the new capabilities found in JDK 7.0

  @Override
    public AudioInputStream getAudioInputStream(File file) throws UnsupportedAudioFileException, IOException {
        AudioInputStream audioInputStream = null;
        final Path path = Paths.get(file.toURI());
        try (final InputStream inputStream = Files.newInputStream(path)) {
            audioInputStream = getAudioInputStream(inputStream, Files.size(path));
        } catch (UnsupportedAudioFileException | IOException ex) {
            throw ex;
        }
        return audioInputStream;
    }

This method overrides an AudioFileReader one, so parameter must be of File type.

Carlos Hoces replied on Sat, 2011/03/26 - 2:34am

Just to be precise, the catch block should be something like this:

   } catch (UnsupportedAudioFileException | IOException ex) {

         // do something here before rethrowing the exception

          throw ex;
    }

Otherwise, there will be no reason to catch for just rethrowing the exception back to the caller.

Anyway, the reason for presenting that catch block is to show the multicatch capabilities found in JDK 7.0

Slim Ouertani replied on Sat, 2011/03/26 - 9:16am

I posted about it but using Scala try-with-resources  

so no need to wait java 7 :)

Carlos Hoces replied on Sat, 2011/03/26 - 1:50pm

The code snippet I've posted above already runs under JRE 7.0 so you're right: there's no need to wait :)

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.