Java 11 Developer Certification - Exception try-catch-finally

May 04, 2021

In this section we will look further into the try-catch-finally blocks in an Exception.

try-catch block

At a minimum, the try-catch block looks like this.

try {

} catch (Throwable t) {

}

The above try-catch throws nothing and catches nothing. Unline other statements like if and else where we do not need curly braces for a one liner, in try-catch the curly braces are always required regardless of the lines within in.

Alternatively, we can use a finally clause instead of catch clause like below

try {

}
finally() {

}

Just declaring a try block without either catch or finally results in a compiler error.

Any exception that occurs in a statement wrapped by the try block will immediately stop the instruction set at the statement that triggered the error, and fall through to the catch statement.

In other words, all statements after the offending statement will be skipped.

If an error occurs in a loop or nested loop and the try block is outside of the loop, the code breaks out of any participating loops.

So lets look at some java code to understand the these blocks better.

We have the same getErrorStructure method which we used in the last section, it will print the exception class hierarchy.

The TryCatchExamples class has a simple method printThreeStatements, and the second statement does a division. In cases when divisor is passed as 0, it will throw an ArithmeticException, /by zero.

The normal usage is that we keep only such code in try block which might throw a runtime exception. Its a bad practice to include everything in the try block.

package maxCode.online.exceptions;

import java.io.FileInputStream;
import java.io.IOException;

public class TryCatchExamples {
	public static void main(String[] args) {
		TryCatchExamples te = new TryCatchExamples();
		te.printThreeStatements("Before Try Block", 1);
		try {
			FileInputStream f = new FileInputStream("ApplicationProperties.txt");
		} catch (IOException ie) {
			System.out.println("Maybe I want to do something specifically, like"
					+ " populate data as a default if file was properties file " + (5 / 0));
		} catch (Throwable t) {
			t.printStackTrace(System.out);
			printErrorStructure(t);
		}
		te.printThreeStatements("After Try/Catch", 0);
	}

	// This method will just print the hierarchy of the exception
	public static void printErrorStructure(Object o) {
		Class parent = o.getClass();
		String prefix = "";
		System.out.println("Error caught was: ");
		do {
			System.out.println(prefix + " " + parent.getName());
			prefix += "--";
			parent = parent.getSuperclass();
			if (parent == null)
				break;
		} while (parent.getSuperclass() != null);
	}

	private void printThreeStatements(String section, int divisor) {
		System.out.println(section + ": Statement 1 is just fine");
		System.out.println(section + ": Statement 2 will cause the error " + (2 / divisor));
		System.out.println(section + ": Statement 3 is just fine");
	}
}

Output

Before Try Block: Statement 1 is just fine
Before Try Block: Statement 2 will cause the error 2
Before Try Block: Statement 3 is just fine
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at maxCode.online.exceptions.TryCatchExamples.main(TryCatchExamples.java:14)

You could wrap every statement in a try-catch if you never wanted to miss a statement, but in general, an error is a significant event, and you may not want to proceed with succeeding code.

You can design placement of your try-catch blocks strategically to allow flow in some instances and break flow in others.

The FileInputStream method will throw an IOException, the flow will go to the catch block which is doing a divide by zero, as seen in the output.

Suppose if we modify that part and replace 0 by 1, that part of the code will execute fine, and the next statement te.printThreeStatements(“After Try/Catch”, 0); will throw an exception while trying to print the second line.

We can also catch the error, handle it and then the next lines of execution can continue as usual.

We have also used multiple catch blocks in the code, so that all kinds of exception are caught. Always remember in such cases, the broader exception should be at the end, otherwise the flow will not be proper.

try-finally block

We will create a new class for this examples.

FinallyExample has a nested set of try-catch statements in the main method. It is calling a method getPropertyFromFile for one file, and then another file. Both files do not exist currently.

package maxCode.online.exceptions;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class FinallyExample {
	public static void main(String[] args) {
		FinallyExample fex = new FinallyExample();
		try {
			System.out.println("Outer try block starts here....");
			String property = fex.getPropertyFromFile("FinallyProperties.txt");
		} catch (IOException | ArrayIndexOutOfBoundsException io) {
			System.out.println("Outer catch exception block starts here....");
			try {
				// Retry;
				System.out.println("Inner try block attempts retry....");
				String line = fex.getPropertyFromFile("AnotherProperties.txt");
			} catch (IOException e) {
				System.out.println("Inner catch exception block starts here....");
			}
		}
	}

	private String getPropertyFromFile(String filename) throws IOException {
		String property;
		BufferedReader br = new BufferedReader(new FileReader(filename));
		try {
			String line = br.readLine();
			property = line.split("\\s")[1];
			System.out.println("Property value = " + property);
		} finally {
			if (br != null)
				br.close();
		}
		return property;
	}
}

Output

Outer try block starts here....
Outer catch exception block starts here....
Inner try block attempts retry....
Inner catch exception block starts here....

It tries to get file, and in both cases the file does not exist and hence such output.

Now suppose if we add the file FinallyProperties.txt containing only a single line like below

PROPERTY_1 one

Now on running the code, we get the below output.

Outer try block starts here....
Property value = one

Notice that we have used multi-catch block so that IOException or ArrayIndexOutOfBoundsException both kind of exceptions can be caught.

So in this section we covered

  • multiple examples of try/catch blocks using generic and specific examples.
  • the interruption of the flow of the executable thread.
  • how to hide exceptions, we created multiple catch blocks.
  • nested try/catch blocks.
  • the finally clause, which when declared will always get executed. Its purpose is to clean up resources in a uniform way, for example, closing files.

throws clause

In the earlier sections, we saw that the checked exceptions in a method must be either core or handled in the method or the method must declare a throws clause in the declaration. We will look at that throws clause here.

We create a new class for this section, ThrowsClauseExample. We have 4 classes which are creating a custom type of exception which we want to throw. Each of them has a constructor, and calls super.

We also have 4 methods.

  • methodOne throws an unchecked runtime exception
  • methodTwo throws a checked exception which satisfy the catch block
  • methodThree throws Throwable which is specified using throws clause
  • methodFour just throws an unchecked error.

Each method is called in the main for loop, which has the try-catch block, and so each iteration continues after catching the exception.

package maxCode.online.exceptions;

public class ThrowsClauseExample {
	// Custom RuntimeException
	class ACustomRuntimeException extends RuntimeException {
		ACustomRuntimeException(String message) {
			super(message);
		}
	}

	// Custom Exception (not RuntimeException)
	class ACustomCheckedException extends Exception {
		ACustomCheckedException(String message) {
			super(message);
		}
	}

	// Custom Throwable
	class ACustomThrowable extends Throwable {
		ACustomThrowable(String message) {
			super(message);
		}
	}

	// Custom Error
	class AnError extends Error {
		AnError(String message) {
			super(message);
		}
	}

	public static void main(String[] args) {
		ThrowsClauseExample t = new ThrowsClauseExample();

		// For loop allows us to test each exception thrown
		for (int i = 0; i < 4; i++) {
			try {
				switch (i) {
				case 0:
					t.methodOne();
					break;
				case 1:
					t.methodTwo();
					break;
				case 2:
					t.methodThree();
					break;
				case 3:
					t.methodFour();
					break;
				}
				// Catch clause is inside for loop, so execution of
				// for loop continues after we catch the exception
				// Catch clause is inside for loop, so execution of
				// for loop continues after we catch the exception
			} catch (ACustomThrowable te) {
				System.out.println("In the catch clause for" + " ACustomThrowable in main(): " + te);
			} catch (AnError ae) {
				System.out.println("In the catch clause for" + " AnError in main(): " + ae);
			} catch (ACustomRuntimeException are) {
				System.out.println("In the catch clause for" + " ACustomRuntimeException in main(): " + are);
			} catch (RuntimeException e) {
				System.out.println("In the catch clause of main(): " + e);
			}
		}
	}

	// Unchecked Runtime Exception Thrown
	private void methodOne() {
		throw new ACustomRuntimeException("Error in methodOne");
	}

	// Checked - must satisfy catch or specify - here we catch.
	private void methodTwo() {
		try {
			throw new ACustomCheckedException("Error in methodTwo");
		} catch (Exception e) {
			System.out.println("In the catch clause of methodTwo(): " + e);
			throw new ACustomRuntimeException("Changed methodTwo to throw ACustomRuntimeException");
		}
	}

	// Checked - must satisfy catch or specify - here we specify
	// in the throws clause
	private void methodThree() throws ACustomThrowable, ACustomRuntimeException {
		if (10 % 3 == 0)
			throw new ACustomThrowable("Error in methodThree");
		else
			throw new ACustomRuntimeException("Error in methodThree");
	}

	// Unchecked Error thrown
	private void methodFour() {
		throw new AnError("Error in methodFour");
	}
}

Output

In the catch clause for ACustomRuntimeException in main(): maxCode.online.exceptions.ThrowsClauseExample$ACustomRuntimeException: Error in methodOne
In the catch clause of methodTwo(): maxCode.online.exceptions.ThrowsClauseExample$ACustomCheckedException: Error in methodTwo
In the catch clause for ACustomRuntimeException in main(): maxCode.online.exceptions.ThrowsClauseExample$ACustomRuntimeException: Changed methodTwo to throw ACustomRuntimeException
In the catch clause for ACustomRuntimeException in main(): maxCode.online.exceptions.ThrowsClauseExample$ACustomRuntimeException: Error in methodThree
In the catch clause for AnError in main(): maxCode.online.exceptions.ThrowsClauseExample$AnError: Error in methodFour

Note the multiple catch blocks in the for loop. If we remove either of them, the compiler will start complaining to add the respective catch. So its mandatory to either catch the exception OR specify it in the method declaration using throws clause.

Also the order of the catch statements is very important. The more specific exception would never get executed if we add the generalized method at the top. The order should always be from the most specific to the least specific.

There is no limit to how many try-catch can be propogated. So we need not worry about that.

So, in this section, we went through the below topics

  • the four types of errors that can be thrown. Throwable, error, exception (which isn’t runtime exception) and runtime exception.
  • demonstrated customising each of the types.
  • Throwing each of the types from methods.
  • identifying which of the four are considered checked, and the additional requirements to satisfy the compiler when they are checked.
  • Adding one or more exceptions to the throw clause, of a method declaration.
  • testing multiple clauses associated to a single trial clause, noting that order matters and you should declare exceptions in the catch clauses in order of most specific to the least specific.

That is all for this section. In the next part, we will look at more topics related to exception in detail.