Unit 3 – Java
Programming
Exception Handling, Multithreading,
JavaBeans & Network Programming
A Complete Beginner-Friendly Guide with Code Examples
Department of AI & Data Sciences
Chandigarh Engineering College Jhanjeri
1. What is an Exception?
An exception is an unexpected event that disrupts the normal flow of a
program while it is running. For example, dividing a number by zero,
trying to open a file that doesn't exist, or accessing an element that is
out of bounds in an array.
Without exception handling, your program would just crash with a
scary error message. With exception handling, you can catch the error
and do something useful — like show a friendly message or try again.
Note: Think of an exception like a car breakdown. Without a plan,
you're stuck. Exception handling is your roadmap — it tells the program
what to do when things go wrong.
Benefits of Exception Handling
1. Maintains Normal Flow — the program doesn't crash abruptly
2. Separation of Logic and Error Code — cleaner, readable
programs
3. Grouping Error Types — handle file errors separately from math
errors
4. Propagation — an error can travel up the call stack to the right
handler
2. Exception Hierarchy in Java
Java organizes all exceptions in a hierarchy (a family tree). Everything
inherits from Throwable at the top:
[Link]
|
+── Error ← Serious JVM problems
(OutOfMemoryError, StackOverflowError)
| You should NOT try to
handle these.
|
+── Exception ← Problems your program CAN
handle
|
+── RuntimeException ← UNCHECKED (compiler
doesn't force you)
| ├── ArithmeticException (divide
by zero)
| ├── NullPointerException (null
reference)
| ├── ArrayIndexOutOfBoundsException
| └── NumberFormatException
|
+── IOException ← CHECKED (compiler
forces you to handle)
| └── FileNotFoundException
+── SQLException
+── ClassNotFoundException
Checked vs Unchecked Exceptions
Feature Checked Exception Unchecked Exception
When detected At compile time At runtime
Compiler forces YES — must use try- NO — optional
handling? catch or throws
Cause External factors (files, Logic bugs in your code
network, DB)
Examples IOException, ArithmeticException,
FileNotFoundException, NullPointerException,
SQLException ArrayIndexOutOfBoundsException
3. try, catch, finally, throw, throws
These 5 keywords are the building blocks of exception handling. Here
is what each one does:
Keyword Purpose Simple Analogy
try Wraps risky code that Try opening a jar
might throw an exception
catch Handles the exception if If the jar is stuck, use a
one occurs cloth to grip it
finally Always runs — used for Whether you opened it or
cleanup not, put it back on the
shelf
throw Manually create and throw You yourself signal that
an exception something is wrong
throws Warns callers that this Label on the jar saying
method might throw an 'may be difficult to open'
exception
3.1 try-catch Example
public class TryCatchDemo {
public static void main(String[] args) {
try {
int result = 10 / 0; // This causes
ArithmeticException
[Link](result);
} catch (ArithmeticException e) {
[Link]("Caught: " + [Link]());
}
[Link]("Program continues normally.");
}
}
Output:
Caught: / by zero
Program continues normally.
3.2 Multiple catch Blocks
You can have several catch blocks to handle different types of
exceptions:
public class MultipleCatch {
public static void main(String[] args) {
try {
int[] arr = new int[3];
arr[5] = 10; //
ArrayIndexOutOfBoundsException
} catch (ArithmeticException e) {
[Link]("Math error: " +
[Link]());
} catch (ArrayIndexOutOfBoundsException e) {
[Link]("Array error: " +
[Link]());
} catch (Exception e) {
[Link]("Some error: " +
[Link]());
}
}
}
Output:
Array error: Index 5 out of bounds for length 3
3.3 finally Block
The finally block always runs — whether an exception happened or
not. Use it to close files, database connections, etc.
public class FinallyDemo {
public static void main(String[] args) {
try {
[Link]("Trying something risky...");
int x = 5 / 0;
} catch (ArithmeticException e) {
[Link]("Error caught: " +
[Link]());
} finally {
[Link]("Finally block always runs —
cleanup done!");
}
}
}
Output:
Trying something risky...
Error caught: / by zero
Finally block always runs — cleanup done!
3.4 throw — Manually Throwing an Exception
Use throw when you want to deliberately signal that something is
wrong based on your own conditions:
public class ThrowDemo {
static void checkAge(int age) {
if (age < 18) {
throw new ArithmeticException("Access Denied: Age
is " + age);
} else {
[Link]("Access Granted! Welcome.");
}
}
public static void main(String[] args) {
try {
checkAge(15);
} catch (ArithmeticException e) {
[Link]("Caught: " + [Link]());
}
checkAge(20);
}
}
Output:
Caught: Access Denied: Age is 15
Access Granted! Welcome.
3.5 throws — Warning Callers
throws in a method signature says: 'This method might throw an
exception — the caller must handle it.'
import [Link].*;
public class ThrowsDemo {
// 'throws' warns that this method might cause an
IOException
static void readFile(String name) throws IOException {
throw new IOException("File not found: " + name);
}
public static void main(String[] args) {
try {
readFile("[Link]");
} catch (IOException e) {
[Link]("Handled: " + [Link]());
}
}
}
Output:
Handled: File not found: [Link]
4. Creating Your Own Exception (Custom
Exception)
You can create your own exception class by extending Exception (for
checked) or RuntimeException (for unchecked). This lets you give your
error a meaningful name and message.
// Step 1: Create your custom exception class
class InvalidAgeException extends Exception {
// Constructor accepts a message
public InvalidAgeException(String message) {
super(message); // pass message to parent Exception
class
}
}
// Step 2: Use it in your program
public class CustomExceptionDemo {
static void validateAge(int age) throws
InvalidAgeException {
if (age < 0 || age > 150) {
throw new InvalidAgeException("Invalid age: " +
age + ". Must be 0-150.");
}
[Link]("Valid age: " + age);
}
public static void main(String[] args) {
try {
validateAge(25); // valid
validateAge(-5); // throws our custom
exception
} catch (InvalidAgeException e) {
[Link]("Custom Exception caught: " +
[Link]());
} finally {
[Link]("Validation complete.");
}
}
}
Output:
Valid age: 25
Custom Exception caught: Invalid age: -5. Must be 0-150.
Validation complete.
5. What is Multithreading?
Multithreading is Java's ability to run multiple tasks at the same time
within the same program. Instead of doing things one by one, multiple
parts run simultaneously, making your program faster and more
efficient.
Note: Restaurant analogy: A single-threaded program is one chef doing
everything (chopping, cooking, plating) one at a time. Multithreaded is
multiple chefs working simultaneously — the meal is ready much faster!
Process vs Thread
Feature Process Thread
Definition A program in execution A smaller unit inside a
(e.g., Chrome browser) process (e.g., one
Chrome tab)
Memory Has its own separate Shares memory with other
memory space threads in the same
process
Weight Heavyweight — Lightweight — cheap and
expensive to create fast to create
Communication Hard — processes can't Easy — threads share the
easily share data same memory
Example Opening MS Word is a Spell-check and auto-
Process save are Threads inside
Word
6. Thread Life Cycle
Every thread goes through a series of states during its life.
Understanding this helps you write better multithreaded code.
┌─────────┐ start() ┌──────────┐ CPU picks it
┌─────────┐
│ NEW │ ───────────► │ RUNNABLE │ ──────────────► │
RUNNING │
└─────────┘ └──────────┘
└────┬────┘
(created but (ready,
│
not started) waiting for CPU)
│ sleep()/wait()/
│ I/O block
┌────────────────────────────────────┐
│ BLOCKED / WAITING
│
│ (alive but waiting for
resource │
│ or notification)
│
└──────────────┬─────────────────────┘
│
notify()/interrupt()/
│ resource
available
v
┌────────────┐
│ TERMINATED │
│ (finished) │
└────────────┘
State When? Example
New Thread object created, Thread t = new Thread()
start() not called yet
Runnable start() called — waiting for [Link]()
the CPU to pick it up
Running CPU is executing the Inside run() currently
thread's run() method
Blocked/Waiting Waiting for a resource, After sleep(), wait(), or I/O
lock, or notification
Terminated run() method has finished After the last line of run()
7. Creating Threads — Two Methods
Java gives you two ways to create a thread. Both work, but
implementing Runnable is preferred because Java only allows
extending one class, and you may need to extend another.
Method A: Extending the Thread Class
class MyThread extends Thread {
String name;
MyThread(String name) {
[Link] = name;
}
// run() contains the code that executes in the thread
public void run() {
for (int i = 1; i <= 3; i++) {
[Link](name + " → Count: " + i);
}
}
}
public class ThreadExtendDemo {
public static void main(String[] args) {
MyThread t1 = new MyThread("Thread-A");
MyThread t2 = new MyThread("Thread-B");
[Link](); // DO NOT call run() directly — always
use start()
[Link]();
}
}
Output:
Thread-A → Count: 1
Thread-B → Count: 1
Thread-A → Count: 2
Thread-B → Count: 2
Thread-A → Count: 3
Thread-B → Count: 3
(Note: actual order may vary — threads run concurrently!)
Method B: Implementing the Runnable Interface
(Preferred)
class MyTask implements Runnable {
String name;
MyTask(String name) {
[Link] = name;
}
public void run() {
for (int i = 1; i <= 3; i++) {
[Link](name + " → Step: " + i);
}
}
}
public class RunnableDemo {
public static void main(String[] args) {
// Wrap the Runnable inside a Thread object
Thread t1 = new Thread(new MyTask("Task-1"));
Thread t2 = new Thread(new MyTask("Task-2"));
[Link]();
[Link]();
}
}
Output:
Task-1 → Step: 1
Task-2 → Step: 1
Task-1 → Step: 2
Task-2 → Step: 2
Task-1 → Step: 3
Task-2 → Step: 3
extends Thread implements
Runnable
Can extend another NO — Java doesn't allow YES — free to extend any
class? multiple inheritance other class
Recommended? Simple scenarios only YES — preferred for real-
world code
How to start new MyThread().start() new Thread(new
MyTask()).start()
8. Interrupting Threads
You can interrupt a sleeping or waiting thread using interrupt(). The
thread wakes up and an InterruptedException is thrown, which you can
catch and handle.
public class InterruptDemo {
public static void main(String[] args) throws
InterruptedException {
Thread t1 = new Thread(() -> {
try {
[Link]("Thread sleeping for 5
seconds...");
[Link](5000); // sleep for 5 seconds
[Link]("Thread woke up
normally.");
} catch (InterruptedException e) {
// This runs when interrupt() wakes us up
[Link]("Thread was interrupted!
Stopping.");
}
});
[Link]();
[Link](1000); // main thread waits 1 second
[Link](); // interrupt t1 after 1 second
}
}
Output:
Thread sleeping for 5 seconds...
Thread was interrupted! Stopping.
9. Thread Priorities
Java assigns every thread a priority from 1 (lowest) to 10 (highest).
The default is 5. Higher priority threads get more CPU time — but this
is just a hint, not a guarantee.
Constant Value Meaning
Thread.MIN_PRIORITY 1 Lowest priority
Thread.NORM_PRIORITY 5 Default (normal) priority
Thread.MAX_PRIORITY 10 Highest priority
public class PriorityDemo {
public static void main(String[] args) {
Thread low = new Thread(() -> {
for (int i = 0; i < 3; i++)
[Link]("Low Priority: " + i);
});
Thread high = new Thread(() -> {
for (int i = 0; i < 3; i++)
[Link]("HIGH Priority: " + i);
});
[Link](Thread.MIN_PRIORITY); // priority =
1
[Link](Thread.MAX_PRIORITY); // priority =
10
[Link]("Low priority: " +
[Link]());
[Link]("High priority: " +
[Link]());
[Link]();
[Link]();
}
}
Output:
Low priority: 1
High priority: 10
HIGH Priority: 0
HIGH Priority: 1
HIGH Priority: 2
Low Priority: 0
Low Priority: 1
Low Priority: 2
10. Synchronizing Threads
When two or more threads access the same shared data at the same
time, the data can get corrupted. This is called a Race Condition. The
synchronized keyword prevents this by allowing only ONE thread at a
time to execute a critical section.
Note: Bank account analogy: If two people withdraw money at the exact
same moment, the balance could go wrong. synchronized is like a lock
on the ATM — only one person at a time.
Without Synchronization — Problem
class Counter {
int count = 0;
void increment() { count++; } // NOT synchronized —
dangerous!
}
public class NoSyncDemo {
public static void main(String[] args) throws
InterruptedException {
Counter c = new Counter();
Thread t1 = new Thread(() -> { for (int i=0; i<1000;
i++) [Link](); });
Thread t2 = new Thread(() -> { for (int i=0; i<1000;
i++) [Link](); });
[Link](); [Link]();
[Link](); [Link]();
[Link]("Expected 2000, Got: " + [Link]);
}
}
Output:
Expected 2000, Got: 1847 ← WRONG! Race condition happened
With Synchronization — Fixed
class SyncCounter {
int count = 0;
synchronized void increment() { count++; } // only one
thread at a time
}
public class SyncDemo {
public static void main(String[] args) throws
InterruptedException {
SyncCounter c = new SyncCounter();
Thread t1 = new Thread(() -> { for (int i=0; i<1000;
i++) [Link](); });
Thread t2 = new Thread(() -> { for (int i=0; i<1000;
i++) [Link](); });
[Link](); [Link]();
[Link](); [Link]();
[Link]("Count: " + [Link]);
}
}
Output:
Count: 2000 ← CORRECT!
11. Inter-Thread Communication (wait / notify /
notifyAll)
Sometimes threads need to talk to each other — for example, one
thread produces data and another consumes it. Java provides three
methods from the Object class for this:
Method What it does
wait() Current thread gives up the lock and
goes to sleep until notified
notify() Wakes up ONE thread that is waiting on
this object's lock
notifyAll() Wakes up ALL threads waiting on this
object's lock
Note: These methods must be called inside a synchronized block,
otherwise you get an IllegalMonitorStateException.
Producer-Consumer Problem
The classic example: a Producer thread creates items and puts them
in a buffer; a Consumer thread takes items from the buffer. They must
coordinate — Consumer must wait if buffer is empty; Producer must
wait if buffer is full.
class Buffer {
int data;
boolean hasData = false;
// Producer calls this to put data
synchronized void produce(int val) throws
InterruptedException {
while (hasData) {
wait(); // wait if buffer already has
unread data
}
data = val;
hasData = true;
[Link]("Produced: " + val);
notify(); // wake up the Consumer
}
// Consumer calls this to take data
synchronized void consume() throws InterruptedException {
while (!hasData) {
wait(); // wait if there is no data yet
}
[Link]("Consumed: " + data);
hasData = false;
notify(); // wake up the Producer
}
}
public class ProducerConsumerDemo {
public static void main(String[] args) {
Buffer buf = new Buffer();
Thread producer = new Thread(() -> {
try {
for (int i = 1; i <= 3; i++) [Link](i);
} catch (InterruptedException e) {}
});
Thread consumer = new Thread(() -> {
try {
for (int i = 1; i <= 3; i++) [Link]();
} catch (InterruptedException e) {}
});
[Link]();
[Link]();
}
}
Output:
Produced: 1
Consumed: 1
Produced: 2
Consumed: 2
Produced: 3
Consumed: 3
12. Introduction to JavaBeans
A JavaBean is a reusable, self-contained Java class that follows
specific conventions. Frameworks like Spring, JSP, and Hibernate use
JavaBeans to represent data objects (like a Student, Product, User,
etc.).
Note: Think of a JavaBean like a standard-shaped Lego brick. Because
it follows standard rules, any tool or framework can plug into it and use
it without needing to know the details.
The 3 Golden Rules of a JavaBean
5. Private instance variables — data is hidden (encapsulation)
6. Public getter and setter methods — to read and write the data
7. Public no-argument constructor — allows frameworks to create
instances automatically
JavaBean Example
// A JavaBean representing a Student
public class Student {
// Rule 1: Private variables
private int id;
private String name;
private double marks;
// Rule 3: No-argument constructor
public Student() {}
// Optional: Constructor with parameters
public Student(int id, String name, double marks) {
[Link] = id;
[Link] = name;
[Link] = marks;
}
// Rule 2: Public Getters (read private data)
public int getId() { return id; }
public String getName() { return name; }
public double getMarks() { return marks; }
// Rule 2: Public Setters (write private data)
public void setId(int id) { [Link] = id; }
public void setName(String name) { [Link] = name; }
public void setMarks(double marks) { [Link] =
marks; }
}
// Using the JavaBean
public class JavaBeanDemo {
public static void main(String[] args) {
// Create using no-arg constructor + setters
Student s1 = new Student();
[Link](101);
[Link]("Alice");
[Link](88.5);
// Create using parameterized constructor
Student s2 = new Student(102, "Bob", 75.0);
// Read using getters
[Link]("ID: " + [Link]() + ", Name: " +
[Link]() + ", Marks: " + [Link]());
[Link]("ID: " + [Link]() + ", Name: " +
[Link]() + ", Marks: " + [Link]());
}
}
Output:
ID: 101, Name: Alice, Marks: 88.5
ID: 102, Name: Bob, Marks: 75.0
JavaBean Rule Why it matters
Private variables Protects data — external code can't
directly change it
Public getters/setters Controlled access — you can add
validation in setters
No-arg constructor Frameworks (Spring, JSP) can create
objects automatically using reflection
13. Introduction to Network Programming
Network programming lets two computers communicate over a
network (like the internet or a local network). Java's [Link] package
provides the tools to build client-server applications.
Key Terms You Must Know
Term Meaning Example
IP Address Unique number identifying [Link], [Link]
a device on a network (localhost = your own
machine)
Port Number Identifies which Port 80 (HTTP), Port 3306
application on a device to (MySQL), Port 8080
talk to (Tomcat)
Socket One end of a two-way Like a phone — you need
communication link two to make a call
ServerSocket A socket on the server Like a receptionist waiting
waiting for incoming for clients
connections
TCP vs UDP
Feature TCP (Transmission UDP (User Datagram
Control Protocol) Protocol)
Reliability Guaranteed delivery — No guarantee — fire and
checks if data arrived forget
Speed Slower (overhead from Faster (no checks)
checks)
Connection Connection-oriented Connectionless (just
(handshake first) send)
Analogy Phone call — you confirm Postcard — you send it
the other person heard but don't know if it arrives
you
Use cases Web browsing, file Video streaming, online
transfer, email, chat gaming, DNS, VoIP
Java class Socket + ServerSocket DatagramSocket +
DatagramPacket
TCP Client-Server: How it Works
SERVER CLIENT
──────────────────────────────────────────────────────
1. Create ServerSocket(port) 1. Create
Socket(serverIP, port)
2. [Link]() 2. Connect to server
(waits for client...)
3. Get input/output streams 3. Get input/output
streams
4. Read client message 4. Send message to
server
5. Send reply 5. Read server's reply
6. Close socket 6. Close socket
TCP Server Example
import [Link].*;
import [Link].*;
public class SimpleServer {
public static void main(String[] args) throws IOException
{
// Step 1: Create server socket on port 5000
ServerSocket server = new ServerSocket(5000);
[Link]("Server started. Waiting for
client...");
// Step 2: Wait for a client to connect
Socket client = [Link]();
[Link]("Client connected!");
// Step 3: Set up streams
BufferedReader in = new BufferedReader(
new InputStreamReader([Link]()));
PrintWriter out = new
PrintWriter([Link](), true);
// Step 4: Read message from client
String message = [Link]();
[Link]("Client says: " + message);
// Step 5: Send reply
[Link]("Hello Client! I got: " + message);
// Step 6: Close
[Link]();
[Link]();
}
}
TCP Client Example
import [Link].*;
import [Link].*;
public class SimpleClient {
public static void main(String[] args) throws IOException
{
// Step 1: Connect to server at localhost port 5000
Socket socket = new Socket("localhost", 5000);
[Link]("Connected to server!");
// Step 2: Set up streams
PrintWriter out = new
PrintWriter([Link](), true);
BufferedReader in = new BufferedReader(
new InputStreamReader([Link]()));
// Step 3: Send message to server
[Link]("Hi Server!");
// Step 4: Read server's reply
String reply = [Link]();
[Link]("Server says: " + reply);
[Link]();
}
}
Output:
SERVER output: CLIENT output:
Server started. Waiting for Connected to server!
client... Server says: Hello
Client!
Client connected! I got: Hi Server!
Client says: Hi Server!
UDP Example — Sending a Datagram
UDP doesn't need a connection — you just send packets. Faster but
no guarantee of delivery.
// UDP Sender
import [Link].*;
public class UDPSender {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket();
byte[] data = "Hello UDP!".getBytes();
InetAddress address =
[Link]("localhost");
DatagramPacket packet = new DatagramPacket(data,
[Link], address, 6000);
[Link](packet);
[Link]("UDP message sent!");
[Link]();
}
}
// UDP Receiver
import [Link].*;
public class UDPReceiver {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket(6000);
byte[] buffer = new byte[256];
DatagramPacket packet = new DatagramPacket(buffer,
[Link]);
[Link](packet);
String msg = new String([Link](), 0,
[Link]());
[Link]("Received: " + msg);
[Link]();
}
}
Output:
Sender: UDP message sent!
Receiver: Received: Hello UDP!
Quick Reference Summary – Unit 3
Topic Key Concept / Keyword In One Line
Exception try-catch-finally Wrap risky
code in try,
handle errors
in catch
Checked IOException, SQLException Must handle at
Exception compile time
Unchecked ArithmeticException, NullPointerException Runtime bugs
Exception — optional to
handle
throw throw new Exception() Manually
trigger an
exception
throws void method() throws Ex Warn callers
this method
may throw
Custom extends Exception Create your
Exception own
meaningful
exception type
Multithreading Multiple threads in one process Run multiple
tasks at the
same time
Thread Lifecycle New→Runnable→Running→Blocked→Termina Five states
ted every thread
passes
through
Extend Thread class T extends Thread { run() } Method A to
create a
thread
Implement class T implements Runnable { run() } Method B —
Runnable preferred
approach
[Link](m Pauses thread for milliseconds Used to
s) simulate time
delays
interrupt() [Link]() Wake up a
sleeping/waitin
g thread
setPriority(n) Values 1-10, default 5 Suggest CPU
to run this
thread more
often
synchronized synchronized void method() One thread at
a time —
prevents race
conditions
wait() [Link]() Give up lock,
sleep until
notify()
notify() [Link]() Wake up one
waiting thread
Producer- wait()+notify() pattern Classic
Consumer synchronizatio
n problem
JavaBean 3 rules: private, get/set, no-arg constructor Standard
reusable data
class
Network Prog [Link] package Tools for
TCP/UDP
client-server
communicatio
n
TCP Socket + ServerSocket Reliable,
connection-
based
communicatio
n
UDP DatagramSocket + DatagramPacket Fast,
connectionless
, no delivery
guarantee
IP Address 192.168.x.x / [Link] Unique ID of a
device on the
network
Port 0-65535 (e.g., 8080, 3306) Identifies
which app to
talk to on a
device