Remember, calling System.gc() does not actually run the garbage collector it is just a suggestion to the JVM that it may be appropriate to perform garbage collector. The JVM may or may not actually run the garbage collector.
So, in short, you should consider it your responsibility to close any IO Streams you open.
You could write the following code and you may even see code like this in examples:
public String readFile(File file) throws FileNotFoundException, IOException { BufferedReader in = new BufferedReader(new FileReader(file)); StringBuilder text = new StringBuilder(); // read the first line String line = in.readLine(); // continue to read lines whilst they are available while ( line != null ) { text.append(line); line = in.readLine(); } // close the stream in.close(); return text.toString(); }
The problem here is that if an exception is thrown during the read loop the method will exit without closing the stream and hence without releasing the file system resources.
If we wrap the code in a try-catch-finally statement and place the call to the close method in the finally clause we can ensure it always runs regardless of how the method exits. For example:
public String readFile(File file) throws FileNotFoundException, IOException { BufferedInputStream in = null; try { StringBuilder text = new StringBuilder(); in = new BufferedReader(new FileReader(file)); // read the first line String line = in.readLine(); // continue to read lines whilst they are available while ( line != null ) { text.append(line); line = in.readLine(); } return text.toString(); } finally { // close the stream in.close(); } }
Now whilst this approach appears to solve the problem there is a flaw. If the creation of the input stream fails for example by throwing a FileNotFoundException then when the finally clause executes it will call the close method on 'in'. But the variable 'in' will be null and so a NullPointerException will be thrown masking the original problem and almost certainly evading any FileNotFoundExcpetion handling in the calling method.
Fortunately the answer is fairly easy, check to see if 'in' is null before trying to close it.
public String readFile(File file) throws FileNotFoundException, IOException { BufferedInputStream in = null; try { StringBuilder text = new StringBuilder(); in = new BufferedReader(new FileReader(file)); // read the first line String line = in.readLine(); // continue to read lines whilst they are available while ( line != null ) { text.append(line); line = in.readLine(); } return text.toString(); } finally { // close the stream if ( in != null ) in.close(); } }
This approach works well when working with a single resource, unfortunately the problem is more complicated when multiple resources are involved.
The following mistake can be seen in many examples and whilst the code initially looks correct it has a major flaw:
public static boolean compareFile(File file1, File file2) throws FileNotFoundException, IOException { BufferedInputStream in1 = null; BufferedInputStream in2 = null; try { in1 = new BufferedInputStream(new FileInputStream(file1)); in2 = new BufferedInputStream(new FileInputStream(file2)); // compare the files boolean result = true; int i1, i2; do { i1 = in1.read(); i2 = in2.read(); result = (i1 == i2); } while ( result && i1 != -1 && i2 != -1); return result; } finally { // close the streams if ( in1 != null ) in1.close(); if ( in2 != null ) in2.close(); } }
The problems here is the close method can itself throw an exception and if this happens when closing the first stream, the second stream will not be closed.
The answer is simply to use another try-catch-finally statement around the first call to the close method.
public static boolean compareFile(File file1, File file2) throws FileNotFoundException, IOException { BufferedInputStream in1 = null; BufferedInputStream in2 = null; try { in1 = new BufferedInputStream(new FileInputStream(file1)); in2 = new BufferedInputStream(new FileInputStream(file2)); // compare the files boolean result = true; int i1, i2; do { i1 = in1.read(); i2 = in2.read(); result = (i1 == i2); } while ( result && i1 != -1 && i2 != -1); return result; } finally { // close the streams try { if ( in1 != null ) in1.close(); } finally { if ( in2 != null ) in2.close(); } } }