Exception Handling|Sessions 68 to 77¶

  • Java Exception Handling by Durga sir 10h
  • Create a Custom Exception in Java
  • Exception Handling in Java

What is an Exception?¶

Any unexpected events that occur during program execution are called exceptions, such as FileNotFoundException. The primary purpose of exception handling is to ensure the program terminates gracefully.

In [1]:
class Test {
    public static void main() {
        doStuff();
    }

    public static void doStuff() {
        doOtherStuff();
    }

    public static void doOtherStuff() {
        System.out.println("Hello");
    }
}

Test.main();
Hello
In [2]:
// example 1
class Test {
    public static void main() {
        doStuff();
    }

    public static void doStuff() {
        doOtherStuff();
    }

    // the method where the exception occurs is responsible for creating the exception object 
    // along with all relevant (name, description, location) information.
    public static void doOtherStuff() {
        System.out.println(10/0);
    }
}

Test.main();
---------------------------------------------------------------------------
java.lang.ArithmeticException: / by zero
	at Test.doOtherStuff(#12:14)
	at Test.doStuff(#12:8)
	at Test.main(#12:4)
	at .(#14:1)
In [3]:
// example 2
class Test {
    public static void main() {
        doStuff();
        System.out.println(10/0);
    }

    public static void doStuff() {
        doOtherStuff();
        System.out.println("Hi");
    }

    public static void doOtherStuff() {
        System.out.println("Hello");
    }
}

Test.main();
Hello
Hi
---------------------------------------------------------------------------
java.lang.ArithmeticException: / by zero
	at Test.main(#12:5)
	at .(#15:1)

Every exception occurs at runtime whether it is checked or unchecked.

Types of Exception¶

There are 2 types of exception:

  • Checked Exception
    • The exceptions which are checked by the compiler for smooth execution of program at runtime are called checked exception or compile time exception. These exceptions must be caught by try-catch or declare to be thrown by throws.
  • Unchecked Exception
    • The exceptions which are not checked by the compiler whether programmer handling it or not such exceptions are called unchecked exception or run time exception.
In [4]:
import java.io.*;

class ABC {
    public static void main() {
        // checked exception: unreported exception FileNotFoundException; must be caught or declared to be thrown
        PrintWriter pw = new PrintWriter("./abc.txt");
        pw.println("Hello");
    }
}

ABC.main();
|           PrintWriter pw = new PrintWriter("./abc.txt");
unreported exception java.io.FileNotFoundException; must be caught or declared to be thrown
In [5]:
class ABC {
    public static void main() {
        try {
            PrintWriter pw = new PrintWriter("./abc.txt");
        } catch (FileNotFoundException e) {
            System.out.println(e.getMessage());
        } 
    
        // unchecked exception
        System.out.println(10 / 0);
    }
}

ABC.main();
---------------------------------------------------------------------------
java.lang.ArithmeticException: / by zero
	at ABC.main(#16:10)
	at .(#17:1)
In [6]:
System.out.println("Statement 1");
System.out.println(10/0);
System.out.println("Statement 2");
Statement 1
---------------------------------------------------------------------------
java.lang.ArithmeticException: / by zero
	at .(#19:1)
In [7]:
System.out.println("Statement 1");
try {
    System.out.println(10/0);
} catch (ArithmeticException e) {
    System.out.println(e.toString());
}
System.out.println("Statement 2");
Statement 1
java.lang.ArithmeticException: / by zero
Statement 2

Print the Exception Information¶

These methods are available in Throwable class.

  • e.getMessage()
  • e.toString()
  • e.printStackTrace()
// worst practice
try {

} catch (Exception e) {

}

// best practice
try {

} catch (ArithemeticException e) {

} catch (IOException e) {
    
} catch (FileNotFoundException e) {

} catch (Exception e) {
    
}
In [8]:
try {
    System.out.println(10/0);
} catch (ArithmeticException e) {
    System.out.println(e.getMessage());
}
/ by zero
In [9]:
try {
    System.out.println(10/0);
} catch (ArithmeticException e) {
    System.out.println(e); // e.toString()
}
java.lang.ArithmeticException: / by zero
In [10]:
try {
    System.out.println(10/0);
} catch (ArithmeticException e) {
    e.printStackTrace();
}
java.lang.ArithmeticException: / by zero
	at REPL.$JShell$25.do_it$($JShell$25.java:15)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at io.github.spencerpark.ijava.execution.IJavaExecutionControl.lambda$execute$1(IJavaExecutionControl.java:95)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
	at java.base/java.lang.Thread.run(Thread.java:1583)

Loopholes¶

  • Order of catch blocks are important
    • First child blocks, at the end parent block
In [11]:
try {
    System.out.println(10/0);
} catch (Exception e) {
    System.out.println(e.getMessage());
} catch (ArithmeticException e) {
    System.out.println(e.getMessage());
}
|   } catch (ArithmeticException e) {
|       System.out.println(e.getMessage());
|   }
exception java.lang.ArithmeticException has already been caught
In [12]:
// best practice
try {
    System.out.println(10/0);
} catch (ArithmeticException e) {
    System.out.println(e);
} catch (Exception e) {
    System.out.println(e.getMessage());
}
java.lang.ArithmeticException: / by zero

try-catch-finally block¶

try {}
catch () {}

try {}
catch (X e) {}
catch (Y e) {}

// invalid
try {}
catch (X e) {}
catch (X e) {}

try {}
finally {}

try {}
catch (X e) {}
try {}
finally {}

try {}
catch (X e) {}
try {}
catch (Y e) {}

// Error: try without catch/finally
try {}

// Error: catch without try
catch (Y e) {}

// Error: finally without try
finally {}

// Error: catch without try
try {}
finally {}
catch (A e) {}

// Error: try without finally/catch
// Error: catch without try
try {}
System.out.println("Hello");
catch (A e) {}
---
try {
    try {
        
    } catch (X e) {}
} catch (X e) {}
---
try {
    try {}
    finally {}
} catch (X e) {}
---
try {
    
} catch (X e) {
    
} finally {
    try {
        
    } catch (A e) {
        
    }
}

throw keyword¶

  • The throw keyword is primarily used to raise custom exceptions.
In [13]:
try {
	throw new ArithmeticException("Divison by 0 is not possible");
} catch (ArithmeticException e) {
	System.out.println(e.getMessage());
}
Divison by 0 is not possible
In [14]:
// example 1
class Test {
    static ArithmeticException e = new ArithmeticException("Division by 0");

    public static void main() {
        throw e;
    }
}

Test.main();
---------------------------------------------------------------------------
java.lang.ArithmeticException: Division by 0
	at Test.<clinit>(#12:3)
	at .(#29:1)
In [15]:
class Test {
    static ArithmeticException e; // e points to NULL

    public static void main() {
        throw e;
    }
}

Test.main();
---------------------------------------------------------------------------
java.lang.NullPointerException: Cannot throw exception because "REPL.$JShell$12E$Test.e" is null
	at Test.main(#12:5)
	at .(#30:1)
In [16]:
// example 2
System.out.println(10/0);
System.out.println("Hello");
---------------------------------------------------------------------------
java.lang.ArithmeticException: / by zero
	at .(#31:2)
In [17]:
class Test {
    public static void main() {
        throw new ArithmeticException("Division by 0"); // used throw to explicitly raise error, so program stops here
        System.out.println("Hello"); // error: unreachable statement
    }
}

Test.main();
|           System.out.println("Hello"); // error: unreachable statement
unreachable statement
In [18]:
// example 3
class Test {
    public static void main() {
        throw new Test(); // you can use throw only throwable objects
    }
}

Test.main();
|           throw new Test(); // you can use throw only throwable objects
incompatible types: Test cannot be converted to java.lang.Throwable
In [19]:
class Test extends RuntimeException { // Exception in thread "main" Test
    public static void main() {
        throw new Test(); 
    }
}

throws Keyword¶

  • The throws keyword is primarily used with checked exceptions to delegate responsibility to the caller.
In [20]:
class Main {
    public static void main() {
        PrintWriter pw = new PrintWriter("abc.txt");
        pw.println("Hello");
    }
}

Main.main();
|           PrintWriter pw = new PrintWriter("abc.txt");
unreported exception java.io.FileNotFoundException; must be caught or declared to be thrown
In [21]:
class Test {
    public static void main() {
        Thread.sleep(1000);
    }
}

Test.main();
|           Thread.sleep(1000);
unreported exception java.lang.InterruptedException; must be caught or declared to be thrown

To solve the unreported exception problem in the above code, there are two approaches:

  • Using the throws keyword
  • Using a try-catch blockt.
In [22]:
class Test {
    public static void main() throws InterruptedException{
        Thread.sleep(1000);
    }
}

Test.main();
In [23]:
class Test {
    public static void main() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println(e);
        }
    }
}

Test.main();
In [24]:
class Test {
    public static void main() {
        try {
            doStuff();
        } catch (InterruptedException e) {
            System.out.println(e);
        }
    }

    public static void doStuff() throws InterruptedException {
        doOtherStuff();
    }

    public static void doOtherStuff() throws InterruptedException {
        Thread.sleep(100);
    }
}

Test.main();
In [25]:
class Test {
    public static void main() {
        try {
            doStuff();
        } catch (InterruptedException e) {
            System.out.println(e);
        }
    }

    public static void doStuff() {
        doOtherStuff();
    }

    public static void doOtherStuff() {
        Thread.sleep(100);
    }
}

Test.main();
|           } catch (InterruptedException e) {
|               System.out.println(e);
|           }
exception java.lang.InterruptedException is never thrown in body of corresponding try statement

|           Thread.sleep(100);
unreported exception java.lang.InterruptedException; must be caught or declared to be thrown
In [26]:
// Example 1
// throws keyword is only valid for method and constructor, not class

class Test throws Exception {
    public static void test() throws Exception {}
    public static void main() throws Exception {}
}
|   class Test throws Exception {
'{' expected
In [27]:
// Example 2

class Test extends RuntimeException {
    public static void main() throws Test {}
}

Test.main();
In [28]:
class Test {
    public static void main() throws Test {}
}
|       public static void main() throws Test {}
incompatible types: Test cannot be converted to java.lang.Throwable
In [29]:
// Example 3

class Test {
    public static void main() {
        throw new Exception(); // checked exception
    }
}
|           throw new Exception(); // checked exception
unreported exception java.lang.Exception; must be caught or declared to be thrown
In [30]:
// Exception in thread "main" java.lang.Error
class Test {
    public static void main(){
        throw new Error(); // unchecked exception
    }
}

Test.main();
---------------------------------------------------------------------------
java.lang.Error: null
	at Test.main(#12:4)
	at .(#38:1)

Checked Exceptions (Fully Checked)¶

Fully checked exceptions are exceptions that must be either:

  • Handled in a try-catch block, or
  • Declared in the method signature using throws.

These exceptions are part of the Exception class but do not extend RuntimeException.

  • IOException
  • SQLException
  • ClassNotFoundException
  • FileNotFoundException
  • InterruptedException

Must be handled either by a try-catch block or by declaring throws in the method signature.

Unchecked Exceptions (Runtime Exceptions)¶

  • NullPointerException
  • ArrayIndexOutOfBoundsException
  • ArithmeticException
  • ClassCastException

Partially Checked Exceptions¶

Partial checked exceptions could refer to certain exceptions that technically can be thrown during normal application flow but are not strictly enforced by the compiler to be either caught or declared in the method signature.

  • IllegalArgumentException
  • IllegalStateException

Errors¶

  • OutOfMemoryError
  • StackOverflowError
  • VirtualMachineError
In [31]:
class Test {
    public static void main() {
        try {
            System.out.println("Hello");
        } catch (ArithmeticException e) { // unchecked
             System.out.println(e);
        }
    }
}

Test.main();
Hello
In [32]:
class Test {
    public static void main() {
        try {
            System.out.println("Hello");
        } catch (Exception e) { // partial checked
             System.out.println(e);
        }
    }
}

Test.main();
Hello
In [33]:
import java.io.*;

class Test {
    public static void main() {
        try {
            System.out.println("Hello");
        } catch (IOException e) { // fully checked
             System.out.println(e);
        }
    }
}

Test.main();
|           } catch (IOException e) { // fully checked
|                System.out.println(e);
|           }
exception java.io.IOException is never thrown in body of corresponding try statement
In [34]:
// InterruptedException is a fully checked exception and must be either caught or declared

class Test {
    public static void main() {
        try {
            System.out.println("Hello");
        } catch (InterruptedException e) { // fully checked
             System.out.println(e);
        }
    }
}

Test.main();
|           } catch (InterruptedException e) { // fully checked
|                System.out.println(e);
|           }
exception java.lang.InterruptedException is never thrown in body of corresponding try statement
In [35]:
class Test {
    public static void main() {
        try {
            System.out.println("Hello");
        } catch (Error e) { // unchecked
             System.out.println(e);
        }
    }
}

Test.main();
Hello

Keywords in Exception Handling¶

  • try: Used to enclose risky code that might throw exceptions.
  • catch: Used to handle exceptions that occur within the try block.
  • finally: Used to execute cleanup code, regardless of whether an exception was thrown or not.
  • throw: Used to explicitly hand over a custom exception object to the JVM.
  • throws: Used to delegate the responsibility of exception handling to the caller of a method.

Possible Compile-Time Exception Messages¶

  • Unreported Exception X; must be caught or declared to be thrown.
  • Exception X has already been caught.
  • Exception X is never thrown in the body of the corresponding try statement.
  • Unreachable Statement
  • Incompatible Types
    found: X  
    required `java.lang.Throwable`
    
  • try without catch or finally
  • catch without try
  • finally without try

Custome Exception¶

  • Custom exceptions are user-defined exceptions that extend the Exception or RuntimeException class.
In [36]:
class TooYoungException extends RuntimeException {
    TooYoungException(String msg) {
        super(msg);
    }
}
In [37]:
class TooOldException extends RuntimeException {
    TooOldException(String msg) {
        super(msg);
    }
}
In [38]:
import java.util.Scanner;

class CastException {
    public static void eligible(int age) {
        // int age = Integer.parseInt(args[0]);
        // Scanner scanner = new Scanner(System.in);
        // System.out.println("Enter your age: ");
        // int age = scanner.nextInt();
        // scanner.close();

        if (age > 60) {
            throw new TooYoungException("Please wait some more time, definitely you will get best matched");
        } else if (age < 18) {
            throw new TooOldException("Your age is already crossed for marriage age, no chance of getting married.");
        } else {
            System.out.println("You'll get matched details soon by email");
        }
    }
}

CastException.eligible(18);
You'll get matched details soon by email
In [39]:
CastException.eligible(65);
---------------------------------------------------------------------------
REPL.$JShell$42$TooYoungException: Please wait some more time, definitely you will get best matched
	at CastException.eligible(#45:1)
	at .(#47:1)
In [40]:
CastException.eligible(15);
---------------------------------------------------------------------------
REPL.$JShell$43$TooOldException: Your age is already crossed for marriage age, no chance of getting married.
	at CastException.eligible(#45:1)
	at .(#48:1)
  • Common Java Exceptions
  • Checked and Unchecked Exceptions in Java

Top 10 Java Exceptions¶

NoClassDefFoundError¶

  • When JVM unable to find the .class file, it will raise the error.
  • ClassNotFoundException vs NoClassDefFoundError
  • It is child of RuntimeException hence it is unchecked

ArrayIndexOutOfBoundsException¶

  • Java ArrayIndexOutOfBoundsException
  • It is child of RuntimeException hence it is unchecked
  • If a piece of code tries to access an illegal index of an array, the respective method throws an ArrayIndexOutOfBoundException.
In [41]:
int[] x = new int[] {1, 2, 3};

System.out.println(x[0]);
System.out.println(x[10]);
1
---------------------------------------------------------------------------
java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 3
	at .(#51:1)

NullPointerException¶

  • Helpful NullPointerExceptions in Java
  • It is child of RuntimeException hence it is unchecked
  • If an application attempts to use null where it actually requires an object instance, the method will throw a NullPointerException.
In [42]:
String s = null;
s.length();
---------------------------------------------------------------------------
java.lang.NullPointerException: Cannot invoke "String.length()" because "REPL.$JShell$52.s" is null
	at .(#53:1)

ClassCastException¶

  • Explanation of ClassCastException in Java
  • It is child of RuntimeException hence it is unchecked
  • At runtime, if the code attempts to downcast an object to a subtype of which it isn’t an instance, the method throws a ClassCastException.
In [43]:
// child class can cast to parent class
String str = new String("Hello");
Object o = (Object) str;
In [44]:
// not vice versa
Object o = new Object();
String str = (Object) o;
|   String str = (Object) o;
incompatible types: java.lang.Object cannot be converted to java.lang.String
In [45]:
// valid
Object o = new String("Hello");
String s = (String) o;

StackOverflowError¶

  • It is child of RuntimeException hence it is unchecked
  • The StackOverflowError in Java
In [46]:
class Test {
    public static void m1() {
        m2();
    }

    public static void m2() {
        m1();
    }

    public static void main() {
        m1();
    }
}

// Test.main();

ExceptionInInitializerError¶

  • It is child of RuntimeException hence it is unchecked
  • When Does Java Throw the ExceptionInInitializerError?
In [47]:
class Test {
    public static int x = 10 / 0;
}

Test.x;
---------------------------------------------------------------------------
java.lang.ExceptionInInitializerError: null
	at .(#56:1)
In [48]:
class Test {
    static {
        String s = null;
        System.out.println(s.length());
    }
}

Test t = new Test();
---------------------------------------------------------------------------
java.lang.ExceptionInInitializerError: null
	at .(#57:1)

IllegalArgumentException¶

  • It is child of RuntimeException hence it is unchecked
  • A method throws an IllegalArgumentException if we call it with some illegal or inappropriate arguments.
  • IllegalArgumentException or NullPointerException for a Null Parameter?
In [49]:
Thread t = new Thread();
t.setPriority(7); // range(1,10)

t.setPriority(15);
---------------------------------------------------------------------------
java.lang.IllegalArgumentException: null
	at java.base/java.lang.Thread.setPriority(Thread.java:1868)
	at .(#59:3)

NumberFormatException¶

  • Java throws NumberFormatException – an unchecked exception – when it cannot convert a String to a number type.
  • Understanding the NumberFormatException in Java
  • It is class of IllegalArgumentException.
In [50]:
int i = Integer.parseInt("10");
In [51]:
int i = Integer.parseInt("ten");
---------------------------------------------------------------------------
java.lang.NumberFormatException: For input string: "ten"
	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
	at java.base/java.lang.Integer.parseInt(Integer.java:662)
	at java.base/java.lang.Integer.parseInt(Integer.java:778)
	at .(#60:1)

IllegaStateException¶

  • IllegalStateException signals that a method’s been invoked at an illegal or inappropriate time.
In [52]:
Iterator<Integer> intListIterator = new ArrayList<>().iterator(); 
intListIterator.remove();
|   Iterator<Integer> intListIterator = new ArrayList<>().iterator();
incompatible types: java.util.Iterator<java.lang.Object> cannot be converted to java.util.Iterator<java.lang.Integer>

AssertionError¶

  • It is child of Error hence it is unchecked.
  • An AssertionError is thrown when a Java program encounters a failed assertion
In [53]:
// by default, assertions are disabled in Java. To enable assertions, 
// you need to use the `-ea` flag when running the Java program.

// javac Main.java
// java -ea Main
class Main {
    public static void main() {
        int num = 5;
        assert num > 0: "Number must be positive";
        System.out.println("Number is positive: " + num);
    }
}

Main.main();
Number is positive: 5

try with resources¶

  • Introduced in Java 1.7
  • Java provides the try-with-resources statement, which ensures that resources are closed automatically after usage, without the need to explicitly define them in the finally block.
try (FileWriter fw = new FileWriter("output.txt"); FileReader fr = new FileReader("output.txt")) {
    // reassignment here is compilation error
} catch () {
    
}

// valid since 1.7
// autocloseable resources: netwrok, database, io
try () {
    
}
In [54]:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

String filePath = "example.txt";
BufferedReader reader = null;

// 1.6
try {
    reader = new BufferedReader(new FileReader(filePath));
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    System.out.println(e);
} finally {
    if (reader != null) {
        reader.close();
    }
}
java.io.FileNotFoundException: example.txt (No such file or directory)
In [55]:
String filePath2 = "example.txt";

// 1.7
try (BufferedReader reader = new BufferedReader(new FileReader(filePath2))) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    System.out.println(e);
}
java.io.FileNotFoundException: example.txt (No such file or directory)
In [56]:
try (int x = 10/0;) {
    System.out.println(x);
}
|   try (int x = 10/0;) {
incompatible types: try-with-resources not applicable to variable type
    (int cannot be converted to java.lang.AutoCloseable)

Multi catch block¶

// there should be no relationship between the exception types (no parent-child or child-parent) 
// in a multi-catch, else a compilation error will occur.
try {
    
} catch (ChildException | Exception e) {
    
}
In [57]:
try {
    int x = 10/0;
    BufferedReader reader = new BufferedReader(new FileReader("example.txt"));
    String line = reader.readLine();
} catch (ArithmeticException | IOException e) {
    System.out.println(e);
}
java.lang.ArithmeticException: / by zero
In [58]:
try {
    String s = null;
    System.out.println(s.length());
    int x = 10/0;
} catch (ArithmeticException | NullPointerException e) {
    System.out.println(e);
}
java.lang.NullPointerException: Cannot invoke "String.length()" because "<local0>" is null
In [59]:
try {
    int x = 10 / 0;
} catch (ArithmeticException | Exception e) { // a specific exception should not be caught with a general one (Exception)
    System.out.println(e);
}
|   } catch (ArithmeticException | Exception e) { // a specific exception should not be caught with a general one (Exception)
Alternatives in a multi-catch statement cannot be related by subclassing
  Alternative java.lang.ArithmeticException is a subclass of alternative java.lang.Exception
In [60]:
// it's valid
try {
    int l = Integer.parseInt("hell");
} catch (ArithmeticException e) {
    System.out.println(e);
} catch (Exception e) {
    System.out.println(e);
}
java.lang.NumberFormatException: For input string: "hell"