Exception and Errors

  • Both are subclasses of Throwable class
  • Throwable
    • Exception
    • Error
classDiagram
Throwable <|-- Exception
Throwable <|-- Error
Exception <|-- IOException
Exception <|-- RuntimeException
RuntimeException <|-- NullPointerException
Error <|-- IOError
Error <|-- VirtualMachineError
VirtualMachineError <|-- OutofMemoryError
VirtualMachineError <|-- StackOverflowError

Classification

  • Errors
    • Unchecked (runtime)
  • Exception
    • Checked (compile time)
    • Unchecked (runtime)

Exception

  • These are problems that occur at runtime and compile time.
  • it mainly occurs in the code written by the developers.

Checked

  • Checked at compile time.
  • All exceptions which inherit from Exception class (including itself) except RuntimeException are checked exception.
  • For checked exception you need to specify throws beside method signature if you don’t handle it.
  • If you call a method which has a checked exception specified or throw a checked exception then you must handle it or you need to specify throws keyword beside method signature if you don’t handle it.
  • Exception
    • IOException
    • SQLException
    • DataFormatException
public class Cat {
	public void dance() throws SQLException {  
		throw new SQLException();  
	}
}
 
public class Zoo {
	/**
     * If you don't handle checked exceptions then you have to specify throws clause
     */
    public static void danceCat(Cat cat) {
        try {
            cat.dance();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

Unchecked

  • Occur at runtime
  • All exceptions that inherit from RuntimeException (including itself) are unchecked.
  • For unchecked exception it is not required to specify throws keyword.
  • Exception:
    • RuntimeException
      • NullPointerException — calling an action on null object
      • ArithmeticException — invalid math operation like diving by zero
      • DateTimeException — problem while calculating a date-time

Custom Exceptions

  • For Checked Exceptions you can extend Exception class
  • For Unchecked Exceptions you can extend RuntimeException class (which internally extends Exception)
public class FibonacciInputException extends Exception {
    public FibonacciInputException(String message) {
        super(message);
    }
}    

Error

  • It indicates serious problems that a reasonable application should not try to catch.
  • Most such errors are abnormal conditions like lack of system resources.
  • Error
    • IOError — Serious issue with underlying filesystem
    • AssertionError
    • VirtualMachineError — indicates JVM running out of resources
      • InternalError
      • UnknownError
      • OutOfMemoryError
      • StackOverflowError
  • It is possible to throw error but it is not recommended
 try {
    throw new Error();
} catch (Throwable ex) {
    // you need to catch via "Throwable" superclass
    System.out.println("found the error");
}

try-catch-finally

  • The finally block executes regardless of whether an exception is thrown or caught.
  • We generally use the finally block to execute clean up code like closing connections, closing files, or freeing up threads, as it executes regardless of an exception.
  • There can only be one finally but there can be multiple catch blocks.
  • No matter whether exception is handled or not, or return statement is present or not, finally will always execute.
  • return statement in finally block will always override try or catch’s return statements if present.
int fun() {
	try {
		return 0;
	} finally {
		System.out.println("Executing finally");
	}
}
 
System.out.println(fun());
//--Output--
// Executing finally
// 0
 
int fun2() {  
    try {  
        throw new RuntimeException();  
    } finally {  
        System.out.println("finally executes");  
    }  
}
 
System.out.println(fun2());
//--Output--
// finally executes
// Exception in thread "main" java.lang.RuntimeException 
 
int fun3() {
	try {
		return 0;
	} finally {
	    System.out.println("finally executes");
	    return 50; // overrides try's return statement  
	}
}
 
System.out.println(fun3());
//--Output--
// finally executes
// 50
 
int fun4() {  
	try {  
		System.out.println("Executing try");  
		throw new RuntimeException();  
//            return 10;  //unreachable
	} catch (Exception e) {  
		System.out.println("Executing catch");  
		return 20;  // Not executed
	} finally {  
		System.out.println("Executing finally");  
		return 30;  // Executed
	}  
}
 
System.out.println(fun4());
//--Output--
// Executing finally
// 0
 
  • There is one condition in which finally is not executed, it is when System.exit() is called in try or catch block.
int fun() {
	try {
		throw new RuntimeException(); 
	} catch(Execution e) {
		System.out.println("Executing catch");
		System.exit(0);
	} finally {
		System.out.println("Executing finally");
	}
}
 
System.out.println(fun());
//--Output--
// Executing catch

catch

  • The exception thrown from try block try to find the first match in multiple catch block and executes it and directly goes to the finally block.
  • It is recommended to write specific exceptions before generic one when specifying multiple catch blocks.
  • A match is said to happen from thrown exception to the catch block exception when former can be assigned to the latter implicitly.
void fun(boolean flag) {
	try {
		if(flag) {
			throw new NullPointerException();
		} else {
			throw new Exception();
		}
	} catch(NullPointerException e) {
		System.out.println("NullPointerException caught");
	} catch (Exception e) {
		System.out.println("Exception caught");
	}
}
 
fun(true);
//--Output--
// NullPointerException caught
fun(false);
//--Output--
// Exception caught

Multi catching exception

  • Using pipe(|) symbol we can catch more than one type of exception with single catch block.
try {
	// ....
} catch(IOException | SQLException e) {
	// ....
}