Java Programming
Exception Handling | I/O Streams | Multithreading
University Long-Answer Notes (7 Marks Each)
Q1. try, catch, finally, throw, and throws — Syntax & Examples
Java provides a robust exception-handling mechanism using five keywords: try, catch, finally, throw,
and throws. Together they allow programs to detect, handle, and recover from runtime errors gracefully.
1. try Block
A try block encloses code that might throw an exception. If an exception occurs, execution jumps to the
matching catch block.
Syntax:
try {
// risky code
}
2. catch Block
Catches and handles a specific type of exception thrown inside the try block. Multiple catch blocks can
follow one try.
catch (ExceptionType e) {
// handle the exception
}
3. finally Block
Always executes whether or not an exception occurred. Used for cleanup (closing files, DB
connections, etc.).
finally {
// always runs
}
4. throw Keyword
Used to explicitly throw an exception object from within a method or block.
throw new ArithmeticException("Division by zero");
5. throws Keyword
Declared in the method signature to indicate that the method may throw certain checked exceptions —
the caller must handle them.
void readFile() throws IOException {
// code that may throw IOException
}
Complete Example
public class ExceptionDemo {
// 'throws' in method signature
static void checkAge(int age) throws Exception {
if (age < 18)
throw new Exception("Age must be >= 18"); // 'throw'
[Link]("Access granted.");
}
public static void main(String[] args) {
try { // try block
checkAge(15);
} catch (Exception e) { // catch block
[Link]("Caught: " + [Link]());
} finally { // finally block
[Link]("Program ends.");
}
}
}
Output:
Caught: Age must be >= 18
Program ends.
Q2. Checked vs. Unchecked Exceptions
Java exceptions are divided into two broad categories based on when they are detected — at compile
time or at runtime.
Checked Exceptions
These are exceptions that are checked by the compiler at compile time. The programmer must handle
them using try-catch or declare them using throws. They extend Exception (but not RuntimeException).
Examples: IOException, SQLException, FileNotFoundException
import [Link].*;
public class CheckedDemo {
public static void main(String[] args) {
try {
FileReader fr = new FileReader("[Link]"); // May throw
FileNotFoundException
} catch (FileNotFoundException e) {
[Link]("File not found: " + [Link]());
}
}
}
Unchecked Exceptions
These exceptions are NOT checked by the compiler. They occur at runtime and extend
RuntimeException. The programmer is not forced to handle them, but it is good practice to do so.
Examples: ArithmeticException, NullPointerException, ArrayIndexOutOfBoundsException
public class UncheckedDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
[Link](arr[5]); // ArrayIndexOutOfBoundsException at runtime
}
}
Comparison Table
Feature Checked Exception Unchecked Exception
Detection Time Compile time Runtime
Must Handle? Yes (compiler enforces) No (optional)
Superclass Exception RuntimeException
Example IOException, SQLException ArithmeticException,
NullPointerException
Use Case External resource failures Programming logic errors
Q3. ArithmeticException — Handling Division by Zero
ArithmeticException is an unchecked exception (extends RuntimeException) that is thrown when an
illegal arithmetic operation is performed — the most common case being integer division by zero.
Class Hierarchy
[Link]
└── [Link]
└── [Link]
└── [Link]
└── [Link]
Program — Division by Zero
public class DivisionDemo {
public static void main(String[] args) {
int a = 20, b = 0;
try {
int result = a / b; // throws ArithmeticException
[Link]("Result: " + result);
} catch (ArithmeticException e) {
[Link]("Error: " + [Link]());
} finally {
[Link]("Execution complete.");
}
}
}
Output:
Error: / by zero
Execution complete.
Note: Floating-point division by zero does NOT throw an exception — it returns Infinity or NaN.
double x = 10.0 / 0; // Output: Infinity (no exception)
Q4. User-Defined Exceptions — Invalid Age Input
User-defined (custom) exceptions allow programmers to create meaningful exception types specific to
their application's domain. They are created by extending the Exception class (for checked) or
RuntimeException (for unchecked).
Steps to Create a Custom Exception
• Create a class that extends Exception or RuntimeException.
• Provide a constructor that passes the message to the superclass.
• Use throw to raise the custom exception wherever needed.
Program — InvalidAgeException
// Step 1: Define custom exception
class InvalidAgeException extends Exception {
InvalidAgeException(String msg) {
super(msg); // Pass message to Exception
}
}
// Step 2: Use the custom exception
public class AgeValidator {
static void validateAge(int age) throws InvalidAgeException {
if (age < 0 || age > 150) {
throw new InvalidAgeException("Invalid age: " + age);
}
[Link]("Age " + age + " is valid.");
}
public static void main(String[] args) {
int[] testAges = {25, -5, 200};
for (int age : testAges) {
try {
validateAge(age);
} catch (InvalidAgeException e) {
[Link]("Caught: " + [Link]());
}
}
}
}
Output:
Age 25 is valid.
Caught: Invalid age: -5
Caught: Invalid age: 200
Q5. Byte Streams vs. Character Streams
Java I/O streams are of two types — Byte Streams, which handle raw binary data (8-bit), and Character
Streams, which handle text data (16-bit Unicode). Both are part of the [Link] package.
Byte Streams
Handle data as raw bytes (8-bit). Used for binary files such as images, audio, and video. The base
classes are InputStream and OutputStream.
Key Classes: FileInputStream, FileOutputStream, BufferedInputStream, DataInputStream
// Byte Stream Example — copy file byte by byte
import [Link].*;
public class ByteStreamDemo {
public static void main(String[] args) throws IOException {
FileInputStream in = new FileInputStream("[Link]");
FileOutputStream out = new FileOutputStream("[Link]");
int b;
while ((b = [Link]()) != -1)
[Link](b);
[Link](); [Link]();
[Link]("File copied.");
}
}
Character Streams
Handle data as characters (16-bit Unicode). Designed for text files. The base classes are Reader and
Writer.
Key Classes: FileReader, FileWriter, BufferedReader, PrintWriter
// Character Stream Example — read text file
import [Link].*;
public class CharStreamDemo {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("[Link]");
int ch;
while ((ch = [Link]()) != -1)
[Link]((char) ch);
[Link]();
}
}
Comparison Table
Feature Byte Stream Character Stream
Data Unit 8-bit byte 16-bit character (Unicode)
Base Classes InputStream / OutputStream Reader / Writer
Used For Binary files (images, audio) Text files (.txt, .csv)
Key Classes FileInputStream, FileReader, FileWriter
FileOutputStream
Encoding No encoding applied Handles character encoding
Q6. Read with FileReader — Write with FileWriter
FileReader and FileWriter are character stream classes used for reading from and writing to text files
respectively. They work with characters (Unicode) making them ideal for text file operations.
Program — Read from [Link] and Write to [Link]
import [Link].*;
public class FileReadWrite {
public static void main(String[] args) {
String inputFile = "[Link]";
String outputFile = "[Link]";
try {
// ── Reading using FileReader ──
FileReader fr = new FileReader(inputFile);
StringBuilder sb = new StringBuilder();
int ch;
while ((ch = [Link]()) != -1) {
[Link]((char) ch);
}
[Link]();
[Link]("Read from file:\n" + [Link]());
// ── Writing using FileWriter ──
FileWriter fw = new FileWriter(outputFile);
[Link]([Link]()); // Write the same content to output
[Link]();
[Link]("Written to [Link] successfully.");
} catch (FileNotFoundException e) {
[Link]("File not found: " + [Link]());
} catch (IOException e) {
[Link]("I/O Error: " + [Link]());
}
}
}
Output (assuming [Link] contains 'Hello Java!'):
Read from file:
Hello Java!
Written to [Link] successfully.
Tip: Use BufferedReader and BufferedWriter for better performance with large files — they read/write
data in chunks instead of character by character.
Q7. Thread Life Cycle in Java
A thread in Java goes through a well-defined sequence of states during its lifetime, collectively called
the Thread Life Cycle. The [Link] class manages these states.
The Five States of a Thread
• New — Thread object is created but start() has not been called yet.
• Runnable — start() is called; thread is ready to run and waiting for CPU allocation by the
scheduler.
• Running — Thread is actually executing (CPU is assigned).
• Blocked/Waiting — Thread is paused temporarily (waiting for I/O, a lock, or sleep to finish).
• Terminated (Dead) — Thread has finished its run() method or was stopped.
Thread Life Cycle Diagram
┌─────────────────────────────────────────┐
│ THREAD LIFE CYCLE │
└─────────────────────────────────────────┘
new Thread() start() CPU allocated
┌──────────┐ ─────────────► ┌──────────────┐ ──────────► ┌─────────┐
│ NEW │ │ RUNNABLE │ │ RUNNING │
└──────────┘ └──────────────┘ └─────────┘
▲ │
│ sleep()/wait()/I/O │
│ ◄─────────────────────────┘
│ ┌─────────────────┐
│ │ BLOCKED/WAITING│
│ └─────────────────┘
│ notify() / I/O done
┌────────────┐
run() completes ► │ TERMINATED │
└────────────┘
Quick Example
class MyThread extends Thread {
public void run() {
[Link]("Thread is RUNNING");
}
}
public class LifeCycleDemo {
public static void main(String[] args) throws InterruptedException {
MyThread t = new MyThread(); // State: NEW
[Link](); // State: RUNNABLE → RUNNING
[Link](); // Wait for thread to TERMINATE
[Link]("Thread state: " + [Link]()); // TERMINATED
}
}
Q8. Two Ways to Create a Thread in Java
Java provides two approaches to create and start a new thread: extending the Thread class or
implementing the Runnable interface.
Method 1 — Extending the Thread Class
Create a subclass of Thread and override the run() method. Call start() to begin execution.
class MyThread extends Thread {
public void run() {
for (int i = 1; i <= 3; i++)
[Link]("Thread-A: " + i);
}
}
public class ThreadExample1 {
public static void main(String[] args) {
MyThread t = new MyThread();
[Link](); // Invokes run() in a new thread
}
}
Output:
Thread-A: 1
Thread-A: 2
Thread-A: 3
Method 2 — Implementing the Runnable Interface
Implement the Runnable interface and pass the object to a Thread constructor. This approach is
preferred as Java supports only single inheritance, and using Runnable allows the class to extend
another class too.
class MyRunnable implements Runnable {
public void run() {
for (int i = 1; i <= 3; i++)
[Link]("Runnable Thread: " + i);
}
}
public class ThreadExample2 {
public static void main(String[] args) {
MyRunnable r = new MyRunnable();
Thread t = new Thread(r); // Pass Runnable to Thread
[Link]();
}
}
Output:
Runnable Thread: 1
Runnable Thread: 2
Runnable Thread: 3
Comparison
Aspect Extending Thread Implementing Runnable
Inheritance Cannot extend other class Can extend another class
Flexibility Less flexible More flexible (preferred)
Code Reuse Lower Higher
Usage new MyThread().start() new Thread(new
MyRunnable()).start()
Q9. Synchronization in Multithreading
When multiple threads access a shared resource simultaneously, data inconsistency can occur — this
is called a race condition. Synchronization is a mechanism that ensures only one thread accesses a
shared resource at a time, maintaining data integrity.
The synchronized Keyword
Java uses the synchronized keyword on methods or blocks. Only one thread can hold the lock on an
object at a time; other threads must wait.
Syntax (Synchronized Method):
synchronized void methodName() {
// critical section
}
Problem Without Synchronization (Race Condition)
class Counter {
int count = 0;
void increment() { count++; } // NOT thread-safe!
}
If two threads call increment() simultaneously, the count may be updated incorrectly due to interleaved
execution.
Program — Synchronization Demo
class BankAccount {
private int balance = 1000;
// synchronized — only one thread enters at a time
synchronized void withdraw(int amount) {
if (balance >= amount) {
[Link]([Link]().getName()
+ " withdrawing " + amount);
balance -= amount;
[Link]("Remaining balance: " + balance);
} else {
[Link]("Insufficient balance!");
}
}
}
public class SyncDemo {
public static void main(String[] args) {
BankAccount account = new BankAccount();
Thread t1 = new Thread(() -> [Link](700), "Thread-1");
Thread t2 = new Thread(() -> [Link](700), "Thread-2");
[Link]();
[Link]();
}
}
Output:
Thread-1 withdrawing 700
Remaining balance: 300
Insufficient balance!
Without synchronized, both threads might pass the balance check and cause the balance to go
negative — a classic race condition.
Q10. Inter-Thread Communication — wait(), notify(), notifyAll()
Inter-thread communication allows synchronized threads to communicate with each other about the
state of shared resources. Java provides three methods in the Object class for this: wait(), notify(), and
notifyAll().
Key Methods
Method Description
wait() Causes the current thread to release the lock and
wait until another thread calls notify() or notifyAll().
notify() Wakes up ONE thread that is waiting on the same
object's lock (chosen arbitrarily).
notifyAll() Wakes up ALL threads waiting on the same
object's lock. They then compete for the lock.
Important: These methods must be called from within a synchronized block/method, otherwise
IllegalMonitorStateException is thrown.
Classic Producer-Consumer Example
class SharedBox {
int item;
boolean hasItem = false;
synchronized void produce(int val) throws InterruptedException {
while (hasItem) {
wait(); // Wait until consumer takes the item
}
item = val;
hasItem = true;
[Link]("Produced: " + val);
notify(); // Notify the consumer
}
synchronized void consume() throws InterruptedException {
while (!hasItem) {
wait(); // Wait until producer puts an item
}
[Link]("Consumed: " + item);
hasItem = false;
notify(); // Notify the producer
}
}
public class ProducerConsumer {
public static void main(String[] args) {
SharedBox box = new SharedBox();
Thread producer = new Thread(() -> {
try {
for (int i = 1; i <= 3; i++) [Link](i);
} catch (InterruptedException e) { [Link](); }
});
Thread consumer = new Thread(() -> {
try {
for (int i = 1; i <= 3; i++) [Link]();
} catch (InterruptedException e) { [Link](); }
});
[Link]();
[Link]();
}
}
Output:
Produced: 1
Consumed: 1
Produced: 2
Consumed: 2
Produced: 3
Consumed: 3
Summary of Inter-Thread Communication
• wait() — Thread releases lock and waits passively.
• notify() — Wakes up one waiting thread (selected by JVM).
• notifyAll() — Wakes up all waiting threads; they compete for the lock.
• All three must be used inside synchronized blocks.
• They belong to Object class, not the Thread class.
Java Programming — University Notes | Exception Handling • I/O Streams • Multithreading