Error
) - Serious problems that applications should not try to catch.RuntimeException
) - Exceptions that are not checked at compile time.Exception
, excluding RuntimeException
) - Exceptions that must be handled or declared.java.lang.Object | | java.lang.Throwable | _______________|_______________ | | \_/ \_/ java.lang.Error java.lang.Exception | ____________________|____________________ | | \_/ \_/ java.lang.RuntimeException [Other subclasses of Exception]Checked vs. Unchecked Exceptions:
throws
keyword.
These include all exceptions that extend Exception
but not RuntimeException
.
Examples: IOException
, SQLException
, ClassNotFoundException
.RuntimeException
(e.g., NullPointerException
, ArithmeticException
)Error
(e.g., OutOfMemoryError
, StackOverflowError
)package com.mtitek.exceptions; public class MyTest1 { public static void main(String[] args) { System.out.println("main: start"); try { int a = 10 / 0; // This will throw ArithmeticException System.out.println("Result: " + a); // This line won't execute } catch (ArithmeticException e) { System.out.println("Cannot divide by zero!"); e.printStackTrace(); } System.out.println("main: end"); } }Output:
main: start Cannot divide by zero! java.lang.ArithmeticException: / by zero at com.mtitek.exceptions.MyTest1.main(MyTest1.java:7) main: endNote:
ArithmeticException
is a subclass of RuntimeException
(unchecked exception),
so the compiler does not require this exception to be handled. However, handling it prevents program termination:package com.mtitek.exceptions; public class MyTest1 { public static void main(String[] args) { System.out.println("main: start"); int a = 10 / 0; // Unhandled exception will terminate the program System.out.println("main: end"); // This line will never execute } }Output:
main: start Exception in thread "main" java.lang.ArithmeticException: / by zero at com.mtitek.exceptions.MyTest1.main(MyTest1.java:7)When the JVM encounters an unhandled exception, the program terminates abruptly.
Throwable
can be caught,
it's recommended to handle only Exception
and its subclasses.
Error
types typically indicate serious JVM problems that applications cannot recover from
(e.g., OutOfMemoryError
, StackOverflowError
).try { int a = 10 / 0; } // Compiler error: "Syntax error, insert 'Finally' to complete BlockStatements"
try { // Code that might throw exceptions } catch (ArithmeticException e) { // Handle arithmetic exceptions specifically System.out.println("Arithmetic error: " + e.getMessage()); } catch (RuntimeException e) { // Handle other runtime exceptions System.out.println("Runtime error: " + e.getMessage()); } catch (Exception e) { // Handle any other exceptions System.out.println("General error: " + e.getMessage()); }
try { // ... } catch (Exception e) { // ... } catch (Exception e) { // Compiler error: Unreachable catch block // ... }
try { // ... } catch (Exception e) { // Catches all exceptions // ... } catch (ArithmeticException e) { // Compiler error: Unreachable - already caught by Exception // ... }
public static void main(String[] args) { System.out.println("main: start"); try { // outer try try { // inner try int a = 10 / 0; // First exception thrown here } catch (ArithmeticException e) { System.out.println("inner try: caught ArithmeticException"); int b = 10 / 0; // Second exception thrown here } catch (Exception e) { System.out.println("inner try: catch Exception"); // This block is skipped } } catch (Exception e) { System.out.println("outer try: caught Exception"); // Second exception caught here } System.out.println("main: end"); }Output:
main: start inner try: caught ArithmeticException outer try: caught Exception main: end
public static void main(String[] args) { System.out.println("main: start"); try { // outer try try { // inner try int a = 10 / 0; // Exception thrown here } finally { System.out.println("inner try: finally - cleanup performed"); } } catch (Exception e) { System.out.println("outer try: caught Exception"); } System.out.println("main: end"); }Output:
main: start inner try: finally - cleanup performed outer try: caught Exception main: end
public static void main(String[] args) { System.out.println("main: start"); try { // outer try try { // inner try int a = 10 / 0; // First exception thrown here } catch (ArithmeticException e) { System.out.println("inner try: caught ArithmeticException"); int b = 10 / 0; // Second exception thrown here } finally { System.out.println("inner try: finally - always executes"); } } catch (Exception e) { System.out.println("outer try: caught Exception"); } System.out.println("main: end"); }Output:
main: start inner try: caught ArithmeticException inner try: finally - always executes outer try: caught Exception main: end
package com.mtitek.exceptions; public class MyTest1 { public static void main(String[] args) { System.out.println("main: start"); try { int v1 = 10; int v2 = 0; if (v2 == 0) { throw new ArithmeticException("Warning, integer division by zero!"); } else { int v = v1 / v2; } } catch (ArithmeticException e) { e.printStackTrace(); } System.out.println("main: end"); } }Output:
main: start java.lang.ArithmeticException: Warning, integer division by zero! at test.MyTest1.main(MyTest1.java:12) main: endThis example demonstrates how the
throw
keyword is used to explicitly throw an exception when a specific condition is met. Note that ArithmeticException
is a runtime (unchecked) exception, so it doesn't need to be declared in the method signature.throws
keyword.
This allows calling methods to know about the exceptions that may be thrown, and either handle them or declare them as well.Exception
but not RuntimeException
).
Unchecked exceptions (those extending RuntimeException
) and errors (extending Error
) can be thrown without declaration.
In Java, you must either handle or declare a checked exception, otherwise the compiler will generate a compilation error.throws Exception
throws IOException, SQLException
package com.mtitek.exceptions; public class MyTest1 { public static void main(String[] args) { System.out.println("main: start"); try { int v1 = divide(10, 0); System.out.println("Result: " + v1); } catch (Exception e) { System.err.println("Caught exception: " + e.getMessage()); e.printStackTrace(); } System.out.println("main: end"); } private static int divide(int p1, int p2) throws Exception { if (p2 == 0) { throw new Exception("Division by zero is not allowed!"); } return p1 / p2; } }Output:
main: start Caught exception: Division by zero is not allowed! java.lang.Exception: Division by zero is not allowed! at test.MyTest1.divide(MyTest1.java:20) at test.MyTest1.main(MyTest1.java:8) main: endA method must either handle or declare the checked exceptions thrown by the methods it calls.
main
method handles the exception thrown by the divide
method. Alternatively, main
could declare the exception
instead of handling it:package com.mtitek.exceptions; public class MyTest1 { public static void main(String[] args) throws Exception { System.out.println("main: start"); int v1 = divide(10, 0); System.out.println("Result: " + v1); System.out.println("main: end"); } private static int divide(int p1, int p2) throws Exception { if (p2 == 0) { throw new Exception("Division by zero is not allowed!"); } return p1 / p2; } }Output:
main: start Exception in thread "main" java.lang.Exception: Division by zero is not allowed! at test.MyTest1.divide(MyTest1.java:15) at test.MyTest1.main(MyTest1.java:7)Note that the program terminates abruptly because the exception was not handled. Since the
main
method is at the top of the call stack,
the JVM stops propagating the exception and terminates the program with an error message.Throwable
hierarchy. Creating custom exceptions is useful when you want to handle specific types of errors and distinguish them from other exceptions.Exception
- for checked exceptions that must be handled or declared.RuntimeException
- for unchecked exceptions.Error
- for system-level errors (rarely used).Exception
for recoverable conditions that calling code should handle, and RuntimeException
for programming errors.public class BigNumberException extends Exception { public BigNumberException() { super("Number is too large for this operation"); } public BigNumberException(String message) { super(message); } }
public class SmallNumberException extends Exception { public SmallNumberException() { super("Number is too small for this operation"); } public SmallNumberException(String message) { super(message); } }
package com.mtitek.exceptions; public class MyTest1 { public static void main(String[] args) { System.out.println("main: start"); // Test case 1: Division by zero (unchecked exception) try { int v1 = divide(10, 0); System.out.println("Result: " + v1); } catch (BigNumberException | SmallNumberException e) { System.err.println("Custom exception: " + e.getMessage()); } catch (ArithmeticException e) { System.err.println("Arithmetic error: " + e.getMessage()); } // Test case 2: Large number (checked exception) try { int v1 = divide(100, 2); System.out.println("Result: " + v1); } catch (BigNumberException | SmallNumberException e) { System.err.println("Custom exception: " + e.getMessage()); } catch (ArithmeticException e) { System.err.println("Arithmetic error: " + e.getMessage()); } // Test case 3: Small number (checked exception) try { int v1 = divide(-10, 2); System.out.println("Result: " + v1); } catch (BigNumberException | SmallNumberException e) { System.err.println("Custom exception: " + e.getMessage()); } catch (ArithmeticException e) { System.err.println("Arithmetic error: " + e.getMessage()); } System.out.println("main: end"); } private static int divide(int p1, int p2) throws SmallNumberException, BigNumberException { if (p2 == 0) { // ArithmeticException is unchecked, so no need to declare it throw new ArithmeticException("Division by zero is not allowed!"); } else if (p1 < 0) { throw new SmallNumberException("Negative numbers not supported: " + p1); } else if (p2 > 50) { throw new BigNumberException("Number too large for operation: " + p1); } else { return p1 / p2; } } }Output:
main: start Arithmetic error: Division by zero is not allowed! Result: 50 Custom exception: Negative numbers not supported: -10 main: end
AutoCloseable
interface.
It ensures that resources are properly closed even if an exception occurs, eliminating the need for explicit finally blocks in most cases.try (ResourceType resource = new ResourceType()) { // do something here } catch (ExceptionType e) { // handle exception }You can declare multiple resources in a single try-with-resources statement by separating them with semicolons. Resources are closed in reverse order of their declaration.
try (ResourceType resource = new ResourceType(); AnotherResourceType anotherResource = new AnotherResourceType()) { // do something here } catch (ExceptionType e) { // handle exception }Note: Always use try-with-resources for
AutoCloseable
resources instead of manual resource management.package com.mtitek.exceptions; public class MyTest1 { public static void main(String[] args) { System.out.println("Using try-with-resources:"); try (MyAutoCloseableResource resource = new MyAutoCloseableResource()) { resource.doSomething(); } catch (Exception e) { e.printStackTrace(); } System.out.println("main: end"); } }
class MyAutoCloseableResource implements AutoCloseable { @Override public void close() throws Exception { System.out.println("Closing resources ...."); } public void doSomething() { System.out.println("Doing something ..."); } }Output:
Using try-with-resources: Doing something ... Closing resources .... main: endSuppressed Exceptions:
close()
method, the exception from the try block is thrown, and the exception from close()
is added as a suppressed exception.package com.mtitek.exceptions; public class MyTest1 { public static void main(String[] args) { try (MyAnotherAutoCloseableResource resource = new MyAnotherAutoCloseableResource()) { resource.doSomething(); } catch (Exception e) { System.err.println("Main exception: " + e.getMessage()); // Check for suppressed exceptions Throwable[] suppressed = e.getSuppressed(); for (Throwable t : suppressed) { System.err.println("Suppressed: " + t.getMessage()); } } } }
class MyAnotherAutoCloseableResource implements AutoCloseable { @Override public void close() throws Exception { throw new Exception("Error closing resource ..."); } public void doSomething() throws Exception { throw new Exception("Error during doing something ..."); } }Output:
Main exception: Error during doing something ... Suppressed: Error closing resource ...