Exceptions and Assertions

Use try-catch and throw statements

A try block is used to enclose code that might throw an exception and it can be followed by one or many catch block.

A catch block is used to handle an exception. It defines the type of the exception and a reference. For example:

try {  
// Code that may throw an exception  
} catch(Exception e){
 // Do something with the exception using reference e
}

If exception is not handled, the Java Virtual Machine provides a default exception handler that performs the following tasks:

  1. Prints out exception description.
  2. Prints the stack trace (Hierarchy of methods where the exception occurred).
  3. Causes the program to terminate.

But if an exception is handled by in a try-catch block, the normal flow of the application is maintained and rest of the code is executed.

If you want to manually throw an exception, use the throw keyword:

public class Test {  
   public static boolean validateName(String name) {
      boolean valid = true;
      try {
        if(name != null) {
         throw new Exception("The name is not valid");
        }
      } catch(Exception e) {
        valid = false;
      }

      return valid;
   }  
   public static void main(String args[]) {  
      if(validateName(null)) {
        System.out.println("Valid Name");
      } else {
        System.out.println("Invalid Name");
      }
  }  
}

The output is:

Invalid name

 

Use catch, multi-catch, and finally clauses

Catch and multi-catch

If you're handling multiple exceptions, the catch blocks must be ordered from most specific to most general, for example, the catch for IndexOutOfBoundsException must come before the catch for Exception, otherwise, a compile-time error is generated:

try {  
  int arr[] = new int[5];  
  arr[10] = 20;  
}  
catch(ArrayIndexOutOfBoundsException e) {
  e.printStackTrace();
}  
catch(ArithmeticException e) {
  e.printStackTrace();
}

The problem with this example is that it contains duplicate code in each of the catch blocks. For this situation, since Java 7, we can use a multi-catch block:

try {  
  int arr[] = new int[5];  
  arr[10] = 20;  
  int r = arr[10]/10;
}  
catch(ArrayIndexOutOfBoundsException|ArithmeticException ex) {
  ex.printStackTrace();
}

The multi-catch clause specifies the types of exceptions that the block can handle, and each exception type is separated with a vertical bar (|). In this case, the catch parameter is implicitly final. So in the example, the catch parameter ex is final and therefore you cannot assign any values to it within the catch block.

A restriction when using the multi-catch clause is that you can't use related exceptions (subclasses) in the same clause because it is redundant, the exception would already be caught by an alternative. For example, the following is invalid, since ArrayIndexOutOfBoundsException is a subclass of Exception:

try {  
  int arr[] = new int[5];  
  arr[10] = 20;  
  int r = arr[10]/10;
}  
catch(ArrayIndexOutOfBoundsException|Exception ex) { // compile-time error
  ex.printStackTrace();
}

Finally

A finally block is always executed whether an exception is handled or not. It's an optional block, and if there's one, it goes after a try or catch block. This means the following blocks are both valid:

try {
  // code that might throw an exception 
} catch(Exception e) {
  // handle exception
} finally {
  // code that it's executed no matter what
}

try {
  // code that might throw an exception 
} finally {
  // code that it's executed no matter what
}

So for each try block there can be zero or more catch blocks, but only one finally block.

The only situation where a finally block will not be executed, it's if the program exits abruptly (either by calling System.exit() or by a fatal error that causes the process to abort). Finally gets called even in the following case, before the method returns:

void m() {
  try {  
      throws Exception();  
  }  
  catch (Exception e) {   
      return failure;  
  }  
  finally {  
      System.out.println("Of course this is printed");
  }
}

 

Use Autoclose resources with a try-with-resources statement

Normally, a finally block is used to ensure that a resource is closed whether the try statement completes normally or not. For example:

BufferedReader br = new BufferedReader(new FileReader(path));
try {
    return br.readLine();
} catch(IOException e) {
    e.printStackTrace();
} finally {
    if (br != null) br.close();
}

However, Java 7 introduced the try-with-resources statement, which ensures that each resource is closed at the end of the statement. Any object that implements java.lang.AutoCloseable, which includes all objects which implement java.io.Closeable, can be used as a resource. The above example can be rewritten with a try-with-resources statement like this:

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }

But there's a subtle difference between these two try blocks. If the methods readLine() and close() both throw exceptions, then the method in the first example throws the exception thrown from the finally block (from close()) and the exception thrown from the try block (from readLine()) is suppressed. In contrast, in the try-with-resources example, if exceptions are thrown from both the try block (from readLine()) and the try-with-resources statement (from close()), then the exception from the try block is thrown and the exception thrown from the try-with-resources block is suppressed. You can retrieve these suppressed exceptions by calling the Throwable.getSuppressed() method from the exception thrown by the try block.

The Closeable interface extends the AutoCloseable interface. The close() method of the Closeable interface throws exceptions of type IOException while the close method of the AutoCloseable interface throws exceptions of type Exception.

You may declare more than one resources in a try-with-resources statement, separated by a semicolon. When the try block terminates, either normally or because of an exception, the close() methods of the resources are automatically called in the opposite order of their creation. For example:

// At the end, br2 is closed before br1.
try (BufferedReader br1 = new BufferedReader(new FileReader(path1)); 
      BufferedReader br2 = new BufferedReader(new FileReader(path2))) {
        String l1 =  br1.readLine();
        String l2 =  br2.readLine();
    }

An important note, in a try-with-resources statement, any catch or finally blocks are run after the resources declared have been closed.

 

Create custom exceptions and Auto-closeable resources

Custom exceptions

We can create our own exceptions by extending the java.lang.Exception class. For example:

class MyException extends Exception {
  public MyException(String message) {
    super(message);
  }
}
public class Test {
  public static void main(String args[]) {
    try{
            throw new MyException("My exception...");
        } catch(MyException e){
            e.printStackTrace();
        }
  }
}

You can also extend from RuntimeException, to make your exception unchecked (so it doesn't have to be catched if you don't want to).

Auto-closeable resources

You can implement the java.lang.AutoCloseable or java.lang.Closeable interfaces in your own classes and use them with a try-with-resources block. However, the close method of the Closeable interface throws exceptions of type IOException while the close method of the AutoCloseable interface throws exceptions of type Exception. So it's better to implement AutoCloseable, since you can override this behavior and throw other exceptions or no exception at all.

AutoClosable only has one method called close():

public interface AutoClosable {
    public void close() throws Exception;
}

So an implementation would look like this:

public class MyAutoClosable implements AutoCloseable {
    public void someMethod() {
       System.out.println("Doing something");
    }

    @Override
    public void close() throws Exception {
        System.out.println("Closed!");
    }
}

And this is how you'd use it:

try(MyAutoClosable ac = new MyAutoClosable()){
    ac.someMethod();
}

The output:

Doing something
Closed!

 

Test invariants by using assertions

Assertions are statements that you can use to test your assumptions about the code during development. If the assertion turns out to be false, then an AssertionError is thrown. You can use assertions in two forms:

private method(int i) {
  assert i > 0;
  //or
  assert i > 0 : "Parameter i must be a positive value"

  // Do something now that we know i is greater than 0
}

In the first form, the assert expression must evaluate to a boolean value. The other version adds a second expression separated from the first boolean expression by a colon. This expression would be used when the assertion is false in addition to throwing AssertionError. This second expression must resolve to a value, otherwise, a compile-time error is generated.

To enable assertions, you have to compile the code with the assertion enabled. Then, when running the program, assertions have to be enabled again.

Assertions were added to Java since version 1.4, so before that version, you can use assert as an identifier. This is important because in Java 7, assertions are compiled by default, but you can override this behavior at the command line:

javac -source 1.3 Test.java

The command above will issue warnings when the word assert is used as an identifier, but the code will compile and execute. But if you use any of the following commands:

javac -source 1.4 Test.java
javac -source 1.5 Test.java
javac -source 5 Test.java
javac -source 1.6 Test.java
javac -source 6 Test.java
javac -source 1.7 Test.java
javac -source 7 Test.java
javac Test.java

The compilation will fail if assert is used as an identifier.

So compilation is the first step to using assertions. But to execute the assertion checks, you have to enable them with:

java -ea com.example.Test

or

java -enableassertions com.example.Test

The default behavior is to run the code with assertions disabled, but the commands to explicitly disabling assertions are:

java -da com.example.Test

or

java -disableassertions com.example.Test

However, this command is useful in the case you want to enable assertions for some classes or packages only, for example:

java -ea -da:com.example.PrintUtil com.example.Test

To disable assertions only for the com.example.Test class. Or:

java -ea -da:com.example.util... com.example.Test

To disable assertions for the com.example.util package and all of its subpackages.

But no all uses of assertions are considered appropriate. Here are the rules:

  • Don't use assertions to validate arguments to a public method
  • Use assertions to validate arguments to a private method
  • Don't use assertions to validate command-line arguments
  • Use assertions, even in public methods, to check for cases that are never supposed to happen
  • Don't use assert expressions that can cause side effects. For example:

    private void m(List<Integer> l) {
      assert checkSize(l);
    }
    private boolean checkSize(List<Integer> l) {
      l.add(10);
      return l.size() > 3;
    }
    

 

Content