Java SE 8/11 Programmer II Exam Series: Exception Handling and Assertions

Introduction

In this post, we will look at the following exam objectives. Note that this is also a part of the Java SE 11 Programmer II Certification.

  • Use the try-with-resources construct
  • Create and use custom exception classes
  • Test invariants by using assertions

Use the try-with-resources construct

The new try-with-resources statement automatically closes all resources opened in the try clause. This is known as automatic resource management as Java automatically takes care of the closing. The catch and finally blocks are optional. However, we are not allowed to do this in try-catch statement.

FileReader fr = new FileReader(path);   
try(BufferedReader br = new BufferedReader(fr);){
   br.lines().distinct().forEach(System.out::println);
} catch (IOException e) {
// optional exeption handler
} finally {
// optional finally block
}

The resources created in the try clause are only in scope within the try block. The benefit is that we can’t accidentally use a resource that has been closed. The following code doesn’t compile as BufferedReader is closed at the end of the try statement.

    FileReader fr = new FileReader(path);        
    try(BufferedReader br = new BufferedReader(fr);){
        br.lines().distinct().forEach(System.out::println);
    }  catch (Exception e) {
        br.readLine(); 
    } finally {
        br.read();
    }

AutoCloseable

There is a restriction for a class to be created in try-with-resources statement. The class should implement the AutoCloseable interface.

According to the API specification, we are strongly encouraged to declare concrete implementations of the close method to throw more specific exceptions, or to throw no exception at all if the close operation cannot fail.

AutoClosable without exception

public class AutoClosableNoException implements AutoCloseable{
	@Override
	public void close(){
	    System.out.println("Inside close method");
	}
}

AutoClosable with a specific exception

public class AutoClosableSpecificException implements AutoCloseable{
	@Override
	public void close() throws FileNotFoundException {
	    System.out.println("Inside close method");
	}
}

When the try-with-resources ends, the resource is closed using the inherited close() method. The code below executes close() method after try statement.

public class Valid implements AutoCloseable {

  @Override
  public void close() {
    System.out.println("Close the " + this.getClass().getSimpleName());
  }
  public static void main(String[] args) {
	try (Valid c = new Valid()) {
	     System.out.println("Inside try-with-resources statement");
	}
  }
}

It prints out the following:

Inside try-with-resources statement
Close the ValidAutoCloseable

The try-with-resources statement throws a checked exception, which needs to be handled or declared. Otherwise, we get a compile error:

Unhandled exception type Exception thrown by automatic close() invocation on c

The following code would compile if the main() method declared an Exception or the Exception was handled by the catch clause.

public class InvalidAutoClosable implements AutoCloseable {

	@Override
	public void close() throws Exception {
		// TODO Auto-generated method stub
		System.out.println("Inside close method");
	}
	
	public static void main(String[] args) {
		try (InvalidAutoClosable c = new InvalidAutoClosable()) {
			System.out.println("Inside try-with-resources statement");
		}
	}

}

Declare an exception using throws

public static void main(String[] args) throws Exception {
	try (InvalidAutoClosable c = new InvalidAutoClosable()) {
		System.out.println("Inside try-with-resources statement");
	}
}

Handle an exception using catch

try (InvalidAutoClosable c = new InvalidAutoClosable()) {
	System.out.println("Inside try-with-resources statement");
} catch (Exception e) {
	e.printStackTrace();
}

Suppressed Exceptions

There are some cases where exceptions are thrown in both the close method and try-with-resources statement. In this case, Java chooses a primary exception and considers other exceptions as “suppressed exceptions”.

In this example, the try clause throws a primary exception, which is an IOException. Java automatically calls the close() method, which throws an IOException. Then, the exception is added as a suppressed exception. The catch statement catches the primary exception. This is where we print the primary exception message. Finally, it prints the suppressed exception.

public class SuppressedExceptionAutoCloseable implements AutoCloseable {
	
public void close() throws IOException { 
	throw new IOException("IOException in close()");
}

public static void main(String[] args) throws IOException {
	try (SuppressedExceptionAutoCloseable t = new SuppressedExceptionAutoCloseable()) {
		throw new IOException("try throws an exception"); 
	} catch (IOException e) {
		System.out.println("inside catch: " + e.getMessage());
				for (Throwable t: e.getSuppressed())
			System.out.println(t.getMessage());
	}
}
}

The output is as follows:

inside catch: try throws an exception
IOException in close()

However, if we threw an exception other than the IOException in the catch statement, such as a NullPointerException, we would get a different error message.

...
try () {
  throw new NullPointerException("try throws an exception"); 
} catch (IOException e) {			
   ...
}

Because the catch statement tries to match the primary exception, NullPointerException, with the IOException.

Exception in thread "main" java.lang.NullPointerException: try throws an exception
	at exceptions_and_assertions.SuppressedExceptionAutoCloseable2.main(SuppressedExceptionAutoCloseable2.java:24)
	Suppressed: java.io.IOException: IOException in close()
		at exceptions_and_assertions.SuppressedExceptionAutoCloseable.close(SuppressedExceptionAutoCloseable.java:14)
		at exceptions_and_assertions.SuppressedExceptionAutoCloseable2.main(SuppressedExceptionAutoCloseable2.java:25)

Another thing we need to know is that the resources are closed in the reverse order from which they were created. The following example first closes t2 and then t1. Since t1 is closed first, it is selected as the primary exception, whereas t2 is considered as a suppressed exception

public class SuppressedExceptionAutoCloseable3 implements AutoCloseable {
	
	public void close() throws IOException { 
		throw new IOException("IOException in close()" + this.getClass().getName());
	}

	public static void main(String[] args) throws IOException {
		try (SuppressedExceptionAutoCloseable t1 = new SuppressedExceptionAutoCloseable();
		SuppressedExceptionAutoCloseable t2 = new SuppressedExceptionAutoCloseable()) {
			throw new IOException("try throws an exception"); 
		} catch (IOException e) {
			System.out.println("inside catch: " + e.getMessage());
			
			for (Throwable t: e.getSuppressed())
				System.out.println(t.getMessage());
		}
	}
}

The output is as follows:

inside catch: try throws an exception
IOException in close()
IOException in close()

Create and use custom exception classes

When we creating our own exception, we need to decide whether it should be a checked or unchecked exception.

The three most common constructors defined by the Exception class are as follows. Note that using a different constructor allows us to provide more information about what went wrong.

public class CustomException extends Exception {
  // throw new CustomException(); in main
  public CustomException() {
    super(); 
  }
  public CustomException(Exception e) {
    // throw new CustomException(new RuntimeException()); in main
    super(e); 
  }
  public CustomException(String message) {
   // throw new CustomException(new RuntimeException()); in main
    super(message); 
  }
}

The code below demonstrates the different types of constructors defined in CustomException.

public static void main(String[] args) {
	Scanner sc = new Scanner(System.in);
	int number = sc.nextInt();
	try {
		if (number == 0) {
			throw new CustomException();
		}
		else if (number < 0) {
			throw new CustomException("Invalid number: " + number);
		}
		else if (number > 0) {
			throw new CustomException(new RuntimeException());
		}
	} catch (CustomException e) {
		e.printStackTrace();
	}
}

Test invariants by using assertions

Assertions are used for testing and debugging purposes and they are turned off by default.

Assertion Types

Internal Invariants

We use it when we want to be sure that the value is within a certain constraint.

assert i > 100

Another example is converting the code comments to an internal invariant. Here is an example of the Oracle documentation.

Before

if (i % 3 == 0) {
    ...
} else if (i % 3 == 1) {
    ...
} else { // We know (i % 3 == 2)
    ...
}

After

if (i % 3 == 0) {
   ...
} else if (i % 3 == 1) {
    ...
} else {
    assert i % 3 == 2 : i;
    ...
}

Class Invariant

It is a private method to validate the object’s state. It returns a boolean value. The following class shows the usage of a class invariant.

class Cuboid  {

	private int width, length, height;

	public Cuboid(int width, int height) {
		this.width = width;
		this.length = length;
		this.height = height;
	} 
	
	public int getVolume() {
		assert isValid(): "Not a valid Cuboid";
		return width * height * length ;
	}
	private boolean isValid() {
		return (width >= 0 &amp;&amp; height >= 0 &amp;&amp; length >=0);
	}
}

Preconditions

Preconditions on public methods are enforced by explicit checks inside methods resulting in particular, specified exceptions.

According to Oracle documentation and the Effective Java Item 49, preconditions on public methods are enforced by explicit checks specified exceptions, meaning, it is not suitable to use an assert. For example:

 /**
  * Sets the refresh rate.
  *
  * @param  rate refresh rate, in frames per second.
  * @throws IllegalArgumentException if rate <= 0 or
  *          rate > MAX_REFRESH_RATE.
  */
  public void setRefreshRate(int rate) {
	// Enforce specified precondition in public method
    if (rate <= 0 || rate > MAX_REFRESH_RATE)
		throw new IllegalArgumentException("Illegal rate: " + rate);
	setRefreshInterval(1000/rate);
  }

However, nonpublic methods can check their parameters using assertions. These assertions are claims that the enclosing method, setRefreshRate, guarantees that it will enforce the argument checks.

/**
 * Sets the refresh interval (must correspond to a legal frame rate).
 *
 * @param  interval refresh interval in milliseconds.
 */
 private void setRefreshInterval(int interval) {
 // Confirm adherence to precondition in nonpublic method
	assert interval > 0 &amp;&amp; interval <= 1000/MAX_REFRESH_RATE;
	... // Set the refresh interval
}

Post Conditions

Postcondition checks are best implemented via assertions, whether or not they are specified in public methods.

public BigInteger modInverse(BigInteger m) {
  if (m.signum <= 0)
    throw new ArithmeticException("Modulus not positive: " + m);
  ... // Do the computation
  assert this.multiply(result).mod(m).equals(ONE) : this;
  return result;
}

Summary

In this section, we looked at how to use the try-with-resources construct, custom exception classes, and the test invariants by using assertions.  You can find the source code on GitHub.

Leave a Reply

Your email address will not be published. Required fields are marked *