Java’s exception handling mechanism provides developers with an elegant, easy-to-use way to handle exceptional situations in their programs. For example, if there is a file operation to be performed, one might write code like:
String fileToRead = "someFileToRead"; String fileToWrite = "someFileToWrite"; try { FileInputStream fis = new FileInputStream(fileToRead); FileOutputStream fos = new FileOutputStream(fileToWrite); byte[] readBuffer = new byte[2152]; int bytesIn = 0; while((bytesIn = fis.read(readBuffer)) != -1) { fos.write(readBuffer, 0, bytesIn); } //close the streams fos.close(); fis.close(); } catch (Exception e) { //handle exception }
Now, if any exception happens before we get to close the streams, the execution of the program will go to our exception handler, and we will leave the streams open. This is a very potential source of memory leaks. Java’s finally clause provides us with a mechanism to prevent such situations from happening.
The code inside a finally block will be executed through both normal execution of the program as well as situations where exceptions take place. Thus, finally blocks guarantee code execution. Let’s re-arrange our code above to take advantage of a finally block when your code runs normally but also when an exception occurs in the try block.
String fileToRead = "someFileToRead"; String fileToWrite = "someFileToWrite"; FileInputStream fis = null; FileOutputStream fos = null; try { fis = new FileInputStream(fileToRead); fos = new FileOutputStream(fileToWrite); byte[] readBuffer = new byte[2152]; int bytesIn = 0; while((bytesIn = fis.read(readBuffer)) != -1) { fos.write(readBuffer, 0, bytesIn); } } catch (Exception e) { //handle exception } finally { //close the streams //the close operation may throw exceptions try { fos.close(); } catch(Exception e) { } finally { try { fis.close } catch(Exception e) { } } }
This way, we make sure that we close our streams regardless stream operations being successful or exceptional.