UNIT 3 - OOPS - Notes
UNIT 3 - OOPS - Notes
UNIT 3 - OOPS - Notes
Exception Handling basics – Multiple catch Clauses – Nested try Statements – Java’s Built-in
Exceptions – User defined Exception. Multithreaded Programming: Java Thread Model–
Creating a Thread and Multiple Threads – Priorities – Synchronization – Inter Thread
Communication- Suspending –Resuming, and Stopping Threads –Multithreading. Wrappers
– Auto boxing.
Exception is an abnormal condition. An exception can occur for many reasons. Some of them
are:
In Java, an exception is an event that disrupts the normal flow of the program. It is an object
which is thrown at runtime. Exception Handling is a mechanism to handle runtime errors
such as ClassNotFoundException, IOException, SQLException, RemoteException, etc.
statement 1;
statement 2;
statement 3;
statement 4;
statement 5;//exception occurs
statement 6;
statement 7;
statement 8;
statement 9;
statement 10;
Suppose there are 10 statements in a Java program and an exception occurs at statement 5;
the rest of the code will not be executed, i.e., statements 6 to 10 will not be executed.
However, when we perform exception handling, the rest of the statements will be executed.
That is why we use exception handling in Java.
SCE/CSBS/CS3391/VS 1
As you can see from
om the image above, the Throwable class is the root class in the hierarchy.
There are mainly two types of exceptions: checked and unchecked. An error is considered as
thee unchecked exception. However, there are three types of exceptions namely:
1. Checked Exception
2. Unchecked Exception
3. Error
The classes that directly inherit the Throwable class except RuntimeException and Error are
known as checked exceptions. For example, IOException, SQLException, etc. Checked
exceptions are checked at compile-time.
compile
2) Unchecked Exception
The classes that inherit the RuntimeException are known as unchecked exceptions. For
example, ArithmeticException,
ithmeticException, NullPointerException, ArrayIndexOutOfBoundsException,
etc. Unchecked exceptions are not checked at compile-time,
compile time, but they are checked at runtime.
3) Error
SCE/CSBS/CS3391/VS 2
Java Exception Keywords
Java provides five keywords that are used to handle the exception. The following table
describes each.
Keyword Description
try The "try" keyword is used to specify a block where we should place an
exception code. It means we can't use try block alone. The try block must
be followed by either catch or finally.
catch The "catch" block is used to handle the exception. It must be preceded by
try block which means we can't use catch block alone. It can be followed
by finally block later.
finally The "finally" block is used to execute the necessary code of the program.
It is executed whether an exception is handled or not.
SCE/CSBS/CS3391/VS 3
1. Java try...catch block
The try-catch block is used to handle exceptions in Java. Here's the syntax
of try...catch block:
try {
// code
}
catch(Exception e) {
// code
}
Here, we have placed the code that might generate an exception inside the try block.
Every try block is followed by a catch block.
When an exception occurs, it is caught by the catch block. The catch block cannot be used
without the try block.
Example: Exception handling using try...catch
class Main {
public static void main(String[] args) {
try {
catch (ArithmeticException e) {
System.out.println("ArithmeticException => " + e.getMessage());
}
}
}
Output
ArithmeticException => / by zero
In the example, we are trying to divide a number by 0. Here, this code generates an
exception.
To handle the exception, we have put the code, 5 / 0 inside the try block. Now when an
exception occurs, the rest of the code inside the try block is skipped.
SCE/CSBS/CS3391/VS 4
The catch block catches the exception and statements inside the catch block is executed.
If none of the statements in the try block generates an exception, the catch block is skipped.
catch (ArithmeticException e) {
System.out.println("ArithmeticException => " + e.getMessage());
}
finally {
System.out.println("This is the finally block");
}
}
}
Output
SCE/CSBS/CS3391/VS 5
ArithmeticException => / by zero
This is the finally block
In the above example, we are dividing a number by 0 inside the try block. Here, this code
generates an ArithmeticException.
The exception is caught by the catch block. And, then the finally block is executed.
Note: It is a good practice to use the finally block. It is because it can include important
cleanup codes like,
code that might be accidentally skipped by return, continue or break
closing a file or connection
// throw an exception
throw new ArithmeticException("Trying to divide by 0");
}
class Main {
SCE/CSBS/CS3391/VS 6
// declareing the type of exception
public static void findFile() throws IOException {
For each try block, there can be zero or more catch blocks. Multiple catch blocks allow us to
handle each exception differently.
The argument type of each catch block indicates the type of exception that can be handled by
it. For example,
class ListOfNumbers {
public int[] arr = new int[10];
try {
arr[10] = 11;
}
SCE/CSBS/CS3391/VS 7
}
}
}
class Main {
public static void main(String[] args) {
ListOfNumbers list = new ListOfNumbers();
list.writeList();
}
}
Output
IndexOutOfBoundsException => Index 10 out of bounds for length 10
In this example, we have created an integer array named arr of size 10.Since the array index
starts from 0, the last element of the array is at arr[9]. Notice the statement,
arr[10] = 11;
Here, we are trying to assign a value to the index 10.
Hence, IndexOutOfBoundException occurs.
When an exception occurs in the try block,
The exception is thrown to the first catch block. The first catch block does not handle
an IndexOutOfBoundsException, so it is passed to the next catch block.
The second catch block in the above example is the appropriate exception handler
because it handles an IndexOutOfBoundsException. Hence, it is executed.
SCE/CSBS/CS3391/VS 8
// code
} catch (ExceptionType1 | Exceptiontype2 ex) {
// catch block
}
In Java, using a try block inside another try block is permitted. It is called as nested try block.
Every statement that we enter a statement in try block, context of that exception is pushed
onto the stack.
For example, the inner try block can be used to
handle ArrayIndexOutOfBoundsException while the outer try block can handle
the ArithemeticException (division by zero).
Why use nested try block? Sometimes a situation may arise where a part of a block may
cause one error and the entire block itself may cause another error. In such cases, exception
handlers have to be nested.
Syntax:
SCE/CSBS/CS3391/VS 9
....
//main try block
try
{
statement 1;
statement 2;
//try catch block within another try block
try
{
statement 3;
statement 4;
//try catch block within nested try block
try
{
statement 5;
statement 6;
}
catch(Exception e2)
{
//exception message
}
}
catch(Exception e1)
{
//exception message
}
}
//catch block of parent (outer) try block
catch(Exception e3)
SCE/CSBS/CS3391/VS 10
{
//exception message
}
....
Java Nested try Example
Example 1
Let's see an example where we place a try block within another try block for two different
exceptions.
NestedTryBlock.java
public class NestedTryBlock{
public static void main(String args[]){
//outer try block
try{
//inner try block 1
try{
System.out.println("going to divide by 0");
int b =39/0;
}
//catch block of inner try block 1
catch(ArithmeticException e)
{
System.out.println(e);
}
System.out.println("other statement");
}
SCE/CSBS/CS3391/VS 11
//catch block of outer try block
catch(Exception e)
{
System.out.println("handled the exception (outer catch)");
}
System.out.println("normal flow..");
}
}
Output:
D:\JavaCode>java NestedTryBlock
going to divide by 0
java.lang.ArithmeticException: / by zero
java.lang.ArrayIndexOutOfBoundsException: 5
other statement
normal flow..
SCE/CSBS/CS3391/VS 12
parent class Exception using the super() method. Also the constructor of Exception class can
be called without using a parameter and calling super() method is not mandatory.
TestCustomException1.java
// class representing custom exception
class InvalidAgeException extends Exception
{
public InvalidAgeException (String str)
{
// calling the constructor of parent Exception
super(str);
}
}
// main method
public static void main(String args[])
{
try
{
// calling the method
validate(13);
}
catch (InvalidAgeException ex)
{
System.out.println("Caught the exception");
SCE/CSBS/CS3391/VS 13
System.out.println("rest of the code...");
}
}
Output
D:\JavaCode>java TestCustomException1
Caught the exception
Exception occured: InvalidAgeException: age is not valid to vote
rest of the code...
SCE/CSBS/CS3391/VS 14
Thread in Java
A thread in Java is the direction or path that is taken while a program is being executed.
Generally, all the programs have at least one thread, known as the main thread, that is provided
by the JVM or Java Virtual Machine at the starting of the program’s execution. At this point,
when the main thread
ead is provided, the main() method is invoked by the main thread.
In order to perform complicated tasks in the background, we used the Thread concept in Java. Java
All the tasks are executed without affecting the main program. In a program or process, all the
threads have their own separate path for execution, so each thread of a pro
process
cess is independent.
Another benefit of using thread is that if a thread gets an exception or an error at the time of its
execution, it doesn't affect the execution of the other threads. All the threads share a common
memory and have their own stack, loc local
al variables and program counter. When multiple threads
are executed in parallel at the same time, this process is known as Multithreading.
Multithreading
SCE/CSBS/CS3391/VS 15
o Series of executed statements.
o Nested sequence of method calls.
Thread Model
Just like a process, a thread exists in several states. These states are as follows:
2) Running
3) Suspended
4) Blocked
A thread is in the Blocked state when it is waiting for resources.
5) Terminated
A thread comes in this state when at any given time, it halts its execution immediately.
SCE/CSBS/CS3391/VS 16
Creating Thread
A thread is created either by "creating or implementing" the Runnable Interface or by
extending the Thread class. These are the only two ways through which we can create a thread.
Method1: using Thread Class
A Thread class has several methods and constructors which allow us to perform various
operations on a thread. The Thread class extends the Object class. The Object class implements
the Runnable interface. The thread class has the following constructors that are used to perform
various operations.
o Thread()
o Thread(Runnable, String name)
o Thread(Runnable target)
o Thread(ThreadGroup group, Runnable target, String name)
o Thread(ThreadGroup group, Runnable target)
o Thread(ThreadGroup group, String name)
o Thread(ThreadGroup group, Runnable target, String name, long stackSize)
Method 2 using Runnable Interface(run() method)
The Runnable interface is required to be implemented by that class whose instances are intended
to be executed by a thread. The runnable interface gives us the run() method to perform an
action for the thread.
start() method
The method is used for starting a thread that we have newly created. It starts a new thread with a
new callstack. After executing the start() method, the thread changes the state from New to
Runnable. It executes the run() method when the thread gets the correct time to execute it.
Let's take an example to understand how we can create a Java
thread by extending the Thread class:
ThreadExample1.java
// Implementing runnable interface by extending Thread class
public class ThreadExample1 extends Thread {
// run() method to perform action for thread.
public void run()
{
int a= 10;
int b=12;
int result = a+b;
SCE/CSBS/CS3391/VS 17
System.out.println("Thread started running..");
System.out.println("Sum of two numbers is: "+ result);
}
public static void main( String args[] )
{
// Creating instance of the class extend Thread class
ThreadExample1 t1 = new ThreadExample1();
//calling start method to execute the run() method of the Thread class
t1.start();
}
}
Output:
E:\Java_MAT\pgms>java ThreadExample1
Thread started running..
Sum of two numbers is: 22
SCE/CSBS/CS3391/VS 18
}
public void run() {
try {
for(int j = 5; j > 0; j--) {
System.out.println(name + ": " + j);
Thread.sleep(1000);
}
}catch (InterruptedException e) {
System.out.println(name + " thread Interrupted");
}
System.out.println(name + " thread exiting.");
}
}
class ThreadExample2 {
public static void main(String args[]) {
new NewThread("1st");
new NewThread("2nd");
new NewThread("3rd");
try {
Thread.sleep(8000);
} catch (InterruptedException excetion) {
System.out.println("Inturruption occurs in Main Thread");
}
System.out.println("We are exiting from Main Thread");
}
}
Java Thread Methods
SCE/CSBS/CS3391/VS 19
void start() It is used to start the execution of the thread.
SCE/CSBS/CS3391/VS 20
void setDaemon() It marks the thread as daemon or user thread.
static boolean interrupted() It tests whether the current thread has been
interrupted.
static boolean holdLock() It returns true if and only if the current thread
holds the monitor lock on the specified object.
SCE/CSBS/CS3391/VS 21
void notifyAll() It is used to give the notification to all waiting
threads of a particular object.
SCE/CSBS/CS3391/VS 22
Working of the Java Thread Scheduler
Let's understand the working of the Java thread scheduler. Suppose, there are five threads that
have different arrival times and different priorities. Now, it is the responsibility of the thread
scheduler to decide which thread will get the CPU first.The thread scheduler selects the thread that
has the highest priority, and the thread begins the execution of the job. If a thread is already in runnable
state and another thread (that has higher priority) reaches in the runnable state, then the current
thread is pre-empted
empted from the processor, and the arrived thread with higher priority gets the CPU time.
When two threads (Thread 2 and Thread 3) having the same priorities and arrival time, the
scheduling will be decided on the basis of FCFS algorithm. Thus, the thread that arrives first gets
the opportunity to execute first.
What if we call Java run() method directly instead start() method?
o Each thread starts in a separate call stack.
o Invoking the run() method from the main thread, the run() method goes onto the current
call stack rather than at the beginning of a new call stack.
SCE/CSBS/CS3391/VS 23
public static void main(String args[]){
TestCallRun1 t1=new TestCallRun1();
t1.run();//fine, but does not start a separate call stack
}
}
Output:
running...
t1.run();
t2.run();
}
}
SCE/CSBS/CS3391/VS 24
Java join() method
The join() method in Java is provided by the java.lang.Thread class that permits one thread to
wait until the other thread to finish its execution. Suppose th be the object the class
t1.start();
t2.start();
t3.start();
SCE/CSBS/CS3391/VS 25
t2.join();
t3.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
@Override
public void run() {
System.out.println("Thread started:::"+Thread.currentThread().getName());
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread ended:::"+Thread.currentThread().getName());
}
The goal of the program is to make sure main is the last thread to finish and third thread starts
only when first one is dead.
Output is :
Thread started:::t1
Thread started:::t2
Thread ended:::t1
Thread started:::t3
Thread ended:::t2
Thread ended:::t3
All threads are dead, exiting main thread
SCE/CSBS/CS3391/VS 26
Naming Thread
The Thread class provides methods to change and get the name of a thread. By default, each
thread has a name, i.e. thread-0, thread-1 and so on.
Way 1: By we can change the name of the thread by using the setName() method. The
syntax of setName() and getName() methods are given below:
Way 2: We can also set the name of a thread directly when we create a new thread using
the constructor of the class.
Example:
class TestMultiNaming1 extends Thread{
public void run(){
System.out.println("running...");
}
public static void main(String args[]){
TestMultiNaming1 t1=new TestMultiNaming1();
TestMultiNaming1 t2=new TestMultiNaming1();
System.out.println("Name of t1:"+t1.getName());
System.out.println("Name of t2:"+t2.getName());
t1.start();
t2.start();
t1.setName("Saranathan");
System.out.println("After changing name of t1:"+t1.getName());
}
}
Output is:
Name of t1:Thread-0
Name of t2:Thread-1
SCE/CSBS/CS3391/VS 27
After changing name of t1:Saranathan
running...
running...
we can also name a thread during the thread creration
Example as follows:
import java.io.*;
SCE/CSBS/CS3391/VS 28
System.out.println("Thread - 2: " + th2.getName());
Current Thread
t1.start();
t2.start();
}
}
Output:
Thread-0
Thread-1
Thread priority in Java is a number assigned to a thread that is used by Thread scheduler to
decide which thread should be allowed to execute.
SCE/CSBS/CS3391/VS 29
In Java, each thread is assigned a different priority that will decide the order (preference) in
which it is scheduled for running.
Thread priorities are represented by a number from 1 to 10 that specifies the relative priority of
one thread to another. The thread with the highest priority is selected by the scheduler to be
executed first.
The default priority of a thread is 5. Thread class in Java also provides several priority constants
to define the priority of a thread. These are:
1. MIN_PRIORITY = 1
2. NORM_PRIORITY = 5
3. MAX_PRIORTY = 10
These constants are public, final, and static members of the Thread class.
Thread scheduler selects the thread for execution on the first-come, first-serve basis. That is, the
threads having equal priorities share the processor time on the first-come, first-serve basis.
When multiple threads are ready for execution, the highest priority thread is selected and
executed by JVM. In case when a high priority thread stops, yields, or enters into the blocked
state, a low priority thread starts executing.
If any high priority thread enters into the runnable state, it will preempt the currently running
thread forcing it to move to the runnable state. Note that the highest priority thread always
preempts any lower priority thread.
Let’s create a Java program in which we will determine the priority and name of the current
thread.
Program code:
SCE/CSBS/CS3391/VS 30
System.out.println("Priority of Thread: " +t.getPriority());
System.out.println("Name of Thread: " +t.getName());
t.start();
}
}
Output:
Priority of Thread: 5
Name of Thread: NewThread
Thread[NewThread,5,main]
-----------------
How to set Priority of Thread in Java?
The setPriority() of Thread class is used to set the priority of a thread. This method accepts an
integer value as an argument and sets that value as priority of a thread through which it is called.
The syntax to set the priority of a thread is as follows:
Syntax: ThreadName.setPriority(n);
Let’s create a Java program in which we will set the priority of a thread. Look at the program
source code.
Program code:
SCE/CSBS/CS3391/VS 31
Thread[NewThread,2,main]
-------------------
Another Example
t1.start();
t2.start();
t3.start();
}
}
Output:
Thread[Third Thread,8,main]
Thread[First Thread,4,main]
Thread[Second Thread,2,main]
The priority of Thread t1 is 4, t2 is 2, and t3 is 8. Thread t3 is the highest priority as compared to t1 and
t2. But it is not necessary that you will get the same priorities when you will run multiple times.
------------------------------
public class X implements Runnable
{
public void run()
{
System.out.println("Thread X started");
for(inti = 1; i<=4; i++)
{
System.out.println("Thread X: " +i);
}
System.out.println("Exit from X");
}
}
SCE/CSBS/CS3391/VS 32
public class Y implements Runnable
{
public void run()
{
System.out.println("Thread Y started");
for(int j = 0; j <= 4; j++)
{
System.out.println("Thread Y: " +j);
}
System.out.println("Exit from Y");
}
}
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(t2.getPriority() + 4);
t3.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
t3.start();
SCE/CSBS/CS3391/VS 33
}
}
Output:
Thread X started
Thread Z started
Thread X: 1
Thread Z: 0
Thread Y started
Thread Z: 1
Thread X: 2
Thread X: 3
Thread X: 4
Exit from X
Thread Z: 2
Thread Z: 3
Thread Y: 0
Thread Y: 1
Thread Y: 2
Thread Y: 3
Thread Y: 4
Exit from Y
Thread Z: 4
Exit from Z
--------------------------------------
The java instanceof operator is used to test whether the object is an instance of the specified
type (class or subclass or interface).
The instanceof in java is also known as type comparison operator because it compares the
instance with type. It returns either true or false. If we apply the instanceof operator with any
variable that has null value, it returns false.
class Simple1{
public static void main(String args[]){
Simple1 s=new Simple1();
System.out.println(s instanceof Simple1);//true
}
}
Output:true
SCE/CSBS/CS3391/VS 34
public static void main(String args[]){
Dog1 d=new Dog1();
System.out.println(d instanceof Animal);//true
}
}
Output:true
in Java are used to convert primitive types (int, char, float, etc) into corresponding objects.
class Main {
public static void main(String[] args) {
if(aObjinstanceof Integer) {
System.out.println("An object of Integer is created.");
}
SCE/CSBS/CS3391/VS 35
if(bObjinstanceof Double) {
System.out.println("An object of Double is created.");
}
}
}
Run Code
Output
In the above example, we have used the valueOf() method to convert the primitive types into
objects.
Here, we have used the instanceof operator to check whether the generated objects are of Integer
or Double type or not.
However, the Java compiler can directly convert the primitive types into corresponding objects.
For example,
int a = 5;
// converts into object
Integer aObj = a;
double b = 5.6;
// converts into object
Double bObj = b;
Java is an object-oriented programming language, so we need to deal with objects many times
like in Collections, Serialization, Synchronization, etc. Let us see the different scenarios, where
we need to use the wrapper classes.
o Change the value in Method: Java supports only call by value. So, if we pass a
primitive value, it will not change the original value. But, if we convert the primitive
value in an object, it will change the original value.
o Serialization: We need to convert the objects into streams to perform the serialization. If
we have a primitive value, we can convert it in objects through the wrapper classes.
o Synchronization: Java synchronization works with objects in Multithreading.
o java.util package: The java.util package provides the utility classes to deal with objects.
SCE/CSBS/CS3391/VS 36
o Collection Framework: Java collection framework works with objects only. All classes
of the collection framework (ArrayList, LinkedList, Vector, HashSet, LinkedHashSet,
TreeSet, PriorityQueue, ArrayDeque, etc.) deal with objects only.
The eight classes of the java.lang package are known as wrapper classes in Java. The list of eight
wrapper classes are given below:
int a = 56;
// autoboxing
Integer aObj = a;
Autoboxing has a great advantage while working with Java collections.
The automatic conversion of primitive data types into its equivalent Wrapper type is known as
boxing and opposite operation is known as unboxing.
class BoxingExample1{
public static void main(String args[]){
int a=50;
Integer a2=new Integer(a);//Boxing
Integer a3=5;//Boxing
System.out.println(a2+" "+a3);
}
}
Output:50 5
class UnboxingExample1{
public static void main(String args[]){
SCE/CSBS/CS3391/VS 37
Integer i=new Integer(50);
int a=i;
System.out.println(a);
}
}
Output:
50
Autoboxing can be performed with comparison operators. Let's see the example of boxing with
comparison operator:
class UnboxingExample2{
public static void main(String args[]){
Integer i=new Integer(50);
Output: 50
SCE/CSBS/CS3391/VS 38