Exception Handling in Java
Exception handling in Java is a powerful mechanism used to handle runtime errors so that
the normal flow of the program is not disrupted.
An exception is an unwanted or unexpected event that occurs during the execution of a
program and interrupts its normal execution.
Java provides a robust, object-oriented exception handling framework that allows
programmers to detect, handle, and recover from errors gracefully instead of crashing the
program.
Need for Exception Handling
Exception handling is required because:
• Runtime errors can cause abnormal termination of programs.
• Programs should not crash due to invalid input, divide-by-zero, file not found, etc.
• It helps in maintaining program reliability and robustness.
• Separates error-handling code from normal logic.
• Improves readability and maintainability of code.
What is an Exception?
An exception is an object that represents an error condition occurring during program
execution.
Examples of exceptions:
• Dividing a number by zero
• Accessing an invalid array index
• Opening a file that does not exist
• Null object reference
Exception Hierarchy in Java
All exceptions in Java are derived from the Throwable class.
Throwable
├── Error
└── Exception
├── RuntimeException
└── Checked Exceptions
1. Error
• Represents serious problems that occur beyond the control of the program.
• Not handled by programmers.
• Example: OutOfMemoryError, StackOverflowError
2. Exception
• Conditions that programs can handle and recover from.
• Divided into:
❖ Checked Exceptions
❖ Unchecked Exceptions
Types of Exceptions in Java
Java exceptions are mainly divided into:
• Checked Exceptions
• Unchecked Exceptions
1. Checked Exceptions
Checked exceptions are those exceptions that are checked at compile time by the Java
compiler.
If a checked exception occurs, the programmer must handle it using try–catch or declare it
using throws.
Otherwise, the program will not compile.
Characteristics
• Checked at compile time
• Mandatory to handle
• Occur due to external conditions
Examples
• IOException
• SQLException
• FileNotFoundException
• ClassNotFoundException
Example Code
Import [Link].*;
Class Test {
Public static void main(String[] args) throws IOException {
FileReader fr = new FileReader(“[Link]”);
Use
Used when errors are predictable and recoverable, such as file handling or database
access.
2. Unchecked Exceptions
Unchecked exceptions are not checked at compile time.
They occur during runtime due to logical errors in the program.
Characteristics
• Checked at runtime
• Not mandatory to handle
• Usually caused by programming mistakes
Examples
• ArithmeticException
• NullPointerException
• ArrayIndexOutOfBoundsException
• NumberFormatException
Example Code
Class Test {
Public static void main(String[] args) {
Int a = 10 / 0; // ArithmeticException
}
Difference Between Checked and Unchecked Exceptions
Aspect Checked Exception Unchecked Exception
Checked at Compile time Runtime
Handling Mandatory Optional
Cause External factors Programming errors
Examples IOException, NullPointerException,
SQLException ArithmeticException
Use
Helps identify logical and runtime errors in code.
Exception Handling Keywords in Java
try Block
The try block contains code that may generate an exception.
Rule
• Must be followed by at least one catch or finally block
• Cannot exist alone
Example
Try {
Int a = 10 / 0;
catch Block
The catch block handles the exception thrown in the try block.
Features
• Executes only if an exception occurs
• Multiple catch blocks allowed
• Specific exception should be caught before general exception
Example
Try {
Int a = 10 / 0;
Catch (ArithmeticException e) {
[Link](“Division by zero error”);
finally Block
The finally block always executes, whether an exception occurs or not.
Purpose
• Used for cleanup operations
• Closing files, database connections, releasing resources
Example
Try {
Int a = 10 / 2;
Catch (Exception e) {
[Link](“Error”);
Finally {
[Link](“Finally block executed”);
Key Point
✔ finally executes even if:
• Exception occurs
• Exception is not handled
• Return statement exists
try–catch–finally Flow:
• Code in try executes
• If exception occurs → matching catch executes
• Finally executes always
Multiple catch Blocks
Java allows handling multiple exceptions separately.
Try {
Int arr[] = new int[5];
Arr[10] = 50;
Catch (ArithmeticException e) {
[Link](“Arithmetic Error”);
Catch (ArrayIndexOutOfBoundsException e) {
[Link](“Array Index Error”);
Nested try Blocks
A try block inside another try block.
Try {
Try {
Int a = 10 / 0;
Catch (ArithmeticException e) {
[Link](“Inner catch”);
Catch (Exception e) {
[Link](“Outer catch”);
}
User-Defined (Custom) Exceptions
Java allows creating own exception classes by extending Exception.
Example
class InvalidAgeException extends Exception {
InvalidAgeException(String msg) {
super(msg);
class Test {
public static void main(String[] args) {
try {
int age = 15;
if (age < 18)
throw new InvalidAgeException("Not eligible to vote");
catch (InvalidAgeException e) {
[Link]([Link]());
throw Keyword
The throw keyword is used to explicitly throw an exception by the programmer.
Use
• Create custom exceptions
• Force exception handling manually
Example
Class Test {
Static void validate(int age) {
If (age < 18)
Throw new ArithmeticException(“Not eligible”);
Else
[Link](“Eligible”);
Public static void main(String[] args) {
Validate(16);
Key Point
• Used inside a method
• Throws one exception at a time
throws Keyword
The throws keyword is used in method declaration to indicate that a method may pass an
exception to the caller.
Use
• Mainly for checked exceptions
• Transfers responsibility to calling method
Example
Import [Link].*;
Class Test {
Void readFile() throws IOException {
FileReader fr = new FileReader(“[Link]”);
}
Public static void main(String[] args) throws IOException {
Test t = new Test();
[Link]();
Difference Between throw and throws
Aspect throw throws
Purpose Explicitly throw Declare exception
exception
Location Inside method Method signature
Number One exception Multiple
exceptions
Used for Custom exceptions Checked
exceptions
Advantages of Exception Handling in Java
• Improves program stability
• Prevents crashing
• Enables error recovery
• Makes code clean and readable
• Helps in debugging
Introduction to Multithreading
Multithreading in Java is a feature that allows a program to execute two or more threads
concurrently.
A thread is the smallest unit of execution within a process. Multithreading enables a Java
program to perform multiple tasks at the same time, thereby improving performance and
better utilization of system resources.
In simple words, multithreading allows a single Java program to do many things
simultaneously.
Why Multithreading is Needed
In traditional single-threaded programs, tasks are executed one after another, which can
make applications slow and unresponsive. Multithreading overcomes this limitation by
dividing a program into multiple threads that run in parallel.
Need of Multithreading:
• To improve performance and efficiency
• To make applications responsive
• To utilize CPU time effectively
• To handle multiple users or tasks simultaneously
Multithreading in Java
Java provides built-in support for multithreading through:
• Thread class
• Runnable interface
Each thread in Java runs independently, but shares the same memory space, which
makes communication between threads faster.
1. Creating a Thread by Extending Thread Class
In this approach, a class extends the Thread class and overrides its run() method.
The run() method contains the code that will execute in a separate thread.
Steps
1. Create a class that extends Thread
2. Override the run() method
3. Create an object of the class
4. Call the start() method
Syntax
class MyThread extends Thread {
public void run() {
[Link]("Thread is running");
}
}
public class Test {
public static void main(String[] args) {
MyThread t = new MyThread();
[Link]();
}
}
Explanation
• run() → Defines the task performed by the thread
• start() → Creates a new thread and internally calls run()
• Calling run() directly will not create a new thread
Advantages
• Simple and easy to implement
• Direct access to Thread methods
Limitations
• Java does not support multiple inheritance, so the class cannot extend any other
class
2. Creating a Thread by Implementing Runnable Interface
Here, a class implements the Runnable interface and provides the implementation of the
run() method.
An object of this class is then passed to a Thread object.
Steps
1. Create a class that implements Runnable
2. Implement the run() method
3. Create a Thread object
4. Call the start() method
Syntax
class MyRunnable implements Runnable {
public void run() {
[Link]("Thread is running");
}
}
public class Test {
public static void main(String[] args) {
MyRunnable r = new MyRunnable();
Thread t = new Thread(r);
[Link]();
}
}
Explanation
• Runnable represents a task
• Thread represents the execution unit
• This separates task logic from thread control
Advantages
• Allows multiple inheritance
• Better design and flexibility
• Recommended approach in real applications
Limitations
• Slightly more complex than extending Thread
Difference Between Two Thread Creation Methods
Aspect Extending Thread Implementing Runnable
Inheritance Cannot extend another Can extend another class
class
Design Less flexible More flexible
Code Reusability Less More
Object-Oriented Poor Better
Design
Recommended No Yes
Why start() is used instead of run()
• start() creates a new thread
• run() executes like a normal method
• JVM internally calls run() after creating a new call stack
Thread Life Cycle in Java
In Java, a thread is a lightweight unit of execution that runs independently within a
program.
From the moment a thread is created until it finishes execution, it passes through different
states, collectively known as the Thread Life Cycle.
Understanding the thread life cycle is important because it helps programmers:
• Control thread execution
• Manage CPU utilization
• Avoid issues like deadlock and starvation
States of Thread Life Cycle
A Java thread goes through the following main states:
1. New State
2. Runnable State
3. Running State
4. Waiting / Blocked State
5. Terminated (Dead) State
1️⃣ New State
• A thread is said to be in the New state when it is created but not yet started.
• Memory is allocated for the thread, but it does not execute any code.
How it occurs
• When a thread object is created using the Thread class.
Example
Thread t = new Thread();
Key Point
• The thread is not alive in this state.
• start() method has not been called yet.
2️⃣ Runnable State
• After calling the start() method, the thread enters the Runnable state.
• In this state, the thread is ready to run but waiting for CPU time.
How it occurs
[Link]();
Important Note
• Runnable does not mean running immediately.
• The thread waits in a queue until the CPU scheduler selects it.
Key Point
• Multiple threads can be in the Runnable state at the same time.
3️⃣ Running State
• When the CPU allocates time to the thread, it enters the Running state.
• The thread’s run() method starts executing.
Characteristics
• Only one thread per CPU core can be in the Running state.
• Execution happens line by line.
Example
public void run() {
[Link]("Thread is running");
}
4️⃣ Waiting / Blocked State
• A thread enters this state when it is temporarily inactive and not eligible for CPU
execution.
Reasons for entering this state
• Calling sleep() method
• Waiting for I/O operations
• Waiting for another thread to complete (join())
• Waiting to acquire a lock on a resource
Examples
[Link](1000); // Timed waiting
[Link](); // Waiting for another thread
Key Point
• The thread can return to the Runnable state after:
o Sleep time completes
o Resource becomes available
o Another thread finishes execution
5️⃣ Terminated (Dead) State
• A thread enters the Terminated state when it has finished execution.
• Once terminated, a thread cannot be restarted.
How it occurs
• run() method completes normally
• Thread is stopped due to an error or exception
Key Point
• Calling start() again on a terminated thread causes an exception.
Thread Life Cycle Flow
New → Runnable → Running
↓
Waiting / Blocked
↓
Runnable
↓
Terminated
Important Methods Affecting Life Cycle
Method Effect
start() Moves thread from New to
Runnable
run() Contains execution logic
sleep() Moves thread to waiting state
join() Waits for another thread
yield() Gives chance to other threads
Execution end Thread terminates
Thread Priority
Thread priority is a mechanism used by Java to decide which thread should be executed
first when multiple threads are in the runnable state.
Each thread is assigned a priority value, and higher-priority threads are generally
executed before lower-priority threads.
Priority Range
Java supports priority values from 1 to 10:
Constant Value Description
Thread.MIN_PRIORITY 1 Lowest priority
Thread.NORM_PRIORITY 5 Default priority
Thread.MAX_PRIORITY 10 Highest priority
Setting Thread Priority
Thread t1 = new Thread();
[Link](Thread.MAX_PRIORITY);
Getting Thread Priority
int p = [Link]();
Important Points
• Priority is a suggestion to the thread scheduler, not a guarantee.
• Actual execution depends on the OS and JVM implementation.
• Overuse of high priority may cause starvation of low-priority threads.
Use of Thread Priority
• Important background tasks
• Time-critical operations
• Resource management
Sleeping a Thread (sleep() method)
The sleep() method temporarily pauses the execution of the current thread for a
specified time, after which it automatically resumes execution.
Syntax
[Link](milliseconds);
or
[Link](milliseconds, nanoseconds);
Example
try {
[Link](2000); // sleeps for 2 seconds
} catch (InterruptedException e) {
[Link](e);
}
Key Characteristics
• sleep() is a static method of Thread class.
• It does not release locks held by the thread.
• It throws InterruptedException, so it must be handled using try-catch.
Uses of sleep()
• Creating delays
• Simulating time-based operations
• Controlling execution speed
• Animation and periodic tasks
Joining a Thread (join() method)
The join() method allows one thread to wait until another thread finishes its execution.
It is used to achieve synchronization between threads.
Syntax
[Link]();
Example
Thread t1 = new Thread(() -> {
[Link]("Thread t1 running");
});
[Link]();
[Link](); // main thread waits for t1 to finish
[Link]("Main thread continues");
Working
• When join() is called, the calling thread enters waiting state.
• Execution resumes only after the joined thread completes.
Overloaded Forms
join(); // waits until thread finishes
join(long ms); // waits for specified time
join(long ms, int ns);
Uses of join()
• Ensuring task completion order
• Waiting for background tasks
• Coordinating dependent threads
Advantages of Multithreading
1. Better CPU Utilization
Multiple threads can run on different CPU cores, increasing performance.
2. Improved Application Responsiveness
For example, while one thread handles user input, another can perform background
tasks.
3. Resource Sharing
Threads share memory, reducing overhead compared to multiple processes.
4. Faster Execution
Tasks can be executed concurrently rather than sequentially.
5. Scalability
Useful in server-side applications where many clients are handled at the same
time.
Real-World Examples of Multithreading
• Web servers handling multiple client requests simultaneously
• Media players playing music while downloading files
• Banking systems processing multiple transactions at once
• Games handling graphics, sound, and input at the same time
Multitasking vs Multithreading
Aspect Multitasking Multithreading
Level Process level Thread level
Memory Separate memory Shared memory
Speed Slower Faster
Overhead High Low
Role of Multithreading in Java Applications
• Used extensively in GUI applications
• Essential for network programming
• Important in real-time systems
• Backbone of enterprise and server-side applications