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: end
Note: 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: end
This 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 Exceptionthrows 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: end
A 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 ...