OOPs JAVA Unit-2 (AKTU)
Exception Handling
An exception is an abnormal event that occurs during program execution, which
disrupts the normal flow of instructions. Exception Handling is a mechanism to
handle runtime errors so that the normal flow of the application can be maintained.
Example : int a = 10 / 0; // throws ArithmeticException
The Idea Behind Exception Handling
1. Preventing Abnormal Termination: It stops the program from crashing
suddenly when a runtime error occurs, ensuring the normal flow of execution
continues smoothly.
2. Separating Logic from Errors: It keeps your main working code and error-
solving code separate, making the program clean, organized, and easy to read.
Errors vs Exceptions :-
Error (Unrecoverable)
It happens when the computer or the Java engine (JVM) itself breaks, and no matter
how good your code is, you cannot fix it while the program is running.
Examples
• VirtualMachineError: This happens when the JVM itself fails or runs out
of the resources it needs to keep working.
o OutOfMemoryError: This occurs when your computer's RAM is
completely full, and the JVM cannot allocate any more memory for
your objects.
o StackOverflowError: This usually happens due to infinite recursion
(when a function calls itself forever), causing the memory "stack" to
overflow and crash.
CLEAR THE CORE
Exception (Recoverable)
An exception is an abnormal event that occurs during program execution, which
disrupts the normal flow of instructions.
The Throwable class is the root class of all errors and exceptions. The hierarchy is
divided into two main branches: Error and Exception.
Difference
Feature Error Exception
Nature Indicates a serious system or Indicates a manageable problem
JVM problem. in the code.
Recovery Impossible to recover; the Possible to recover using try-
program must stop. catch blocks.
Cause Caused by the environment Caused by the program logic
(e.g., RAM full). (e.g., bad input).
Check Always Unchecked (happens at Can be Checked (compile-time)
Type runtime). or Unchecked.
Examples StackOverflowError, NullPointerException,
OutOfMemoryError IOException
CLEAR THE CORE
Types of Exceptions :-
Checked Exceptions (Compile-Time)
These are checked by the compiler. You must handle them using try-catch or throws,
or the program will not compile.
Key Examples:
• FileNotFoundException: Happens when the code tries to open a file that
does not exist on the system.
• IOException: Triggered when an input or output operation (like reading or
writing data) fails.
• SQLException: Occurs when there is an error connecting to or executing a
query in a database.
• ClassNotFoundException: Thrown when the JVM tries to load a specific
class but cannot find it.
Unchecked Exceptions (Runtime)
These are not checked by the compiler. You can run your program without handling
them, but they will cause a crash during execution if they occur.
Key Examples:
• ArithmeticException: Happens when you perform an invalid math
operation, like dividing a number by zero.
public class ArithmeticExceptionExample {
public static void main(String[] args) {
try {
// Arithmetic Exception
int a = 50 / 0;
} catch (ArithmeticException e) {
[Link]("Arithmetic Exception occurred");
}
• NullPointerException: Occurs when you try to use an object that has not
been initialized (it is null).
CLEAR THE CORE
public class NullPointerExample {
public static void main(String[] args) {
String s = null;
try {
// This will cause an error
[Link]([Link]());
} catch (NullPointerException e) {
[Link]("The string is null, cannot find its length.");}
• ArrayIndexOutOfBoundsException: Triggered when you try to
access an array index that doesn't exist (e.g., asking for index 10 in an array
that only has 5 items).
• IllegalArgumentException: Thrown when you pass an invalid or
inappropriate value to a method.
• NumberFormatException: Happens when you try to convert a String
into a Number, but the String contains letters or symbols (e.g., trying to turn
"Hello" into an integer).
CLEAR THE CORE
Difference –
[Link] Feature Checked Exceptions Unchecked Exceptions (Runtime)
(Compile-Time)
1 When are They are checked by the They are not checked by the
they compiler at compile-time. compiler; they happen at runtime
checked? (execution).
2 Handling You must handle them You are not forced to handle them,
Rule using a try-catch block or though it is a good programming
the throws keyword. practice.
3 Compilation If you do not handle them, The program will compile
Status the program will not successfully even if you don't write
compile (it gives a code to handle them.
compilation error).
4 Root Cause Usually caused by external Usually caused by programming
factors outside the bugs or logical errors (e.g.,
program's direct control dividing by zero, accessing a null
(e.g., missing file, database object).
down).
5 Inheritance They inherit directly from They inherit directly from the
(Parent the Exception class RuntimeException class.
Class) (excluding
RuntimeException).
6 Recovery The program can usually Recovery is difficult; usually, the
recover from these if code logic needs to be fixed to
handled properly (e.g., prevent the crash.
asking the user for a new file
path).
7 Examples FileNotFoundException, ArithmeticException,
IOException, NullPointerException,
SQLException, ArrayIndexOutOfBoundsException.
ClassNotFoundException.
CLEAR THE CORE
Control Flow in Exception Handling
• When an exception occurs in the try block, the remaining code is skipped.
• JVM searches for a matching catch block.
• If found, the catch block executes and then finally runs (if present).
• After that, the program continues normally.
JVM Reaction to Unhandled Exceptions
• If an exception is not handled, the JVM creates an Exception Object.
• It checks the call stack to find a handler – this process is call Stack
Unwinding.
• If no handler is found , the default exception handler prints the error (stack
trace) and stops the program.
Try-Catch-Finally
Try Block: The Problem Zone
• Simple Definition: It contains the code that might cause an error.
• How it works: Java attempts to run this code. If a line fails, Java "jumps out"
of the block immediately.
• Key Point: You cannot have a try block alone; it must be followed by catch
or finally.
Catch Block: The Solution Zone
• Simple Definition: It contains the code that fixes the error.
• How it works: It only runs if the try block fails. It acts like a safety net that
catches the "falling" program and keeps it running.
• Key Point: You can use Multiple Catches to handle different errors (like
Math errors vs. Memory errors) separately.
Finally Block: The Cleanup Zone
• Simple Definition: It contains code that must run, no matter what.
• How it works: Whether the try block succeeds or the catch block runs, finally
always executes at the very end.
CLEAR THE CORE
• Key Point: It is used for "Closing" things—like closing a file, a database
connection, or a scanner to save memory.
When a single piece of code can throw more than one type of exception, we use
multiple catch blocks.
Code :-
public class MultipleExceptionExample {
public static void main(String[] args) {
try {
// Arithmetic Exception
int a = 50 / 0;
// Array Index Exception
int arr[] = new int[5];
arr[10] = 50;
} catch (ArithmeticException e) {
[Link]("Arithmetic Exception occurred.");
} catch (ArrayIndexOutOfBoundsException e) {
[Link]("Array Index is out of range.");
} catch (Exception e) {
// Generic handler for any other exception
[Link]("Some other error occurred.");
} finally {
[Link]("Finally block is always executed.");
}
[Link]("Normal flow continues...");
}
}
CLEAR THE CORE
Important Rules
• Order of Catch Blocks: Always put specific exceptions (like
ArithmeticException) before the general Exception class.
• Only One Catch Runs: At a time, only one exception occurs.
• Finally is Mandatory (Execution-wise): Even if there is a return statement
in the try block, the finally block will still execute before the method returns.
Throw and Throws
Basis throw throws
Meaning Used to explicitly throw an Used to declare exceptions that a
exception in the program. method may throw.
Usage Used inside a method or Used in the method declaration.
block of code.
Purpose It actually creates and throws
It only informs the compiler
the exception object. and caller about possible
exceptions.
Number of Can throw only one exception Can declare multiple exceptions
Exceptions at a time. separated by commas.
Syntax throw new ExceptionName(); methodName() throws
ExceptionName
Handling Requires an exception object. Does not create an exception
object.
Example if (num < 0) { void check() throws
throw new ArithmeticException {
ArithmeticException throw new
("Negative number"); ArithmeticException();
} }
CLEAR THE CORE
Code :
// Step 1: Apni khud ki Exception class banana (User-Defined Exception)
// Isko humne main Exception class se extend kiya hai
class AgeException extends Exception {
public AgeException(String s) {
super(s); // Ye message parent class (Exception) ko bhej dega
}
}
public class VotingMachine {
// vo signature hota h ek / vo bs btayegi ki yha exception aa sakti hai
public static void checkEligibility(int age) throws AgeException {
// actual bnda h zo tmhe bahr nikaal skta h
if(age < 18){
// Yahan hum inbuilt ki jagah apni banayi hui exception throw kar rahe hain
throw new AgeException("Tu chota h tu rehne de");
} else {
[Link]("Jaa bhai vote daal aa!");
}
}
public static void main(String[] args) {
try {
CLEAR THE CORE
checkEligibility(15);
} catch (AgeException e) {
// [Link]() wahi print karega jo upar throw mein likha tha
[Link]([Link]() + " -> thik h chod de");
}
}
}
In-Built Exceptions
In-built exceptions are pre-defined exception classes provided by Java to handle
common errors during program execution. There are two types of built-in exceptions
in Java:
• Checked Exceptions: These exceptions are checked at compile-time, forcing
the programmer to handle them explicitly (e.g., using a try-catch block or the
throws keyword).
• Unchecked Exceptions: These exceptions are checked at runtime and do not
require explicit handling at compile-time.
User-Defined Exceptions
Sometimes, Java's in-built exceptions are not enough to handle specific real-world
errors in our projects (for example, an InsufficientBalanceException in a banking
app). To solve this, developers create their own custom exception classes. These are
called User-Defined Exceptions.
Code :
// Step 1: Create your custom exception class
class InvalidAgeException extends Exception {
// Constructor that takes the error message
public InvalidAgeException(String message) {
CLEAR THE CORE
super(message); // Passes the message to the parent Exception class } }
public class CustomExceptionDemo {
// Step 2: Use the 'throws' keyword to warn about the custom exception
static void checkAge(int age) throws InvalidAgeException {
if (age < 18) {
// Step 3: Use the 'throw' keyword to manually trigger it
throw new InvalidAgeException("Age is less than 18. Not eligible to vote!");
} else {
[Link]("Welcome! You are eligible to vote.");
public static void main(String[] args) {
// Step 4: Handle it using try-catch
try {
checkAge(16); // Passing invalid age to trigger error
} catch (InvalidAgeException e) {
[Link]("Custom Exception Caught: " + [Link]());
CLEAR THE CORE
➢ Methods to Print Exception Details
When an exception is caught in the catch block, Java gives us three main methods
to print its details:
• printStackTrace(): Prints EVERYTHING. It shows the Exception Name +
Reason + the exact Line Number where the error happened.
• toString(): Prints SHORT INFO. It only shows the Exception Name +
Reason.
• getMessage(): Prints ONLY the Description. It shows just the reason for the
error, without the class name or line numbers.
CLEAR THE CORE
I/O Basics
Java I/O Stream
Java I/O Stream is used to perform input and output operations. It allows a
program to read data from input sources and write data to output destinations
such as files.
Java provides two types of streams:
1. Byte Stream
2. Character Stream
Difference between Character Stream and Byte Stream
Basis Byte Stream Character Stream
Data type Works with 8-bit bytes Works with 16-bit
characters
Use Used for binary data (images, Used for text data
audio, etc.)
Main InputStream, OutputStream Reader, Writer
classes
CLEAR THE CORE
Encoding Does not handle character Handles character encoding
encoding automatically
Example FileInputStream f = new FileReader f = new
FileInputStream("[Link]"); FileReader("[Link]");
Byte Stream
• Works with 8-bit bytes.
• Used for reading and writing binary data like images, audio, video, or raw
byte files.
• Main classes: InputStream (for reading) and OutputStream (for writing).
• Example classes: FileInputStream, FileOutputStream.
Code -
import [Link].*;
public class ByteStreamExample {
public static void main(String args[]) {
try {
// WRITING TO A FILE
FileOutputStream fout = new FileOutputStream("[Link]");
[Link](65); // Writes 'A' (ASCII value 65)
[Link]();
[Link]("Data written successfully.");
// READING FROM A FILE
FileInputStream fin = new FileInputStream("[Link]");
int i;
// Loop is mandatory to read until the end of the file
while ((i = [Link]()) != -1) {
[Link]((char) i);
}
CLEAR THE CORE
[Link]();
} catch (Exception e) {
[Link](e);
}
}
}
Character Stream
• Works with 16-bit Unicode characters.
• Used specifically for text data (strings, chars) to avoid encoding issues.
• Main classes: Reader (for reading) and Writer (for writing).
• Example classes: FileReader, FileWriter.
Code -
import [Link].*;
public class CharacterStreamExample {
public static void main(String args[]) {
try {
// WRITING TEXT TO A FILE
FileWriter fw = new FileWriter("[Link]");
[Link]("Hello AKTU Students!");
[Link]();
[Link]("Text written successfully.");
// READING TEXT FROM A FILE
FileReader fr = new FileReader("[Link]");
CLEAR THE CORE
int i;
// Loop reads character by character until -1 (End of File)
while ((i = [Link]()) != -1) {
[Link]((char) i);
[Link]();
} catch (Exception e) {
[Link](e);
Writing file
• Writing a file means storing data into a file.
• In Java, files can be written using classes like FileWriter or
FileOutputStream.
Reading file
• Reading a file means getting data from a file into the program.
• In Java, files can be read using classes like FileReader or FileInputStream.
There are 3 basic steps for file handling :
• Open/Create the file.
• Perform Read or Write operation.
• Close the file using .close()
Now – Write the character stream code if it is in 7 marks
CLEAR THE CORE
Multithreading
Thread: A thread is the smallest unit of execution within a process that runs
independently.
Real-Life Example: Imagine you are using a web browser or Microsoft Word.
• Thread 1 is taking your typing input.
• Thread 2 is checking your spelling mistakes in the background.
• Thread 3 is auto-saving your document.
All these tasks are happening at the exact same time inside one single application.
This simultaneous execution is Multithreading!
What is Multithreading?
Multithreading is a process of executing multiple threads (small sub-tasks)
simultaneously within a single program to save time and maximize CPU utilization.
Thread Life Cycle
1. New (Born State)
A thread is in this state when you create a new thread object, but you have not called
the start() method yet.
CLEAR THE CORE
Example: Thread t = new Thread();
2. Runnable (Ready State)
Once you call the start() method, the thread enters the Runnable state. It means the
thread is ready to run and is waiting in a queue for the CPU processor to pick it up.
3. Running State
When the CPU selects the thread from the Runnable queue, it enters the Running
state. This is when the code inside the thread's run() method actually executes.
4. Blocked / Waiting State
A thread enters this state when it is temporarily inactive or paused. This happens if
the thread is waiting for an I/O operation, waiting for a lock (synchronization), or if
methods like sleep() or wait() are called. Once the wait is over, it goes back to the
Runnable state.
5. Terminated (Dead State)
A thread enters this state when it has completely finished executing its run() method.
Once a thread is terminated, it cannot be started again.
Ways to Create a Thread in Java
There are two ways to create a thread in Java. The core logic of the thread always
goes inside the run() method.
Method 1: By Extending the Thread Class
In this method, your class inherits from the built-in Thread class. You override the
run() method and call the start() method to begin execution.
Code -
// 1. Extend the Thread class
class MyThread extends Thread {
// 2. Override the run() method
public void run() {
CLEAR THE CORE
[Link]("Thread is running using Thread class.");
}
}
public class Main {
public static void main(String[] args) {
// 3. Create an object of the class
MyThread t1 = new MyThread();
// 4. Call start() to execute the thread
[Link]();
}
}
Method 2: By Implementing the Runnable Interface
In this method, your class implements the Runnable interface. You still override the
run() method, but you must pass your object into a standard Thread object to start it.
Code -
// 1. Implement the Runnable interface
class MyRunnable implements Runnable {
// 2. Override the run() method
public void run() {
[Link]("Thread is running using Runnable interface.");
}
}
public class Main {
public static void main(String[] args) {
// 3. Create an object of your class
MyRunnable myObj = new MyRunnable();
// 4. Pass the object to a new Thread constructor
Thread t1 = new Thread(myObj);
CLEAR THE CORE
// 5. Call start()
[Link]();
}
}
Which one is better : Implementing the Runnable interface is better. Since Java
does not support multiple inheritance, if you extend the Thread class, your class
cannot extend any other class. If you implement Runnable, your class is still free to
extend another class if needed.
Thread Priorities
1. Introduction : In Java, every thread has a priority. Thread priority is used by
the thread scheduler to decide when each thread should be allowed to run.
• The CPU generally executes threads with a higher priority before threads
with a lower priority.
• Priorities are represented by integer numbers ranging from 1 to 10.
2. Priority Constants in Java The Thread class provides three static final
constants to set thread priorities:
• Thread.MIN_PRIORITY : Value is 1 (Lowest priority)
• Thread.NORM_PRIORITY : Value is 5 (Default priority for any new
thread)
• Thread.MAX_PRIORITY : Value is 10 (Highest priority)
3. Important Methods To work with thread priorities, we use two built-in
methods of the Thread class:
1. public final void setPriority(int newPriority): Used to change the priority
of a thread. If the value is not between 1 and 10, it throws
IllegalArgumentException.
2. public final int getPriority(): Returns the current priority of the thread.
CLEAR THE CORE
4. Java Program for Thread Priority
Code :
// Step 1: Create a class extending Thread
class PriorityThread extends Thread {
// Step 2: Override the run method
public void run() {
[Link]("Thread Name: " + [Link]().getName() +
" | Priority: " + [Link]().getPriority());
}
}
public class ThreadPriorityExample {
public static void main(String[] args) {
// Step 3: Create thread objects
PriorityThread t1 = new PriorityThread();
PriorityThread t2 = new PriorityThread();
PriorityThread t3 = new PriorityThread();
// Step 4: Set custom names for clarity (Optional but good for exams)
[Link]("Thread-A");
[Link]("Thread-B");
[Link]("Thread-C");
// Step 5: Set priorities using constants or numbers (1 to 10)
[Link](Thread.MIN_PRIORITY); // Sets priority to 1
[Link](Thread.NORM_PRIORITY); // Sets priority to 5
[Link](Thread.MAX_PRIORITY); // Sets priority to 10
// Step 6: Start the threads
[Link]();
[Link]();
[Link]();
}
}
CLEAR THE CORE
Expected Output: (Note in Exam: Write that the exact output order may vary
because thread scheduling depends on the OS, but generally, the highest priority
thread executes first).
Plaintext
Thread Name: Thread-C | Priority: 10
Thread Name: Thread-B | Priority: 5
Thread Name: Thread-A | Priority: 1
5. Important Note
• Inheritance of Priority: By default, a child thread inherits the priority of its
parent thread. The default priority of the main thread is always 5.
• Platform Dependent: Thread scheduling is strictly controlled by the OS
(Operating System). Setting a higher priority is just a request to the OS, not
a strict rule. The OS may sometimes ignore it based on its own algorithms
(like Time Slicing).
Synchronizing Threads in Java
Synchronization is a mechanism used to control the access of multiple threads
to a shared resource. It ensures that only one thread can execute a critical section
of code at a time, prevents data corruption and stops threads from disturbing each
other.
In Java, synchronization is achieved using the synchronized keyword, which locks
an object so that other threads cannot access it until the current thread finishes
execution.
Methods Used in Synchronization
1. wait() – Causes the current thread to wait until another thread notifies it.
2. notify() – Wakes up one waiting thread.
3. notifyAll() – Wakes up all waiting threads.
How to Achieve Synchronization in Java?
We can achieve synchronization in Java mainly in two ways. Both ways use the
synchronized keyword to lock an object so that only one thread can use it at a time.
CLEAR THE CORE
1. Using a Synchronized Method
When you declare a whole method as synchronized, the thread locks the entire
method. No other thread can use this method until the first thread finishes its work.
• Best for: When the complete method contains critical code.
Code -
class Test {
// The whole method is locked
synchronized void display() {
[Link]("Thread running: Only one thread at a time.");
}
}
2. Using a Synchronized Block
Instead of locking the entire method, you can lock only a specific part (block) of
the code.
• Best for: This is highly efficient because it allows other threads to access the
normal, non-critical parts of the method, saving time.
Code -
class Test {
void display() {
[Link]("Normal code: Any thread can run this.");
// Only this specific block is locked
synchronized(this) {
[Link]("Critical code: Locked for one thread only.");
}
}
}
CLEAR THE CORE
Inter-Thread Communication in Java
Inter-Thread Communication is a mechanism that allows multiple threads to
communicate and coordinate with each other while sharing a common resource. It is
used to avoid problems such as busy waiting, resource conflicts, and inefficient
CPU usage.
In multithreaded programming, sometimes one thread must wait for another thread
to perform some task before it can continue execution. Inter-thread communication
allows threads to suspend execution and be notified when the required condition
is met.
In Java, inter-thread communication is implemented using the wait(), notify(), and
notifyAll() methods of the Object class. These methods must be used inside a
synchronized block or synchronized method.
Methods Used in Inter-Thread Communication
1. wait()
The wait() method causes the current thread to temporarily pause execution and
enter the waiting state until another thread sends a notification.
When wait() is called:
• The thread releases the lock on the object.
• The thread enters the waiting state.
• The thread resumes execution only after notify() or notifyAll() is called.
Syntax: wait();
2. notify()
The notify() method is used to wake up a single thread that is waiting on the same
object.
If multiple threads are waiting, only one thread is selected randomly to continue
execution.
Syntax: notify();
CLEAR THE CORE
3. notifyAll()
The notifyAll() method is used to wake up all threads waiting on the same object.
All the waiting threads become runnable, and the scheduler decides which thread
will execute first.
Syntax: notifyAll();
class Test {
synchronized void display() {
try {
[Link]("Thread is waiting...");
wait();
} catch(Exception e) {}
[Link]("Thread resumed execution");
}
synchronized void notifyThread() {
[Link]("Thread notifying...");
notify();
}
Advantages of Inter-Thread Communication
• Improves coordination between multiple threads
• Prevents busy waiting and CPU wastage
• Ensures efficient use of shared resources
CLEAR THE CORE
Write a Java program that creates two threads – one to print even numbers
and the other for odd numbers. (AKTU 24-25)
package practice;
class SequencePrinter {
int number = 1;
int maxNumber = 10;
synchronized void printTurn(int remainder) {
while (number <= maxNumber) {
// Wait if it is NOT your turn
while (number % 2 != remainder) {
try {
wait();
} catch (Exception e) {
[Link](e);
}
}
// Check if limit is crossed after waking up
if (number > maxNumber) {
break;
}
[Link]([Link]().getName() + " printed: " +
number);
number++;
notifyAll();
}
CLEAR THE CORE
}
}
public class Printer {
public static void main(String[] args) {
SequencePrinter printer = new SequencePrinter();
// Thread t1 = new Thread(() -> [Link](1), "Thread-Odd");
// Thread t2 = new Thread(() -> [Link](0), "Thread-Even");
// Simple traditional way to create Odd Thread
Thread t1 = new Thread(new Runnable() {
public void run() {
[Link](1); // 1 is remainder for odd numbers
}
}, "Thread-Odd");
// Simple traditional way to create Even Thread
Thread t2 = new Thread(new Runnable() {
public void run() {
[Link](0); // 0 is remainder for even numbers
}
}, "Thread-Even");
[Link]();
[Link]();
}
}
CLEAR THE CORE
Want to understand these notes easily?
Watch the complete Step-by-Step Lecture for Unit-2 here:
[Link]
Youtube Channel : Clear The Core (CTC)
THANK YOU
CLEAR THE CORE