unit 2 oop
unit 2 oop
Exception Handling
The exception handling in java is one of the powerful mechanism to handle the runtime
errors so that normal flow of the application can be maintained.
Java provides us facility to create our own exceptions which are basically derived classes of
Exception.
If an exception occurs, which has not been handled by programmer then program execution gets
terminated and a system generated error message is shown to the user. For example look at the
system generated exception below: An exception generated by the system is given below
What is an exception?
An Exception is an unwanted event that interrupts the normal flow of the program. When an
exception occurs program execution gets terminated. In such cases we get a system generated error
message. The good thing about exceptions is that they can be handled in Java. By handling the
exceptions we can provide a meaningful message to the user about the issue rather than a system
generated message, which may not be understandable to a user.
Hierarchy of Java Exception classes
The java.lang.Throwable class is the root class of Java Exception hierarchy inherited by two
subclasses: Exception and Error. The hierarchy of Java Exception classes is given below:
Difference between error and exception
Errors indicate that something severe enough has gone wrong, the application should crash rather
than try to handle the error. Exceptions are events that occurs in the code. A programmer can handle
such conditions and take necessary corrective actions.
Few examples: NullPointerException – When you try to use a reference that points to null.
ArithmeticException – When bad data is provided by user, for example, when you try to divide a
number by zero this exception occurs because dividing a number by zero is undefined.
ArrayIndexOutOfBoundsException – When you try to access the elements of an array out of its
bounds, for example array size is 5 (which means it has five elements) and you are trying to access
the 10th element.
Types of Exception There are mainly two types of exceptions: checked and unchecked where error
is considered as unchecked exception. The sun microsystem says there are three types of
exceptions:
1. Checked Exception
2. Unchecked Exception
3. Error
1. Checked Exception
All exceptions other than Runtime Exceptions are known as Checked exceptions as the compiler
checks them during compilation to see whether the programmer has handled them or not. If these
exceptions are not handled/declared in the program, you will get compilation error.
The classes that extend Throwable class except RuntimeException and Error
For example, SQLException, IOException, ClassNotFoundException etc.
2. Unchecked Exception
Runtime Exceptions are also known as Unchecked Exceptions. These exceptions are not checked at
compile-time so compiler does not check whether the programmer has handled them or not but it’s
the responsibility of the programmer to handle these exceptions and provide a safe exit.
The classes that extend RuntimeException
For example, ArithmeticException, NullPointerException, ArrayIndexOutOfBoundsException etc.
4. Error
Error is irrecoverable e.g. OutOfMemoryError, VirtualMachineError, AssertionError etc
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.
throw
The "throw" keyword is used to throw an exception.
throws
The "throws" keyword is used to declare exceptions. It specifies that there may occur an exception
in the method. It doesn't throw an exception. It is always used with method signature.
Output:
Exception in thread main java.lang.ArithmeticException:/ by zero
rest of the code...
There are given some scenarios where unchecked exceptions may occur. They are as follows:
int a=50/0;//ArithmeticException
2) A scenario where NullPointerException occurs
If we have a null value in any variable, performing any operation on the variable throws a
NullPointerException.
1. String s=null;
1. System.out.println(s.length());//NullPointerException
3) A scenario where NumberFormatException occurs
1. String s="abc";
2. int i=Integer.parseInt(s);//NumberFormatException
4) A scenario where ArrayIndexOutOfBoundsException occurs
When an array exceeds to it's size, the ArrayIndexOutOfBoundsException occurs. there may be
other reasons to occur ArrayIndexOutOfBoundsException.
A try block can be followed by one or more catch blocks. Each catch block must contain a different
exception handler. So, if you have to perform different tasks at the occurrence of different
exceptions, use java multi-catch block.
At a time only one exception occurs and at a time only one catch block is executed.
try{
int a[]=new int[5];
a[5]=30/0;
}
catch(ArithmeticException e)
{
System.out.println("Arithmetic Exception occurs");
}
catch(ArrayIndexOutOfBoundsException e)
{
System.out.println("ArrayIndexOutOfBounds Exception occurs");
}
catch(Exception e)
{
System.out.println("Parent Exception occurs");
}
System.out.println("rest of the code");
}
}
Output
Arithmetic Exception occurs
rest of the code
try{
int a[]=new int[5];
System.out.println(a[10]);
}
catch(ArithmeticException e)
{
System.out.println("Arithmetic Exception occurs");
}
catch(ArrayIndexOutOfBoundsException e)
{
System.out.println("ArrayIndexOutOfBounds Exception occurs");
}
catch(Exception e)
{
System.out.println("Parent Exception occurs");
}
System.out.println("rest of the code");
}
}
Output
ArrayIndexOutOfBounds Exception occurs
rest of the code
Java throw
The throw keyword in Java is used to explicitly throw an exception from a method or any block of
code. We can throw either checked or unchecked exception. The throw keyword is mainly used to
throw custom exceptions.
Syntax in Java
Example:
throw new ArithmeticException("/ by zero");
Example
3. }
Output:
Java throws
throws is a keyword in Java that is used in the signature of a method to indicate that this method
might throw one of the listed type exceptions. The caller to these methods has to handle the
exception using a try-catch block.
To prevent this compile time error we can handle the exception in two ways:
1. By using try catch
2. By using the throws keyword
1 The throw keyword is used inside a The throws keyword is used in the function
function. signature.
2 The throw keyword is used to throw an The throws keyword can be used to declare
exception explicitly. It can throw only one multiple exceptions, separated by a comma.
exception at a time Whichever exception occurs, if matched with
the declared ones, is thrown automatically
then.
However, we use multithreading than multiprocessing because threads use a shared memory area.
They don't allocate separate memory area so saves memory, and context-switching between the
threads takes less time than process.
1) It doesn't block the user because threads are independent and you can perform multiple
operations at the same time.
3) Threads are independent, so it doesn't affect other threads if an exception occurs in a single
thread.
Multitasking
A process is heavyweight.
Cost of communication between the process is high.
Switching from one process to another requires some time for saving and loading registers,
memory maps, updating lists, etc.
A thread is lightweight.
It has a single flow of control. It has a beginning, a body, and an end, and executes
commands sequentially
1. Newborn state
2. Runnable state
3. Running state
4. Blocked state
5. Dead state
Newborn State
When we create a thread object, the thread is born and is said to be in newborn state. At this state,
we can do only one of the following things with it:
Runnable State
The runnable state means that the thread is ready for execution and is waiting for the availability of
the processor. However, if we want a thread to relinquish control to another thread of equal priority
before its turn comes, we can do so by using the yield() method.
Running State
Running means that the processor has given its time to the thread for its execution. The thread runs
until it relinquishes control on its own or it is pre-empted by a higher priority thread.
A running thread may relinquish its control in one of the following situations.
1) It has been suspended by using suspend() method. A suspended thread can be revived by using
the resume() method. This approach is useful when we do not want to kill a thread but want to
suspend it for some time due to certain reason.
2) It has been made to sleep. We can make a thread to sleep for a specified time period using the
method sleep (time) where time is in milliseconds.
It means that the thread is out of the queue during this time period. As soon as this time period is
elapsed, the thread re-enters the runnable state.
3) It has been told to wait until some event occurs. It is done using the wait() method.
The thread can be scheduled to run again using the notify() method.
Blocked State
A thread is said to be in blocked state when it is prevented from entering the runnable state and
subsequently the running state.
It happens when the thread is suspended, sleeping or waiting in order to satisfy certain
requirements.
A blocked thread is considered “not runnable” but it is not dead and therefore fully qualified to run
again.
Dead State
Every thread has a life cycle. A running thread ends its life when it completes executing its run ()
method. It is a natural death.
However, we can kill it by sending the stop message to it at any state thus causing a premature
death. A thread can be killed as soon as it is born, or while it is running, or even when it is in “not
runnable” (blocked) condition.
that is, a thread can be defi ned by extending the java.lang.Thread class or by implementing the
java.lang.Runnable interface .
The run() method should be overridden and should contain the code that will be executed by the
new thread. This method must be public with a void return type and should not take any arguments.
The steps for creating a thread by using the first mechanism are:
1. Create a class by extending the Thread class and override the run() method:
/* ThreadEx1.java: A simple program creating and invoking a thread object by extending the
standard Thread class. */
class ThreadEx1
The steps for creating a thread by using the second mechanism are:
1. Create a class that implements the interface Runnable and override run() method:
{
public void run()
2.Creating Object:
4. Start Execution:
thr1.start();
An example program illustrating creation and invocation of a thread object is given below:
class ThreadEx2
{
public static void main(String [] args )
t.start();
Thread class
Thread class provide constructors and methods to create and perform operations on a thread.
● Thread()
● Thread(String name)
● Thread(Runnable r)
2. public void start(): starts the execution of the thread.JVM calls the run() method on the thread.
3. public void sleep(long miliseconds): Causes the currently executing thread to sleep (temporarily
cease execution) for the specified number of milliseconds.
4. public void join(): waits for a thread to die.
5. public void join(long miliseconds): waits for a thread to die for the specified miliseconds.
Runnable interface:
The Runnable interface should be implemented by any class whose instances are intended to be
executed by a thread.
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.
System.out.println("running...");
t1.start();
t2.start();
t1.setName("San");
● In most cases, thread schedular schedules the threads according to their priority (known as
preemptive scheduling).
● But it is not guaranteed because it depends on JVM specification that which scheduling it
chooses
m1.start();
m2.start();
Output:
System.out.println("Thread A started");
for(int i=1;i<=4;i++)
{
System.out.println("\t From ThreadA: i= "+i);
System.out.println("Thread B started");
for(int j=1;j<=4;j++)
{
System.out.println("Thread C started");
for(int k=1;k<=4;k++)
class ThreadPriority
A threadA=new A();
B threadB=new B();
C threadC=new C();
threadC.setPriority(Thread.MAX_PRIORITY);
threadB.setPriority(threadA.getPriority()+1); threadA.setPriority(Thread.MIN_PRIORITY);
threadA.start();
threadB.start();
Daemon thread in java is a service provider thread that provides services to the user thread.
● Its life depend on the mercy of user threads i.e. when all the user threads dies, JVM terminates
this thread automatically.
● There are many java daemon threads running automatically e.g. gc, finalizer etc.
● You can see all the detail by typing the jconsole in the command prompt.
● The jconsole tool provides information about the loaded classes, memory usage, running threads
etc
● It provides services to user threads for background supporting tasks. It has no role in life than to
serve user threads.
The java.lang.Thread class provides two methods for java daemon thread.
1) public void setDaemon(boolean status) used to mark the current thread as daemon thread or user
thread.
super(name);
if(Thread.currentThread().isDaemon())
}
else
t1.setDaemon(true);
t1.start();
t2.start();
// Setting user thread t3 to Daemon
t3.setDaemon(true);
t3.start();
Output:
t1 is Daemon thread
t3 is Daemon thread
t2 is User thread
Priority: When only daemon threads remain in a process, the JVM exits. This makes sense
because when only daemon threads are running, there is no need for a daemon thread to
provide a service to another thread.
Usage: Daemon threads are primarily used to provide background support to user threads.
They handle tasks that support the main execution without interfering with the user’s
operations.
Synchronization in Java
Synchronization in java is the capability to control the access of multiple threads to any shared
resource.
Java Synchronization is better option where we want to allow only one thread to access the shared
resource.
1. Process Synchronization
Thread Synchronization
There are two types of thread synchronization mutual exclusive and inter-thread communication.
1. Mutual Exclusive
1. Synchronized method.
2. Synchronized block.
3. static synchronization.
Mutual Exclusive
Mutual Exclusive helps keep threads from interfering with one another while sharing data.
1. by synchronized method
2. by synchronized block
3. by static synchronisation
By synchronized method
// lines of codes
Example:
In the below program, class Table contains one method print() which is not synchronized.
Two threads, t1 and t2 are accessing the same print() method, which print table 4 and 6,
respectively. Let’s see the output we get from the program when the method is not
synchronized.
class Table{
for(int i=1;i<=10;i++){
System.out.println(val*i);
try{
Thread.sleep(1000);
}catch(Exception e){
System.out.println(e);
}
}
Table t;
Thread1(Table t){
this.t=t;
t.print(4);
Table t;
Thread2(Table t){
this.t=t;
t.print(6);
t1.start();
t2.start();
In the above program, class Thread1 and Thread2 extends the Thread class. In Thread1 class, the
print() method of Table class is called to print table of 4 and in the Thread2 class, the print() method
of the Table class is called to print a table of 6. Both classes access the print() method
simultaneously. Since the method is not synchronized, we get different outputs.
Output
Here, by observing the outputs, we conclude that if the method is not synchronized, then we get
different outputs, or a data inconsistency problem will occur. So, to resolve this issue, we have to
make a synchronized method. Now let’s make the method synchronized and then see the output.
class Table{
System.out.println(val*i);
try{
Thread.sleep(1000);
}catch(Exception e){
System.out.println(e);
Table t;
Thread1(Table t){
this.t=t;
}
t.print(4);
Table t;
Thread2(Table t){
this.t=t;
t1.start();
t2.start();
}
By synchronized block
Example:
Let’s take the same example again of table printing, but instead of making the method
synchronized, we make a synchronized block inside the print () method to print tables 4 and
6. We always get the same output as the thread is synchronized, and at a time, one thread
enters inside the synchronized block to execute the code.
class Table{
for(int i=1;i<=10;i++){
System.out.println(val*i);
try{
Thread.sleep(1000);
}catch(Exception e){
System.out.println(e);
Table t;
Thread1(Table t){
this.t=t;
}
public void run(){
t.print(4);
Table t;
Thread2(Table t){
this.t=t;
t.print(6);
{
public static void main(String[] args) {
t1.start();
t2.start();
Here, we also get the same output that we get in the synchronized method.
Output:
As we know, in Java, each object has one lock, which means at a time, one thread can execute a
block of code on a given instance of the class. So, if we have multiple objects in our program, then
multiple locks are there, and if we are dealing with multiple threads in our program, then the output
may be corrupt, and a data inconsistency problem will occur. To resolve such a problem, we use a
static synchronized block or method.
Example
Let’s assume Shubham, Vikram, Inder, and Kamlesh have a joint account in a bank. The total
balance in the bank account is 10000 rupees. If Shubham or Vikram withdraws 10000, then the
amount withdrawn by any of the rest is zero or insufficient balance is shown to them, but Inder or
Kamlesh is still able to withdraw 10000 rupees, and vice-versa, this shows the program is incorrect
and inconsistent or corrupt data is given by the program. Go through the below program to get
clarity on the above mentioned example.
In the below program, two objects are created b1 and b2. For one object, one lock is there, which
means at a time one thread is allowed to execute the method, so, for two objects, two locks are
there, which allow two threads to execute the same method simultaneously; hence we get corrupt
output, or 20000 amount is withdrawn in spite of having only 10000 amounts in Bank.
int available_balance=10000;
int withdrawl_balance;
Bank(int withdrawl_balance){
this.withdrawl_balance=withdrawl_balance;
String thread_name=currentThread().getName();
if(withdrawl_balance<=available_balance){
System.out.println(thread_name+""+" withdrawmoney:"+""+withdrawl_balance);
available_balance-=withdrawl_balance;
}else{
withdraw();
t1.setName("Shubham");
t2.setName("Vikram");
t1.start();
t2.start();
Bank b2=new Bank(10000);// has one lock
t3.setName("Inder");
t4.setName("Kamlesh");
t3.start();
t4.start();
Output:
Now, if we make method static synchronized then one thread execute method at a time and we get
consistent data.
Bank(int withdrawl_balance){
this.withdrawl_balance=withdrawl_balance;
String thread_name=currentThread().getName();
if(withdrawl_balance<=available_balance){
available_balance-=withdrawl_balance;
}else{
withdraw();
}
public class Main
t1.setName("Shubham");
t2.setName("Vikram");
t1.start();
t2.start();
t3.setName("Inder");
t4.setName("Kamlesh");
t3.start();
t4.start();
}
Output:
class MultiplicationTable {
try {
} catch (InterruptedException e) {
e.printStackTrace();
}
this.table = table;
this.number = number;
@Override
table.printTable(number);
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
class MultiplicationTable {
synchronized(this) {
for (int i = 1; i <= 10; i++) {
try {
} catch (InterruptedException e) {
e.printStackTrace();
this.table = table;
this.number = number;
}
@Override
table.printTable(number);
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
It is a mechanism in which a thread is paused running in its critical section and another thread is
allowed to enter (or lock) in the same critical section to be executed.
● wait()
● notify()
● notifyAll()
1) wait() method
Causes current thread to release the lock and wait until either another thread invokes the notify()
method or the notifyAll() method for this object, or a specified amount of time has elapsed.
2) notify() method
If any threads are waiting on this object, one of them is chosen to be awakened.
3) notifyAll() method
Example:
int sum=0;
synchronize(this)
{
for(int i=1;i<=5;i++)
sum=sum+I;
this.notify();
th.strat();
synchronize(Th.)
th.wait();
System.out.println(“Sum is : ”+ th.sum());
}
The java.io package in Java provides a comprehensive set of input and output (I/O) streams for
reading from and writing to data sources, such as files, network connections, memory buffers, and
more. It includes classes for handling both byte and character data, as well as utility classes for
performing various I/O operations.
Byte Streams and Character Streams. Each type has different classes and methods for handling
data. Here's a detailed breakdown:
1. Byte Streams
Byte streams (InputStream and OutputStream) are used to handle input and output of raw binary
data in Java. They operate with bytes, which can represent any kind of data, including text and
binary data (image files, audio files, etc)
• InputStream: This is the base class for all byte input streams. It provides methods to read
bytes from a source.
• FileInputStream
• ByteArrayInputStream
• BufferedInputStream
• DataInputStream
• ObjectInputStream
• OutputStream: This is the base class for all byte output streams. It provides methods to
write bytes to a destination.
• FileOutputStream
• ByteArrayOutputStream
• BufferedOutputStream
• DataOutputStream
• ObjectOutputStream
Let's look at an example where we read from a file using FileInputStream (a byte stream) and
write to a file using FileOutputStream (a byte stream).
Example Code
import java.io.*;
Explanation
Output
Assume input.txt contains the text "Hello, Byte Streams!". After running the program,
output.txt will be created with the same content.
2. Character Streams
Character streams (Reader and Writer) are used to handle input and output of characters,
specifically for text-based data. They handle Unicode characters and are more suitable for working
with text files.
• Reader: This is the base class for all character input streams. It provides methods to read
characters from a source.
• FileReader
• StringReader
• BufferedReader
• InputStreamReader
• CharArrayReader
• PipedReader
• Writer: This is the base class for all character output streams. It provides methods to write
characters to a destination.
• FileWriter
• StringWriter
• BufferedWriter
• OutputStreamWriter
• CharArrayWriter
• PipedWriter
Example Code
import java.io.*;
Explanation
Output
Assume input.txt contains the text "Hello, Character Streams!". After running the program,
output.txt will be created with the same content.
Key Differences
Byte Streams: Handle binary data, suitable for any type of data including text and non-text.
Character Streams: Handle text-based data, handle Unicode characters, and are
specifically designed for reading and writing textual information.
Byte Stream Vs Character Stream In Java :
They process the data byte by byte. They process the data character by character.
They are most suitable to process binary files. They are most suitable to process text files.
All byte stream classes in Java are descendants of All character stream classes in Java are
InputStream and OutputStream. descendants of Reader and Writer.