finally
block following the try
and catch
blocks to ensure that the resources are released. Also, these methods may throw an exception during the process which we rarely use. But if we don't capture the exception the finally block will fail and not fully complete, failing to release the following resources.
Let say we're copying form one stream to another. Then we need to close both streams at the end of the process as illustrated below.
package com.albertattard.utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class CopyTwoStreams {
public static void main(String[] args) throws IOException {
InputStream in = null;
OutputStream out = null;
try {
// copy code here
} finally {
in.close();
out.close();
}
}
}
If an exception is thrown while closing the input stream, the output stream is never closed as the finally block will stop executing.
Handling Exceptions within the finally
block
There are several ways how we can handle exceptions within the finally
block. One alternative is to make use of a try
and catch
blocks but that may look bad (from a visual perspective). Alternatively, we can have a method which closes our streams and also absorbing any exceptions that may be thrown during the process.
As from Java 1.5, the java.io.Closable interface was introduce to the Java IO package and most (if not all) classes (and interfaces) in this package implement this interface. As you can see from the JavaDoc, this interface has only one method named close()
. The good thing about this interface is that you can define one generic method which handles the close invocation. Following is a simple example:
public static IOException close(Closeable c){
if(c != null) {
try {
c.close();
} catch(IOException e) {
return e;
}
}
return null;
}
So now we can change our previous code to make use of this new static method.
This method simply attempts the close a non-null closable object and returns null
if either the object provided is null
or it managed to close the object without any issues. Otherwise, the exception thrown in the process is returned (instead of propagated) to the caller.
package com.albertattard.utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class CopyTwoStreams {
public static void main(String[] args) throws IOException {
InputStream in = null;
OutputStream out = null;
try {
// copy code here
} finally {
close(in);
close(out);
}
}
}
Any exceptions thrown during the first close are absorbed and only returned by the new static close()
method. These will not disrupt the flow of the finally
block and all streams' close()
methods are invoked.
Unfortunately the above method cannot be used with classes and interfaces that do not implement or inherit from the Closable
interface. Classes and interfaces outside the Java IO package such as the interface java.sql.Connection cannot benefit from such a method.
A generic approach
By using reflection, we can retrieve theclose()
method and dynamically invoke it. Our generic method can be used with anything that has a method called close which takes no parameters, just like the method defined by the Closeable
interface. The advantage of this approach is that we don't have to handle different close objects with different methods which basically do the same thing - simple close the object and absorbing any exceptions thrown in the process.
public static Exception close(Object o){
if(o != null) {
try {
Class<?> clazz = o.getClass();
Method closeMethod =
clazz.getDeclaredMethod("close", new Class[0]);
closeMethod.invoke(o, new Object[0]);
} catch(Exception e) {
return e;
}
}
return null;
}
We had to make some changes to our original static close()
method to make use of reflection (highlighted in the above example). In a nutshell our Java classes, fields and methods are represented by other Java classes which we can use and invoke. A more comprehensive explanation about reflection can be found in this article Reflection in Action.
Conclusion
This short article illustrated how to write a simple generic method that can be used to invoke theclose()
method and return any exceptions thrown in the process. This approach makes use of reflection which adds flexibility but also add performance costs. This is the slowest approach for closing an object.
No comments:
Post a Comment