Exceptions

Understanding Exceptions

Understanding Exception Types

An exception is an event that alters program flow. There’s a Throwable superclass for all objects that represent these events.

The class Error is for when something goes so horribly wrong that your program should not attempt to recover from it. From example, the disk drive has disappeared or the program runs out of memory.

Throwable is the parent class of all exceptions, including Error.

Checked Exceotions

A checked exception is an exception that must be declared or handled by the application code where it’s thrown. They all inherit from Exception, but not from Error or RuntimeException.
They tend to be more anticipated - from example, trying to read a file that doesn’t exist.

The handle or declare rule means that all checked exceptions that could be thrown within a method, are either wrapped in compatible try and catch blocks, or declared in the method signature.

void fall(int distance) throws IOException {
	if(distance > 10) {
		throw new IOException();
	}
}

The throw keyword tells Java that you want to throw an Exception.
The throws keyword simply declares that the method might throw an Exception. It also might not.

To handle the exception

void fall(int distance) {
	try {
		if(distance > 10) {
			throw new IOException();
		}
	} catch(Exception e) {
		e.printStackTrace();
	}
}

Unchecked Exceptions

An unchecked exception is any exception that does not need to be declared or handled by the application code where it’s thrown. They’re often referred to as runtime exceptions. They include any class that inherits from RuntimeException or Error.
They tend to be unexpected, but not necessarily fatal. For example, NullPointerException.

void fall(String input) {
	sout(input.toLowerCase());
}

void(null);

Throwing an Exception

On the exam, you will see two types of code that result in an exception.

The first is code that’s wrong

// throws ArrayIndexOutOfBoundsException
String[] animals = new String[0];
sout(animals[0]);

(!) Pay special attention to code that calls a method on a null reference or that references an invalid array or List index. (!)

The second way is to explicitly request to throw the Exception.

// all these are valid
throw new Exception();
throw new Exception("Ow! I fell. ");
throw new RuntimeException();
throw new RuntimeException("Ow! I fell. ");

(!) Anytime you see throw or throws, make sure the correct one is being used. (!)
throw is used to throw a new exception
throws is used only at the end of a method declaration.

An Exception is an Object. You can store it in a variable.

Exception e = new RuntimeException();
throw e;

They need to be instantiated

throw RuntimeException(); // DOES NOT COMPILE

Be careful with unreacheable code.

try {
	throw new RuntimeException();
	throw new ArrayIndexOutOfBoundsException(); // DOES NOT COMPILE!
} catch(Exception ex) {
}

Recognizing Exception Classes

RuntimeException Classes

RuntimeException and its subclasses are unchecked exceptions that don’t have to be handled or declared. They can be thrown by the programmer or by the JVM.

  • ArithmeticException - Thrown when code attempts to divide by zero
  • ArrayIndexOutOfBoundsException - code uses an illegal index to access an array
  • ClassCastException - an attempt is made to cast an object to a class of which is not an instance
  • NullPointerException - there’s a null reference where an object is required
  • IllegalArgumentException - thrown by the programmer to indicate that a method has been passed an illegal or inappropiate argument
  • NumberFormatException - Subclass of IllegalArgumentException. When an attempt is made to convert a String to a numeric type, but it doesn’t have an appropiate format

Checked Exception Classes

They have Exception in their hierarchy but not RuntimeException. They must be handled or declared.

  • IOException - thrown programmatically when there’s a problem reading or writing a file.
  • FileNotFoundException - subclass of IOException. thrown when code tries to reference a file that does not exist.

Error Classes

Errors are unchecked exceptions that extend from Error. They’re thrown by the JVM and should not be handled or declared. They’re rare.

  • ExceptionInInitializerError - Thrown when a static initializer throws an exception and doesn’t handle it.
static {
	int[] countsOfMoose = new int[3];
	int num = countsOfMoose[-1];
}
  • StackOverflowError - a method calls itself too many times (infinite recursion).
  • NoClassDefFoundError - a class that the code uses is available at compile time but not at runtime.

Handling Exceptions

Using try and catch Statements

Java uses a try statement to separate the logic that might throw an exception, from the logic to handle that exception.

The curly braces are required for try and catch blocks.

try {

} catch(Exception ex) {

}

Some invalid try statements

try // DOES NOT COMPILE
	fall();
catch(Exception ex)
	sout();
try { // DOES NOT COMPILE
	fall();
}

Chaining catch Blocks

First, you must be able to recognize if the exception is a checked or an unchecked exception.
Second, you need to determine whether any of the exceptions are subclasses of the others.

// unchecked exceptions
class AnimalsOutForAWalk extends RuntimeException { }
class ExhibitClosed extends RuntimeException { }
class ExhibitClosedForLunch extends ExhibitClosed { }
public void visitPorcupine() {
	try {
		seeAnimal();
	} catch (AnimalsOutForAWalk e) { // first catch block
		System.out.print("try back later");
	} catch (ExhibitClosed e) { // second catch block
		System.out.print("not today");
	}
}

A rule exists for the order of the catch blocks. Java looks at them in the order they appear. If it’s impossible for one of the catch blocks to be executed, a compiler error about unreachable code occurs.
(!) This happens when a superclass catch block appears before a subclass catch block. Pay attention to any subclass exceptions. (!)

public void visitMonkeys() {
	try {
		seeAnimal();
	} catch(ExhibitClosedForLunch e) { // subclass
		sout("try back later");
	} catch(ExhibitClosed e) { // superclass
		sout("not today");
	}
}

If the order was reversed, they wouldn’t compile as the second one couldn’t ever be reached.

public void visitMonkeys() {
	try {
		seeAnimal();
	} catch(ExhibitClosed e) {
		sout("try back later");
	} catch(ExhibitClosedForLunch e) { // DOES NOT COMPILE
		sout("not today");
	}
}

Applying a Multi-catch Block

If we want to apply the same result to multiple exceptions, we may re-use code.

public static void main(String args[]) {
	try {
		soutln(Integer.parseInt(args[1]));
	} catch(ArrayIndexOutOfBoundsException | NumberFormatException ex) {
		sout("Missing or invalid input");
	}
}

(!) The exam might try to trick you with invalid syntax (!)

catch(Exception1 e | Exception2 e) // DOES NOT COMPILE
catch(Exception1 e1 | Exception2 e2) // DOES NOT COMPILE
catch(Exception1 | Exception2 e) // COMPILES

Multi-cath is intented to be used for exceptions that aren’t related, and it prevents you from specifying redundant types in a multi-catch.

try {
	throw new IOException();
} catch(FileNotFoundException | IOException p) {} // DOES NOT COMPILE

This fails, because FileNotFoundException is already caught by the alternative IOException.
The Exceptions’ order doesn’t matter inside a single block.
You can’t list the same exception type more than once in the same try statement.
The more general superclasses must be caught after their subclasses.

Adding a finally Block

The try statement also lets you run code at the end with a finally clause, regardless of whether an exception is thrown.

try {
	seeAnimals();
	fall();
} catch(Exception e) {
	getHug();
} finally {
	seeMoreAnimals();
}

If an exception is thrown, the finally block is run after the catch block. If no exception is thrown, the finally block is run after the try block completes.

The exam will try to trick you with missing clasuses, or clasuse in the wrong order.

try { // DOES NOT COMPILE
	fall();
} finally {
	sout("all better");
} catch(Exception e) {
	sout("get up");
}

try { // DOES NOT COMPILE
	fall();
}

try { // COMPILES
	fall();
} finally {
	sout("all better");
}

The catch block is not required if finally is present.

StringBuilder sb = new StringBuilder();
try {
	sb.append("t");
} catch(Exception e) {
	sb.append("c");
} finally {
	sb.append("f");
}

sb.append("a");
sout(sb.toString());

This will print tfa.

If there’s a finally block, this will always be executed.

int goHome() {
	try {
		sout("1");
		return -1;
	} catch(Exception e) {
		sout("2");
		return -2;
	} finally {
		sout("3");
		return -3;
	}
}

This will print 13 and it will always return -3.

There’s one exception to this: System.exit(0).

try {
	System.exit(0);
} finally {
	sout("Never going to get here"); // not printed.
}

Finally Closing Resources

It avoids resource leaks by closing resources.

public void readFile(String file) {
	try (FileInputStream is = new FileInputStream("myfile.txt")) {
		// do something
	} catch(IOException e) {
		e.printStackTrace();
	}
}

As soon as a connection passes out of scope, Java will attempt to close it.

Behind the scenes, the compiler replaces this try-with-resources block for a try with a finally block. You can still create a programmer defined finally block, just be aware that the implicit one will be called first.

Basics of Try-with-Resources

One or more resources can be opened in the try clause. When there’re multiple resources opened, they’re closed in the reverse order from which they were created.

try (FileInputStream in = new FileInputStream("data.txt");
		FileOutputStream out = new FileOutputStream("output.txt")) {

}

The catch block is optional in try-with-resources!

You can’t put any random class inside the try-with-resources statement. Java requires a class which implements AutoCloseable interface, which includes a void close() method.

Declaring Resources

While try-with-resources does support declaring multiple variables, each variable must be declared in a separate statement.

try (MyFileClass is = new MyFileClass(1), // DOES NOT COMPILE
		os = new MyFileClass(2)) {

}
try (MyFileClass ab = new MyFileClass(1),
		MyFileClass cd = new MyFileClass(2)) { // DOES NOT COMPILE

}

You can declare a resource using var as the data type.

try (var f = new FileInputStream("it.txt")) {
	// process file
}

Scope of Try-with-Resources

The resources created are in scope only within the try block.

try (Scanner s = new Scanner(System.in)) {
	s.nextLine();
} catch(Exception e) {
	s.nextLine(); // DOES NOT COMPILE
} finally {
	s.nextLine(); // DOES NOT COMPILE
}

The problem is that Scanner has gone out of scope. You can’t accidentally use an object that has been closed.

Following Order of Operation

  • Resources are closed after the try clause ends and before any catch/finally clauses.
  • Resources are closed in the reverse order from which they were created.
public class MyFileClass implements AutoCloseable {
	private final int num;

	public MyFileClass(int num) { this.num = num; }

	public void close() {
		sout("Closing: " + num);
	}
}

public static void main(String... xyz) {
	try(MyFileClass a1 = new MyFileClass(1);
			MyFileClass a2 = new MyFileClass(2)) {
		throw new RuntimeException();		
	} catch (Exception e) {
		sout("ex");
	} finally {
		sout("finally");
	}
}

Since the resources are closed in reverse order, this prints

Closing: 2
Closing: 1
ex
finally

Throwing Additional Exceptions

There’re situations where we need to catch an exception inside of a catch or finally block.

public static void main(String[] a) {
	FileReader reader = null;
	try {
		reader = read();
	} catch(IOException ex) {
		try {
			if(reader != null)
				reader.close();
		} catch (IOException inner) {
			//
		}
	}
}

private static FileReader read() throws IOException {
	//
}

The following example shows that only the last exception to be thrown matter.

try {
	throw new RuntimeException();
} catch(RuntimeException ex) {
	throw new RuntimeException();
} finally {
	throw new Exception();
}

Another example

public String exceptions() {
	StringBuilder result = new StringBuilder();
	String v = null;

	try {
		try {
			result.append("before_");
			v.length();
			result.append("after_");
		} catch(NullPointerException e) {
			result.append("catch_");
			throw new RuntimeException();
		} finally {
			result.append("finally_");
			throw new Exception();
		}
	} catch(Exception e) {
		result.append("done");
	}
	return result.toString();
}

This prints before_catch_finally_done

Calling Methods That Throw Exceptions

When you’re calling a method that throws an exception, the rules are the same as within a method.

class NoMoreCarrotsException extends Exception {}

public class Bunny {
	private static void eatCarrot() throws NoMoreCarrotsException {
	}

	public static void main(String[] args) {
		eatCarrot(); // DOES NOT COMPILE
	}
}

Checked exceptions must be handled or declared. The solution would be either of this.

public static void main(String[] args)
	throws NoMoreCarrotsException {
	eatCarrot();
}
public static void main(String[] args) {
	try {
		eatCarrot();
	} catch(NoMoreCarrotsException ex) {
		//
	}
}

The reverse is also true, we cannot try to catch an exception when a method doesn’t include it in its declaration.

private void eatCarrot() {}

public void bad() {
	try {
		eatCarrot();
	} catch(NoMoreCarrotsException ex) { // DOES NOT COMPILE
		// ex
	}
}

(!) When you see a checked exception declared inside a catch block on the exam, check and make sure the code in the associated try block is capable of throwing the exception or a subclass of the exception. If it’s not capable, the code is unreachable and does not compile.
Remember that this rules does not extend to unchecked exceptions or exceptions declared in a method signature. (!)

Declaring and Overriding Methods with Exceptions

When a class overrides a method from a superclass or implements a method from an interface, it’s not allowed to add new checked exceptions to the method signature.

class CanNotHopException extends Exception { }

class Hopper {
	public void hop() {}
}

class Bunny extends Hopper {
	public void hop() throws CanNotHopException {} // DOES NOT COMPILE
}

An overriden method in a subclass is allowed to declare fewer exceptions than the superclass or interface. This is legal because callers are already handling them.

class Hopper {
	public void hop() throws CanNotHopException { }
}

class Bunny extends Hopper {
	public void hop() { }
}

Similarly, a class is allowed to declare a subclass of an exception type.

class Hopper {
	public void hop() throws Exception {}
}

class Bunny extends Hopper {
	public void hop() throws CanNotHopException {}
}

The following code is legal because it has an unchecked exception in the subclass.

class Hopper {
	public void hop() {}
}

class Bunny extends Hopper {
	public void hop() throws IllegalStateException {}
}

The declaration is redundant. Methods are free to throw any unchecked exceptions they want without mentioning them in the method declaration.

Printing an Exception

There’re three ways to print an exception.

  • You can let Java print it out
  • Print just the message
  • Print where the stack trace comes from
private static void hop() {
	throw new RuntimeException("cannot hop");
}

public static void main(String[] args) {
	try {
		hop();
	} catch(Exception ex) {
		sout(ex);
		sout(ex.getMessage());
		ex.printStackTrace();
	}
}

They result in the following output

java.lang.RuntimeException: cannot hop
cannot hop
java.lang.RuntimeException: cannot hop
	at Handling.hop(Handling.java:15)
	at Handling.main(Handling.java:7)

Summary

An exception indicates something unexpected happened. A method can handle an exception by catching it or declaring it for the caller to deal with. Many exceptions are thrown by Java libraries. You can throw your own exceptions with code such as throw new Exception().

All exceptions inherit Throwable. Subclasses of Error are exceptions that a programmer should not attempt to handle. Classes that inherit RuntimeException and Error are runtime (unchecked) exceptions. Classes that inherit Exception, but not RuntimeException, are checked exceptions. Java requires checked exceptions to be handled with a catch block or declared with the throws keyword.

A try statement must include at least one catch block or a finally block. A multicatch block is one that catches multiple unrelated exceptions in a single catch block. If a try statement has multiple catch blocks chained together, at most one catch block can run. Java looks for an exception that can be caught by each catch block in the order they appear, and the first match is run. Then execution continues after the try statement. If both catch and finally throw an exception, the one from finally gets thrown.

A try-with-resources block is used to ensure a resource like a database or a file is closed properly after it is created. A try-with-resources statement does not require a catch or finally block but may optionally include them. The implicit finally block is executed before any programmer-defined catch or finally blocks.

RuntimeException classes you should know for the exam include the following:

  • ArithmeticException
  • ArrayIndexOutOfBoundsException
  • ClassCastException
  • IllegalArgumentException
  • NullPointerException
  • NumberFormatException

IllegalArgumentException is typically thrown by the programmer, whereas the others are typically thrown by the standard Java library.

Checked Exception classes you should know for the exam include the following:

  • IOException
  • FileNotFoundException

Error classes you should know for the exam include the following:

  • ExceptionInInitializerError
  • StackOverflowError
  • NoClassDefFoundError

For the exam, remember that NumberFormatException is a subclass of IllegalArgumentException, and FileNotFoundException is a subclass of IOException. When a method overrides a method in a superclass or interface, it is not allowed to add checked exceptions. It is allowed to declare fewer exceptions or declare a subclass of a declared exception. Methods declare exceptions with the keyword throws.

Exam Essentials

Understand the various types of exceptions. All exceptions are subclasses of java.lang.Throwable. Subclasses of java.lang.Error should never be caught. Only subclasses of java.lang.Exception should be handled in application code.

Differentiate between checked and unchecked exceptions. Unchecked exceptions do not need to be caught or handled and are subclasses of java.lang.RuntimeException and java.lang.Error. All other subclasses of java.lang.Exception are checked exceptions and must be handled or declared.

Understand the flow of a try statement. A try statement must have a catch or a finally block. Multiple catch blocks can be chained together, provided no superclass exception type appears in an earlier catch block than its subclass. A multi-catch expression may be used to handle multiple exceptions in the same catch block, provided one exception is not a subclass of another. The finally block runs last regardless of whether an exception is thrown.

Be able to follow the order of a try-with-resources statement. A try-with-resources statement is a special type of try block in which one or more resources are declared and automatically closed in the reverse order of which they are declared. It can be used with or without a catch or finally block, with the implicit finally block always executed first.

Identify whether an exception is thrown by the programmer or the JVM. IllegalArgumentException and NumberFormatException are commonly thrown by the programmer. Most of the other unchecked exceptions are typically thrown by the JVM or built-in Java libraries.

Write methods that declare exceptions. The throws keyword is used in a method declaration to indicate an exception might be thrown. When overriding a method, the method is allowed to throw fewer or narrower checked exceptions than the original version.

Recognize when to use throw versus throws. The throw keyword is used when you actually want to throw an exception—for example, throw new RuntimeException(). The throws keyword is used in a method declaration.