Exception Handling in Java | Part-2

This is the second post on a series of Exception Handling in Java. In this article, we will learn methods to print exception info, try with multiple catch blocks, finally block and most important difference between final, finally, finalize. Before starting these, please go through Exception Handling in Java | Part-1 so that you clear with the basics of Exception Handling.



Methods to print exception information :


Thowable class defines following methods to print exception information:

    Method                      printable format

  1. printStackTrace()   -   Name of exception : Description StackTrace

  2. toString()                 -   Name of exception : Description

  3. getMessage()           -   Description

For example :

class Test{
	public static void main(String[] args){
		try{
			sop(10/0);
		}catch(AE ae){
			sop(e.getMessge())          // "/ by zero"
			sop(e.toString());	    // "java.lang.AE : / by zero"
			sop(e.printStackTrace());   // "java.lang.AE : / by zero
								  at Test.main()"	
		}
	}
}

Note : Internally default exception handler will use the printStrackTrace() to print exception information to console.


Try with multiple catch blocks


The way of handling an exception is varied from exception to exception. Hence for every exception type, it is highly recommended to take a separate catch block i.e. try with multiple catch blocks.

Worst programming practice :

try{
  //riskey code
}catch(Exception e){
			
}

Best programming practice: use multiple catch blocks

try{
  //riskey code
}catch(ArithmeticException e){
			
}catch(SQLException e e){
			
}catch(FileNotFoundException e){
			
}catch(Exception e){
  //Defaullt exception
}

Note :
  • If try with multiple catch block is present then the order of catch block is very important.

  • We have to take the child first then the parent otherwise we will get a compile-time error saying Exception xxx has already been caught.

  • We can't declare two catch blocks for the same exception otherwise we will get a compile-time error.


Difference between final, finally, finalize

final :

  • final is modifier applicable for classes, methods, and variables

  • If the class is declared as final then we can't extend that class i.e. Inheritance is not possible for final classes

  • If a method is final then we can't use that method in the child class

  • If a variable is declared as final then we can't reassignment for that variable

finally():

try{
  // riskey code
}catch(Exception e){
  // handler code			
}finally{
  // clean-up code
}

The specialty of the finally block is it will be executed always irrespective of whether an exception is raised or not, handled, or not handled.

finalize():

It is a method always invoked by garbage collector just before destroying an object to perform cleanup activities.
once the finalize method completes immediately garbage collector destroys that object.


Note : 
  • finally block is responsible to perform clean up activity related to try block i.e. whatever resources as a part of try block will be closed inside finally block whereas the finalize method is responsible to perform clean up activity related to object i.e. whatever resources associated with the object will be deallocated by using finalize method


Various possible combinations of try-catch-finally


Note :

  1. In try-catch-finally order is very important

  2. try without a catch or finally is invalid otherwise Compile Time Error: try without a catch or finally is invalid

  3. Whenever we are writing catch block compulsory try block is required... try without a catch is invalid

  4. finally without try is invalid

  5. Inside try-catch and finally block we can declare try-catch and finally block i.e. nesting of try-catch-finally is allowed

  6. For try-catch and finally block curly bases are mandatory



throw in Java


Sometimes we can create exception objects explicitly and handover to JVM manually for these we have to use the throw keyword.
For e.g. throw new AE("/ by zero")
The main objective of throw keyword is handover our created exception object to the JVM manually
Hence the result of the following two programs are the same:
The main method is responsible to create the exception object and handover to JVM:

public class Test1{
    public static void main(String[] args) {
        System.out.println(10/0);
    }
}

o/p:
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at ExceptionHandling.Test1.main(Test1.java:3)

Here Programmer creating exception object explicitly and handover to JVM manually

public class Test1{
    public static void main(String[] args) {
        throw new ArithmeticException("Divide by zero");
    }
}

o/p:
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at ExceptionHandling.Test1.main(Test1.java:3)

The best use of the throw keyword is for user-defined exceptions.
Let's see some interesting example of custom exceptions:

Case 1: throw e, if e refers null then we will get NPE

public class Test1{
    static ArithmeticException e = new ArithmeticException();
    public static void main(String[] args) {
        throw e;
    }
}

o/p:
Exception in thread "main" java.lang.ArithmeticException
	at ExceptionHandling.Test1(Test1.java:2)
public class Test1{
    static ArithmeticException e;
    public static void main(String[] args) {
        throw e;
    }
}

o/p:
Exception in thread "main" java.lang.NullPointerException
	at ExceptionHandling.Test1.main(Test1.java:4)

Case 2: After throw statement, we are not allowed to write any statement directly otherwise we will get compile-time error unreachable statement


public class Test1 {
	public static void main(String[] args) {
		throw new ArithmeticException("/ by zero");
		System.out.println("print hello");
	}
}

o/p :
 Compile Time Error:(7, 9) java: unreachable statement
public class Test1 {
	public static void main(String[] args) {
		System.out.println(10/0);
		System.out.println("print hello");
	}
}

o/p: 
Run time Exception in thread "main" java.lang.ArithmeticException: / by zero
	at ExceptionHandling.Test1.main(Test1.java:5)

Case 3: We can use the throw keyword only for throwable type if we are trying for a normal java object then we will get a compile-time error saying incompatible types.


public class Test1 {
    public static void main(String[] args) {
        throw new Test1();
    }
}

o/p:
 Error:(5, 9) java: incompatible types: ExceptionHandling.Test1 cannot be converted to java.lang.Throwable
public class Test1 extends ArithmeticException{
    public static void main(String[] args) {
        throw new Test1();
    }
}

o/p:
Exception in thread "main" ExceptionHandling.Test1
	at ExceptionHandling.Test1.main(Test1.java:5)

throws in Java


In our program if there is a possibility of raising checked exception then compulsory we should handle it, otherwise we will get a compile-time error: Unreported exception java.io.FileNotFoundException; must be caught or declared to be thrown

e.g.
public class Test1{
    public static void main(String[] args) {
        PrintWriter pw = new PrintWriter("abc.txt");
        pw.write("a");
    }
}
o/p:
 Unreported exception java.io.FileNotFoundException; must be caught or declared to be thrown

We can handle this compile-time error by using two ways:

  1. By using try-catch

  2. By using throws

public class Test1{
    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(1000);
    }
}

Note:

  • We can use the throws keyword to delegate the responsibility of exception handling to the caller(JVM/method) then the caller is responsible to handle the exception

  • It is used for only checked exceptions

  • Usage of throws keyword doesn't prevent abnormal termination of a program


public class Test1{
	public static void main(String[] args) throws InterruptedException {
        doMoreStuff();
    }
    public void doMoreStuff() throws InterruptedException {
        doStuff();
    }
	public void doStuff() throws InterruptedException {
        Thread.sleep(1000);
    }
}

In the above program, if we removed at least one throws statement then our program won't compile

Use of throws

  • We can use the throws keyword for method and constructor but not for classes.
    Class Test1 throws Exception - Invalid
    Test1() throws Exception - Valid
    public static void main(String[] args)throws Exception { - valid

  • We can use throws keyword only for throwable types otherwise we will get a compile-time error - incompatible type

  • Within try block if there is no chance of raising an exception, we can't write a catch block for that exception otherwise we will get a compile-time error saying Exception xxx is never thrown in body of corresponding try statement, but this rule is applicable for only fully checked exceptions

  • public class Test1{
        public static void main(String[] args){
            try {
                Thread.sleep(1000);
            }catch (SQLException e){
                System.out.println("sql exception");
            }
        }
    }
        
    o/p:
       Error:(5, 6) java: exception java.sql.SQLException is never thrown in body of corresponding try statement
       Error:(4, 25) java: unreported exception java.lang.InterruptedException; must be caught or declared to be thrown
      

Lets quick summarise what we learn in this post:

  • Methods to print exception information

  • How to use try with multiple catch blocks

  • Difference between final, finally, finalize

  • Various possible combinations of try-catch-finally

  • throw keyword

  • throws keyword

With this, we also came to the end of this article. In the next article, we would be revising and learning Exception handling keywords summary, Various possible compile-time errors in exception handling, Customized or user-defined exceptions, etc.. Please let us know about your views and opinions in the comment section below. I hope you enjoyed these posts. Please stay with us for more posts.
For more details on exception handling please refer Video

0 comments:

Post a Comment