Tuesday, January 20, 2015

Keynote: Exceptions in Action

Preamble

The java.lang.Throwable class is the superclass of all errors and exceptions in the Java language.
It means that you can throw and catch only objects that inherit this class.

Catch or Specify Requirement

If you invoke some method that throws some Checked exception, you have to wrap it into try catch or add it to the method declaration! Otherwise - Compile Error!

*Checked exceptions - the class Exception and any subclasses that are not also subclasses of RuntimeException
  • So, Exception and its subclasses(checked exceptions), except RuntimeException, are under Catch or Specify Requirement!
  • Error, RuntimeException(unchecked exception) and their descendants are not under Catch or Specify Requirement!
If the runtime system exhaustively searches all the methods on the call stack
without finding an appropriate exception handler, the runtime system (and, consequently, the program) terminates!


So, Exception can be specified even in main method and forwarded out of application.
VM will exit in this case, if such exception happens.

U can specify multiple exceptions thrown by method:
public void doSomething() throws ParseException, DataFormatException
However, usually there no sense to do it if one exception is subclass of another:
public void doSomething() throws Exception

Try Catch block

After each try block must follow either one or more catch blocks or finally or both! Otherwise - Compile Error!

*If there only try block & finally specified (without any catch blocks), then your Exception is not mentioned as handled! So, u've to specify it in the method declaration.
  • A try statement hasn't to have a catch block if it has a finally block.
  • None of the try, catch, and finally blocks can exist independently!
  • There can be none or only one finally block after the try one, and it can be located only after the catch blocks if such ones defined!
U have to handle exception from Top(Subclasses) to Bottom(Base classes).,
Otherwise - Compile Error, cause the first catch block will always handle all of your exceptions with the specified base class!

public void doSomethingRight {
    try {/**code that throws exception**/}
    catch (Exception e) {/**some code**/}
    catch (ArithmeticException e) {/**some code**/}  // "Compile-Error: exception java.util.zip.ArithmeticException has already been caught"
}

public void doSomethingWrong {
    try {/**code that throws exception**/}
    catch (ArithmeticException e) {/**some code**/}
    catch (Exception e) {/**some code**/}           // valid: will compile fine
}

Multi-catch (Since JDK7)

Since Java SE 7, a single catch block can handle more than one type of exceptions - it's called multi-catch

Types in multi-catch should be disjoined(one shouldn't be base class of the other)! Otherwise - Compile Error!

public void doSomethingRight {
    try {/**code that throws few exceptions**/} 
    catch (DataFormatException | ParseException e) {/**some code**/}  // valid: will compile fine 
}

public void doSomethingWrong {
    try {/**code that throws few exceptions**/}
    catch (DataFormatException | Exception e) {/**some code**/}  // "Compile-Error: Types in multi-catch must be disjoint:..."
}

In the multi-catch, the catch parameter e is implicitly final, therefore you can't assign any values to it within the catch block!

Finally block

The finally block always(even if there was no any exception!) executes when the try catch block exits!

*Note: finally block may not execute if JVM exited or thread was interrupted while try catch code.
  • finally block executes regardless was there any exception or not!
  • finally executes regardless was there any return in try or catch or not!
  • finally block can't be located before the the catch block.
  • If both catch and finally blocks define return statements, the calling method will receive the value from the finally block.

public int tryFinallyReturnPriorCatchReturn() {
    int x = 0;
    try {
        throw new Exception("Low Level Exception");
    }
    catch (Exception e) {
        System.out.println("Catch statement x=" + x); // --> Catch statement x=0
        return x;
    }
    finally {
        x++;
        System.out.println("Finally statement x=" + x); // --> Finally statement x=1
        return x;      // 1 will be returned
    }
}
...
public int tryFinallyInvocationPriorCatchReturn() {
    int x = 0;
    try {
        throw new Exception("Low Level Exception");
    }
    catch (Exception e) {
        System.out.println("Catch statement x=" + x); // --> Catch statement x=0
        return x;     // 0 will be returned regardless increment happened in finally
    }
    finally {
        x++;
        System.out.println("Finally statement x=" + x); // --> Finally statement x=1        
    }
}
...

Exceptions in Overriding methods

A base or overriding method is free to throw any Error or RuntimeException.
If base method doesn't have any throws clause, then its overriding method cannot declare any Checked exception as well!
overriding method only needs to specify a subset of the list of Checked exception classes the overridden(base) method can throw!

class A {
    protected void doSomething() throws Exception { 
        throw new Exception("Checked Exception!");
    }
}
class B extends A {
    /**cause super method invocation throws checked exception it should be cached or specified in overriding one**/
    protected void doSomething() throws Exception { 
        super.throwHandledException();
    }
// or (assume that it exists independently)
    /**no need to specify en exception of super method cause it doesn't invoked**/
    protected void doSomething() {}
}

Even if your sub method doesn't invoke it's super method u can't specify the base class of its exception class into your sub method!

class A {
    protected void doSomething() throws IOException { 
        throw new IOException("Specified IO Exception!");
    }
}
class B extends A {
    protected void doSomething() throws FileNotFoundException {     // valid.
        throw FileNotFoundException("Particular File not found!");
    }
// but (assume that it exists independently)
    protected void doSomething() throws Exception {} // compile-error
}

Try-With-Resources (Since JDK7)

The try-with-resources statement ensures that each resource is closed at the end of the statement.
Otherwords, in try-with-resources statement any catch or finally block is run after the resources declared have been closed.

*Resource - is an object that must be closed after the program is finished with it.
  • Within try-with-resources u can use multiple resources - all of those will be closed when no longer needed.
  • Use the try-with-resources statement in situations, which automatically releases system resources when no longer needed.
  • You can use try-with-resources to ensure that a resources are closed regardless of whether the try statement completes normally or abruptly.
  • Each resource should implement AutoCloseable and Closeable(subinterface of AutoCloseable)

private void readWithout() { 
    // regular approach
    try { 
        FileInputStream input = new FileInputStream("file.dat");
        int c;
        while ((c = in.read()) != -1) {
            System.out.println(byteValue + " ");
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (input != null)
        {
            try {            
                input.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
private void readWith() { 
    // try-with-resources approach
    try (FileInputStream input = new FileInputStream("file.dat")) {
 int c;
        while ((c = in.read()) != -1) {
            System.out.println(byteValue + " ");
        }
    } catch (IOException) {
        e.printStackTrace();
    }
}

Miscelaneous

  • Checked Exceptions - are subject of Catch or Specify Requirement, that app should anticipate and recover from.
  • Unchecked Exceptions(RunTimeException) - are not subject of Catch or Specify Requirement: internal exceptional conditions, that the app usually cannot anticipate and recover from.
  • Errors(Error) - are not subject of Catch or Specify requirement: external exceptional conditions, that the app usually cannot anticipate and recover from.
NullPointerException - happens in runtime during an attempt to access to member field of not initialized (null) object. App might choose to catch those, but it probably makes sense to eliminate the bug that caused those exception to occur.

public void doSomething(int [] arr) { 
    int x = arr.length;    // Nullpointer will be thrown, if u'll call "doSomething(null);"
}

ClassCastException - will be thrown in runtime if it's not possible to cast one object to another, which are from the same inheritance tree.
Be aware: Compile-Error will happen if u'll trying to cast incompatible objects:

class A {}
class B extends A {}
class C extends A {}
...
public static void main (String[] args) {
    A a = new A();
    B b = new B();
    C c = new C();

    b = (B) a    //--> ClassCastException in runtime
    b = (C) c    //--> Compile-error
}

ExceptionInInitializerError - the program ends with such one because any exception thrown in a static block is wrapped into ExceptionInInitializerError before it's thrown.
StackOverflowError - will be thrown if an application recurses too deeply e.g. infinite loop or recursion.
...
@see Oracle Java tutorials: Exceptions.
@see Java SE8 API: Exception

No comments:

Post a Comment