In Java, exceptions represent unexpected events or errors that occur during program execution, such as trying to divide by zero or accessing a file that doesn’t exist. To gracefully handle these situations without crashing, Java provides the try-catch-finally
mechanism, allowing you to catch and manage errors while maintaining program control.
try
BlockThe try
block contains code that might throw an exception. It’s the “risky” section where something could go wrong.
try {
int result = 10 / 0; // This will throw ArithmeticException
}
If an exception occurs inside the try
block, Java immediately stops executing that block and looks for an appropriate catch
block to handle it.
catch
BlockThe catch
block handles exceptions thrown in the corresponding try
block. You can specify the type of exception you want to catch.
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("Cannot divide by zero!");
}
You can also catch multiple exception types by adding multiple catch
blocks:
try {
String text = null;
System.out.println(text.length()); // Throws NullPointerException
} catch (ArithmeticException e) {
System.out.println("Math error: " + e.getMessage());
} catch (NullPointerException e) {
System.out.println("Null reference detected!");
}
finally
BlockThe finally
block contains code that always runs after the try
and catch
blocks, regardless of whether an exception occurred or not. It’s commonly used for cleaning up resources like closing files or database connections.
try {
System.out.println("Trying risky operation");
} catch (Exception e) {
System.out.println("Exception handled");
} finally {
System.out.println("This runs no matter what");
}
Even if you return from the try
or catch
block, the finally
block will still execute.
You can nest try-catch
blocks inside one another to handle exceptions at different granularities:
try {
try {
int[] numbers = {1, 2};
System.out.println(numbers[5]); // Throws ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Inner catch: Invalid index");
}
int a = 10 / 0; // Throws ArithmeticException
} catch (ArithmeticException e) {
System.out.println("Outer catch: Division by zero");
}
public class Main {
public static void main(String[] args) {
try {
try {
int[] numbers = {1, 2};
System.out.println(numbers[5]); // Throws ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Inner catch: Invalid index");
}
int a = 10 / 0; // Throws ArithmeticException
} catch (ArithmeticException e) {
System.out.println("Outer catch: Division by zero");
}
}
}
// Bad: silently ignores exceptions
catch (Exception e) {
// nothing here
}
e.printStackTrace()
or logging frameworks to record errors for diagnosis.catch (Exception e) {
e.printStackTrace();
}
Catch specific exceptions: Avoid catching generic Exception
unless necessary to handle unexpected errors.
Clean up resources: Always use finally
(or try-with-resources in newer Java) to close resources like files or connections.
import java.io.*;
public class ExceptionDemo {
public static void main(String[] args) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("file.txt"));
String line = reader.readLine();
System.out.println(line);
} catch (FileNotFoundException e) {
System.out.println("File not found!");
} catch (IOException e) {
System.out.println("I/O error occurred");
} finally {
try {
if (reader != null) {
reader.close();
System.out.println("Reader closed");
}
} catch (IOException e) {
System.out.println("Failed to close reader");
}
}
}
}
This example opens a file, reads a line, and handles possible exceptions while ensuring the file resource is closed in the finally
block.
try
block contains code that may throw exceptions.catch
blocks handle specific exceptions thrown in the try
block.finally
block always executes, used mainly for cleanup.Understanding and properly using try-catch-finally
is essential for writing reliable Java programs that can gracefully recover from errors without crashing.
In Java, you don’t just wait for exceptions to happen—you can explicitly throw them yourself using the throw
keyword. This allows you to enforce rules, validate inputs, and signal problems in your code clearly. In this section, we’ll explore how to throw exceptions, catch them, and keep your program stable.
throw
The throw
statement lets you create and throw an exception manually. For example, if a method receives an invalid argument, you can throw an IllegalArgumentException
:
public void setAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative");
}
this.age = age;
}
When throw
is executed, the normal program flow stops, and Java looks for a matching catch
block.
IOException
) must be declared in the method signature with throws
and handled by the caller.IllegalArgumentException
or NullPointerException
) do not require declaration or mandatory handling.In the example above, IllegalArgumentException
is an unchecked exception, so you don’t have to declare it in the method signature.
To handle exceptions thrown by your code or others, use a try-catch
block:
try {
setAge(-5);
} catch (IllegalArgumentException e) {
System.out.println("Error: " + e.getMessage());
}
This catches the exception thrown by setAge()
and prints an error message, preventing the program from crashing.
import java.util.Scanner;
public class AgeValidator {
public static void setAge(int age) {
if (age < 0 || age > 150) {
throw new IllegalArgumentException("Invalid age: " + age);
}
System.out.println("Age set to " + age);
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter your age: ");
int inputAge = scanner.nextInt();
try {
setAge(inputAge);
} catch (IllegalArgumentException e) {
System.out.println("Caught exception: " + e.getMessage());
System.out.println("Please enter a valid age between 0 and 150.");
}
scanner.close();
}
}
Explanation:
setAge
method validates the age.IllegalArgumentException
.main
method catches this exception and displays a friendly message.throw
to explicitly signal that an error has occurred.IllegalArgumentException
can be thrown without declaration.try-catch
blocks to handle exceptions and prevent program termination.Mastering throwing and catching exceptions is essential to writing reliable Java programs that handle errors gracefully and communicate problems clearly.
Java provides many built-in exceptions, but sometimes you need to represent domain-specific errors that are not covered by the standard ones. In these cases, creating your own custom exceptions helps make your code clearer and more expressive.
Custom exceptions are classes that extend either:
Exception
(checked exceptions, must be declared or caught), orRuntimeException
(unchecked exceptions, optional declaration and handling).InvalidAgeException
Suppose your program needs to validate age input, and you want a custom exception to represent invalid age values:
// Checked exception (extends Exception)
public class InvalidAgeException extends Exception {
public InvalidAgeException(String message) {
super(message);
}
}
Or, as an unchecked exception:
// Unchecked exception (extends RuntimeException)
public class InvalidAgeException extends RuntimeException {
public InvalidAgeException(String message) {
super(message);
}
}
Here is how you might use InvalidAgeException
in your code:
public class User {
private int age;
// For checked exceptions, declare with throws
public void setAge(int age) throws InvalidAgeException {
if (age < 0 || age > 150) {
throw new InvalidAgeException("Age " + age + " is not valid.");
}
this.age = age;
}
}
To use this method and handle exceptions:
public class Main {
public static void main(String[] args) {
User user = new User();
try {
user.setAge(200);
} catch (InvalidAgeException e) {
System.out.println("Caught exception: " + e.getMessage());
}
}
}
If InvalidAgeException
extends RuntimeException
, you can omit the throws
declaration and try-catch
block but still choose to catch it if desired.
class InvalidAgeException extends Exception {
public InvalidAgeException(String message) {
super(message);
}
}
class User {
private int age;
public void setAge(int age) throws InvalidAgeException {
if (age < 0 || age > 150) {
throw new InvalidAgeException("Age " + age + " is not valid.");
}
this.age = age;
}
}
public class Main {
public static void main(String[] args) {
User user = new User();
try {
user.setAge(200);
} catch (InvalidAgeException e) {
System.out.println("Caught exception: " + e.getMessage());
}
}
}
Exception
(e.g., InvalidAgeException
).Exception
if you need the exception to be checked, forcing callers to handle it.RuntimeException
for unchecked exceptions that indicate programming errors or conditions unlikely to be recovered from.Throwable
), e.g.:public class InvalidAgeException extends Exception {
public InvalidAgeException(String message) {
super(message);
}
public InvalidAgeException(String message, Throwable cause) {
super(message, cause);
}
}
Creating custom exceptions allows you to design error handling that fits your program’s needs perfectly. They provide a way to:
By defining, throwing, and catching custom exceptions, your Java applications become easier to maintain and more robust.
In Java, exceptions are broadly categorized into checked and unchecked exceptions. Understanding the difference is essential for writing robust programs that handle errors properly while keeping the code clean and maintainable.
Checked exceptions are exceptions that the Java compiler forces you to handle explicitly. This means any method that can throw a checked exception must either:
try-catch
block, orthrows
keyword.If neither is done, your program will fail to compile.
IOException
— for input/output errors (e.g., file not found).SQLException
— when database access errors occur.ClassNotFoundException
— if a class cannot be found during runtime.import java.io.*;
public class CheckedExample {
public static void readFile(String filename) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(filename));
System.out.println(reader.readLine());
reader.close();
}
public static void main(String[] args) {
try {
readFile("data.txt");
} catch (IOException e) {
System.out.println("File error: " + e.getMessage());
}
}
}
Because readFile
declares throws IOException
, callers must handle or propagate it.
Unchecked exceptions are exceptions that the compiler does not require you to handle or declare. They usually indicate programming errors or unexpected runtime conditions.
Unchecked exceptions are subclasses of RuntimeException
.
NullPointerException
— when you try to use a null
reference.ArithmeticException
— such as division by zero.IndexOutOfBoundsException
— accessing invalid array or list indices.public class UncheckedExample {
public static void divide(int a, int b) {
int result = a / b; // May throw ArithmeticException
System.out.println("Result: " + result);
}
public static void main(String[] args) {
divide(10, 0); // Will throw exception at runtime
}
}
The compiler won’t force you to catch ArithmeticException
, but if it occurs, the program crashes unless handled.
Aspect | Checked Exceptions | Unchecked Exceptions |
---|---|---|
Compiler Enforcement | Must be handled or declared | No compile-time requirement |
Typical Use Case | Recoverable conditions (e.g., I/O errors) | Programming errors (e.g., null pointer) |
Verbosity | Can lead to more verbose code due to mandatory handling | Cleaner code, but can mask errors |
Error Propagation | Explicit propagation via method signatures | Propagates unchecked, may cause runtime failure |
Flexibility | Safer in critical systems needing explicit error handling | Faster development, but riskier if misused |
IllegalArgumentException
) and checked exceptions for external system issues (IOException
).By grasping checked vs unchecked exceptions, you’ll write Java programs that are both reliable and maintainable, handling errors effectively without cluttering your code.