Senin, 01 Desember 2014

Right way to Close InputStream and OutputStream in Java



For some unknown reasons many Java programmers are not very comfortable with IO package. I don't know why, but I have found them much more comfortable with java.lang and java.util than java.io. One possible reason of this could be that, writing IO code require a bit of C++ like programming, which involves doing clean-up, releasing resources once done etc. Since Java made coding a lot easier by taking care of memory management, unknowingly it also introduced bad practice of not releasing resource after use e.g. database connections, socket connection, files, directory, printers, scanners or any other scarce resource. The laziness of just doing work and forget everything is very easy, because of this many Java programmer never bother about doing clean-up. This habit is most visible in programmers who have never done system programming using C or C++. Since IO requires you to deal with streams, channels, and file descriptors, which need to be closed properly, Java developer find it uneasy to deal with. On other day, I asked one candidate to write code for copying content of one file to another without using copy() method or a third-party library. Though he managed to write the code, he made a common mistake, he was not closing streams properly. It's important to close streams, to release file descriptor held by this class, as its limited resource and used in both socket connection and file handling. A serious resource leak may result in file descriptor exception as well. Before moving ahead, let's see the part of  the code candidate wrote for copying file from one directory to another directory in Java without using any third-party library.



FileInputStream fis = null;
FileOutputStream fos = null;

try {

fis = new FileInputStream("../input/fxrates.txt");
fos = new FileOutputStream("../output/fxrates.txt");

// code for reading from input stream and writing to output stream

} finally {

try {
// He was careful to close streams in finally block, but it’s not complete
// Can you spot error?

if(fis != null) fis.close();
if(fos != null) fos.close();

} catch(IOException e) { System.out.println("Failed to close streams"); }

}

Most of his code is al-right and even better than many Java programmers. He was even careful to close streams in finally block, but he still made an error, which could cause resource leak in his Java program. Can you spot the error? Yes, output stream will not be closed if close() method of input stream will throw an Exception i.e. fos.close() will not even execute if fis.close() throws exception. This means file descriptor held by OutputStream will never release causing a resource leak in Java program. It's not uncommon, I have seen many such code, where developers has right intention to release resources by closing streams but fail to realize something equally important. Right way of closing stream is by closing them in their own try catch block, so that failure of closing one stream should not prevent calling close() on other stream. Here is the right way of closing InputStream and OutputStream in Java :

InputStream is = null;
OutputStream os = null;

try {

is = new FileInputStream("../input/fxrates.txt");
os = new FileOutputStream("../output/fxrates.txt");

......

} finally {

try { if (is != null) is.close(); } catch(IOException e) {//closing quietly}
try { if (os != null) os.close(); } catch(IOException e) {//closing quietly}

}


How to Close InputStream and OutputStream in Java properly

This code will not forget to call os.close() even if is.close() will throw IOException, which ensures that file descriptor held by OutputStream will be released. If you don't like so many try-catch and try-finally block or fed-up with verbosity of this program then you can also try Apache commons IO package. It provides a closeQuitetly() method to close streams quietly i.e. above finally block can be re-written by using IOUtils.closeQuietly() as following.

try{
.......
........
} finally {
IOUtils.closeQuietly(in);
IOUtils.closeQuietly(os);
}

closeQuitely() is an overloaded method for closing URLConnection, Closable, Socket, ServerSocket, Selector, InputStream, OutputStream, Reader and Writer classes. It is also null-safe, so don't check if Stream is null before calling this method. Here is source code of closeQuitely() method for closing InputStream :



 public static void closeQuietly(InputStream input) {

        try {
if (input != null) {
input.close();
}
} catch (IOException ioe) {
// ignore
}
}

By the way, you have a much better alternative if you are using Java 7. It has provided try-with-resource statements for automatic resource management in Java. All resources opened in try block will automatically closed by Java, provided they implements Closable and AutoClosable. Since all InputStream and OutputStream are eligible to be used inside try-with-resource statements, you should take advantage of that. This is really great for Java programmer, as they are not as careful as their C++ counterparts, especially while releasing resource. Here is how does above code look like with try-with-resource statement.



try (FileInputStream fis = new FileInputStream("../input/fxrates.txt");
FileOutputStream fos = new FileOutputStream("../output/fxrates.tx")) {

// code for reading contents

.....

} catch (IOException ioex) {
System.out.println("Failed to copy files : " + ioex.getMessage());
ioex.printStackTrace();
}

As you can see, we have got rid of lot of boiler plate try-finally code. Since you can declare more than one resource inside try-with-resource block, allocate your streams, channels, and readers there.



That's all on this post about right way of closing InputStream and OutputStream in Java. We have seen three examples of closing streams in Java and how combining close() call of two stream can cause resource leak in Java. Take away is always close streams in their own try-catch block. If you are using Apache commons IO in your project then take advantage of IOUtils.closeQuietly() method to reduce boiler-plate code. Prefer try-with-resource over manual handling of resource in Java 7. Bottom line is all opened streams must be closed once you are through with them. This rule applies to all resources e.g. database connections, network connections, files, printers and any other shared resource. You must release once you are done.



If you like this article and love to read more about InputStream, Files and OutputStream in Java, see these amazing articles :

Difference between FileInputStream and FileReader in Java

How to read a file line-by-line in Java

5 examples to convert InputStream to String in Java

2 ways to open ZIP files in Java with example

How to convert InputStream to Byte Array in Java

























Source:http://javarevisited.blogspot.com/2014/10/right-way-to-close-inputstream-file-resource-in-java.html

Tidak ada komentar:

Posting Komentar