[Go to site: main page, start]

0% found this document useful (0 votes)
2 views29 pages

Java Notes

The document covers Exception Handling and Input/Output in Java, explaining mechanisms for managing errors and ensuring program stability. It details the structure of try-catch blocks, the use of finally, throw and throws keywords, and the hierarchy of exceptions. Additionally, it introduces Java I/O basics, including types of streams, file handling, and the concept of multithreading, highlighting the advantages and disadvantages of both multitasking and multithreading.

Uploaded by

dishitagupta2003
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views29 pages

Java Notes

The document covers Exception Handling and Input/Output in Java, explaining mechanisms for managing errors and ensuring program stability. It details the structure of try-catch blocks, the use of finally, throw and throws keywords, and the hierarchy of exceptions. Additionally, it introduces Java I/O basics, including types of streams, file handling, and the concept of multithreading, highlighting the advantages and disadvantages of both multitasking and multithreading.

Uploaded by

dishitagupta2003
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

UNIT -III Exception Handling, I/O: Exceptions

Exception Handling in Java is a mechanism used to handle both compile-time (checked) and runtime
(unchecked) exceptions, allowing a program to continue execution smoothly even in the presence of
errors.

• Handles abnormal conditions that occur during program execution.


• Helps maintain program stability by preventing unexpected termination.

Basic try-catch Example


The try block contains code that might throw an exception,

The catch block handles the exception if it occurs.

Output

Error: Division by 0!

Finally Block
The finally block executes after the try and catch blocks in most situations, whether an exception
arised or not. It is typically used for closing resources such as database connections, open files, or
network connections.

Finally may not execute in cases like:

• [Link]()
• JVM crash
• infinite loop before finally
throw and throws Keywords
throw: Used to explicitly throw a single exception. We use throw when something goes wrong (or
“shouldn’t happen”) and we want to stop normal flow and hand control to exception handling.

throws: Declares exceptions that a method might throw, informing the caller to handle them. It is
mainly used with checked exceptions (explained below). If a method calls another method that
throws a checked exception, and it doesn’t catch it, it must declare that exception in its throws
clause
Internal Working of try-catch Block:
• JVM executes code inside the try block.
• If an exception occurs, remaining try code is skipped and JVM searches for a matching catch
block.
• If found, the catch block executes.
• Control then moves to the finally block (if present).
• If no matching catch is found, the exception is handled by JVM’s default handler.
• The finally block always executes, whether an exception occurs or not.

Exception Hierarchy
In Java, all exceptions and errors are subclasses of the Throwable class. It has two main branches

• Exception.
• Error

Types of Java Exceptions


Java defines several types of exceptions that relate to its various class libraries. Java also allows users
to define their it's exceptions.

1. Built-in Exception

Built-in Exception are pre-defined exception classes provided by Java to handle common errors
during program execution. There are two type of built-in exception in java.

• Checked Exception: These exceptions are checked at compile time, forcing the programmer
to handle them explicitly.
• Unchecked Exception: These exceptions are checked at runtime and do not require explicit
handling at compile time.

2. User-Defined Exception

Sometimes, the built-in exceptions in Java are not able to describe a certain situation. In such cases,
users can also create exceptions, which are called "user-defined Exceptions".

Methods to Print the Exception Information

• printStackTrace(): Prints the full stack trace of the exception, including the name, message
and location of the error.
• toString(): Prints exception information in the format of the Name of the exception.
• getMessage() : Prints the description of the exception

Nested try-catch
A nested try-catch means placing a try-catch block inside another try block. This is useful when you
want to handle different types of exceptions separately.
Handling Multiple Exception
We can handle multiple type of exceptions in Java by using multiple catch blocks, each catching a
different type of exception.

Difference Between Exception and Error

How JVM Handles an Exception


When an exception occurs in Java, the JVM (Java Virtual Machine) follows a clear step-by-step
process:

Step-by-Step Flow

Exception Occurs

• An error happens during execution


• Example: divide by zero, invalid array index

Exception Object is Created

• JVM creates an object of the exception class


• Example: ArithmeticException, NullPointerException

Exception is Thrown

• The exception is “thrown” to the JVM

Searching for Handler

• JVM looks for a matching catch block


• It searches:
o First in the current method
o Then moves up the call stack (method by method)

Handler Found?

• If found → executes that catch block


• If not found → JVM terminates the program

Default Exception Handler

• If no handler is found, JVM prints:


o Exception name
o Description
o Line number
• Then program stops

User-Defined Custom Exception in Java


A user-defined custom exception is an exception class created by the programmer to represent
application-specific or business-specific error scenarios.

Examples of User-defined Exception:

• Invalid bank transaction


• Insufficient balance
• Age not eligible for registration
• Invalid login attempt

Java Custom Exception


A custom exception in Java is an exception defined by the user to handle specific application
requirements. These exceptions extend either the Exception class (for checked exceptions) or the
RuntimeException class (for unchecked exceptions).

Why Use Java Custom Exceptions?


We use Java custom exception,

• To represent application-specific errors.


• To add clear, descriptive error messages for better debugging.
• To encapsulate business logic errors in a meaningful way.

Types of Custom Exceptions

There are two types of custom exceptions in Java.

• Checked Exceptions: It extends the Exception class. and it must be declared in the throws
clause of the method signature.
• Unchecked Exceptions: It extends the RuntimeException class. They are not checked by the
compiler, which means the compiler does not require them to be declared in the throws
clause or handled using a try-catch block. Unchecked exceptions are typically used for
programming errors or invalid operations.

Stack Trace Elements


• A stack trace is a report generated by the JVM when an exception occurs.
• It shows the sequence of method calls (call stack) that were active at the time of the
exception.

Each line in a stack trace is called a Stack Trace Element.

It represents:

• Class name
• Method name
• File name
• Line number

Line 1

at [Link]([Link])

• Class → Demo
• Method → methodB
• File → [Link]
• Line → 10 (where exception occurred)

Line 2

• at [Link]([Link])
• Method methodA called methodB

Line 3

• at [Link]([Link])

Entry point of program

Order of Stack Trace

• Top → Bottom
• Top = where exception occurred
• Bottom = where program started

Accessing Stack Trace in Code

printStackTrace()

• Prints the entire stack trace to the console


• Shows exception type, message, and all method calls
• Mainly used for quick debugging

getStackTrace()
• Returns an array of StackTraceElement objects
• Allows programmatic access to stack trace details
• Used when you want to customize or process the trace information

Why Stack Trace is Important?

• Helps in debugging errors


• Shows exact line where exception occurred
• Helps trace method call sequence

Input / Output Basics


Java I/O (Input/Output) is a collection of classes and streams in the [Link] package that handle
reading data from sources (like files, keyboard, or network) and writing data to destinations (like files,
console or sockets). It provides both byte and character streams to support all types of data.

3 most commonly used default streams that Java has provided:

• [Link]
• [Link]
• [Link]

Types of Streams

Depending on the type of operations, streams can be divided into two primary classes:

1. Input Stream: These streams are used to read data that must be taken as an input from a source
array or file or any peripheral device. For eg., FileInputStream, BufferedInputStream,
ByteArrayInputStream etc.

2. Output Stream: These streams are used to write data as outputs into an array or file or any output
peripheral device. For eg., FileOutputStream, BufferedOutputStream, ByteArrayOutputStream etc.

Types of Streams Depending on type of data (byte vs character

Depending on the types of file, Streams can be divided into two primary classes which can be further
divided into other classes as can be seen through the diagram below followed by the explanations.
1. ByteStream:

Byte streams in Java are used to perform input and output of 8-bit bytes. They are suitable for
handling raw binary data such as images, audio, and video, using classes like InputStream and
OutputStream.

Here is the list of various ByteStream Classes:

2. CharacterStream:

Character streams in Java are used to perform input and output of 16-bit Unicode characters. They
are best suited for handling text data, using classes like Reader and Writer which automatically
handle character encoding and decoding.
Reading and Writing (Java I/O Basics)
Reading Data

Reading means taking input from a source (keyboard, file, etc.)

Writing Data

Writing means sending output to a destination (file, console, etc.)


Console Input/Output
What is Console I/O?

Interaction between user and program using keyboard and screen

(a) Using Scanner (Most Common)

(b) Using BufferedReader

Reading and Writing Files


File Handling in Java

Java provides classes to read from and write to files

(a) Reading from File

(b) Writing to File


(c) Using Buffered Streams (Efficient)

Create a File

• In order to create a file in Java, you can use the createNewFile() method.
• If the file is successfully created, it will return a Boolean value true and false if the file already
exists.
UNIT-IV Multithreading and Generic
Programming
Multitasking
Multi-tasking is the ability of an operating system to run multiple processes or tasks concurrently,
sharing the same processor and other resources. In multitasking, the operating system divides the
CPU time between multiple tasks, allowing them to execute simultaneously. Each task is assigned a
time slice, or a portion of CPU time, during which it can execute its code. Multi-tasking is essential for
increasing system efficiency, improving user productivity, and achieving optimal resource utilization.

Advantages

• Better CPU utilization – multiple tasks run efficiently


• Time saving – tasks execute simultaneously
• User can perform multiple operations (e.g., browsing + music)
• Improves productivity

Disadvantages

• High memory usage (each process needs separate memory)


• Context switching overhead
• Complex to manage processes
• Slower than multithreading (process creation is costly)

Multithreading
Multi-threading is a technique in which an operating system divides a single process into multiple
threads, each of which can execute concurrently. Threads share the same memory space and
resources of the parent process, allowing them to communicate and synchronize data easily. Multi-
threading is useful for improving application performance by allowing different parts of the
application to execute simultaneously.
Advantages

• Lightweight (threads share memory)


• Faster execution than processes
• Better responsiveness (UI doesn’t freeze)
• Efficient communication (shared memory)

Disadvantages

• Complex to implement
• Synchronization issues (race condition)
• Debugging is difficult
• Thread interference / deadlock risk

Differences between multitasking and multi-threading


Java Threads
A Java thread is the smallest unit of execution within a program. It is a lightweight subprocess that
runs independently but shares the same memory space as the process, allowing multiple tasks to
execute concurrently.

• A thread is a lightweight, independent unit of execution inside a program (process).


• Threads allow parallel execution of tasks.
• A process can have multiple threads.
• Each thread runs independently but shares the same memory space as the process, allowing
multiple tasks to execute concurrently.
• Example: Imagine a restaurant kitchen. Multiple chefs (threads) are preparing different
dishes at the same time. This speeds up service and utilizes all available resources (CPU).

Advantages of Threads

• Lightweight: Threads require less memory and are faster to create than processes.
• Better Performance: Multiple threads execute concurrently, improving overall speed.
• Efficient CPU Utilization: CPU can switch between threads, reducing idle time.
• Improved Responsiveness: Keeps applications responsive (e.g., UI works while background
tasks run).
• Shared Memory: Threads share the same memory space, making communication faster and
easier.
• Parallel Execution: Threads can run simultaneously on multiple cores, increasing efficiency.
• Resource Sharing: Threads share resources like files and data, reducing overhead.

Thread life cycle


During its thread life cycle, a Java thread transitions through several states stated below from
creation to termination:

• New State
• Runnable State
• Blocked State
• Waiting State
• Timed Waiting State
• Terminated State

The diagram below represents various states of a thread at any instant:

New State: A thread is in the new state when it is created but not yet started using start().

Runnable State:

• A thread is in the runnable state when it is ready to run and waiting for CPU time.
• It may be running or waiting in the queue for execution.

Blocked State:

A thread enters the blocked state when it is waiting to acquire a lock (monitor) to enter a
synchronized block/method.

Waiting State:

A thread is in the waiting state when it is waiting indefinitely for another thread to perform an
action.

Example: using wait(), join() (without timeout).

Timed Waiting State:

A thread is in timed waiting when it waits for a specific period of time.

Example: sleep(time), wait(time), join(time).

Terminated State:

A thread enters the terminated state after it has finished execution or is stopped due to an error.

Creating Threads in Java


We can create threads in java using two ways:

• Extending Thread Class (by creating a thread)


• Implementing a Runnable interface (converting a class into thread)
1. By Extending Thread Class

• Create a class that extends Thread.


• Override the run() method, this is where you put the code that the thread should execute.
• Then create an object of your class and call the start() method. This will internally call run() in
a new thread.

2. Using Runnable Interface

• Create a class that implements Runnable.


• Override the run() method, this contains the code for the thread.
• Then create a Thread object, pass your Runnable object to it and call start().

When to Use Which?

• Use extends Thread: if your class does not extend any other class.
• Use implements Runnable: if your class already extends another class (preferred because
Java doesn’t support multiple inheritance).

Java Thread Class


• The Thread class in Java is used to create and manage threads for multithreading.
• It is present in the [Link] package.

Differences between Runnable Interface and Thread Class

Thread Class Methods


Thread Class Constructors
Thread Priority
Each thread is assigned a priority which affects the order in which it is scheduled for running.

• The thread of the same priority is given equal treatment by the java scheduler and therefore
they share the processor on a first come serve basis.
• Thread class defines several priority constants:
o MIN_PRIORITY = 1
o NORM_PRIORITY = 5
o MAX_PRIORITY = 10
• A thread of lower priority gains the control when the following events should happen: ○ It
stops running at the end of run(). ○ It is made to sleep using sleep(). ○ It is told to wait using
wait().
• If another thread of a higher priority comes along, the currently running thread will be pre-
empted by the incoming threads thus forcing the current thread to move to the runnable
state.
• Syntax: ThreadName. setpriority(int number);

Synchronization in Java
Synchronization is used to control the execution of multiple processes or threads so that shared
resources are accessed in a proper and orderly manner. It helps avoid conflicts and ensures correct
results when many tasks run at the same time.

• It controls the access of shared resources.


• It avoids data inconsistency.
• It ensures proper execution of processes.

There are three main ways to achieve synchronization .

1. Synchronized Methods

A synchronized method ensures that only one thread can execute it at a time on the same object
instance.

2. Synchronized Blocks

Instead of synchronizing an entire method, Java allows synchronization on specific blocks of code.
This improves performance by locking only the necessary section.

3. Static Synchronization
Static synchronization is used to synchronize static methods. In this case, the lock is placed on the
class object rather than the instance.

Types of Synchronization

There are two type of synchronizations in Java which are listed below:

1. Process Synchronization

Process Synchronization is a technique used to coordinate the execution of multiple processes. It


ensures that the shared resources are safe and in order.

2. Thread Synchronization

Thread Synchronization is used to coordinate and order the execution of the threads in a multi-
threaded program.

There are two types of thread synchronization are mentioned below:

• Mutual Exclusive
• Cooperation (Inter-thread communication)

Mutual Exclusion

• Mutual Exclusion is a property of process synchronization that states that no two processes
can exist in the critical section at the same time.
• The term was first coined by Dijkstra.
• Any synchronization technique must satisfy mutual exclusion to avoid race conditions.
• The need arises due to concurrency.
• Mutual exclusion methods prevent simultaneous use of shared resources like global variables
(critical sections).
• When process P1 uses resource R1, another process cannot use it until P1 finishes.
• Examples: files, printers, shared data

Requirements of Mutual Exclusion

• No two processes should be in critical section simultaneously


• A process stays in critical section for limited time
• No assumptions about process speeds
• A process outside should not block others
• Every process should get access in finite time
• No process should suffer indefinite delay (starvation)

Approaches to Implement Mutual Exclusion

Software Method

• Responsibility lies with processes


• Error-prone and high overhead
Hardware Method

• Uses special machine instructions


• Faster but not complete solution
• Cannot guarantee no deadlock/starvation

Programming Language Method

• Supported by OS or programming languages

Inter-thread communication

• Inter-thread communication in Java is a mechanism in which a thread is paused from running


in its critical section, and another thread is allowed to enter (or lock) the same critical section
to be executed.
• Inter-thread communication is also known as Cooperation in Java.

Polling
• Polling is the process of checking a condition repeatedly until it becomes true.
• It is usually implemented using loops to check a condition.
• If the condition becomes true, a specific action is performed.
• Example: one thread produces data, another consumes it (queue problem).

Problems with Polling

• Wastes CPU cycles


• Makes implementation inefficient
• Slows down execution due to continuous checking

How Java Multi-Threading Solves This

Java avoids polling using:

o wait()
o notify()
o notifyAll()
• These methods belong to the Object class
• Must be used inside a synchronized block

wait()

• Makes the thread release the lock and go to sleep


• Waits until another thread calls notify()

notify()
• Wakes up one waiting thread
• Does not release the lock immediately

notifyAll()

• Wakes up all waiting threads

The image below demonstrates the concept of Thread Synchronization and Inter-Thread
Communication in Java

READ EXPLANATION OF THIS DIAGRAM IN MAM PDF

Volatile Keyword
• The volatile keyword in Java ensures that all threads see the latest value of a variable.
• It prevents caching, so updates are immediately visible to other threads.

Working of Volatile Modifier

• Applies only to variables


• Guarantees visibility → changes are instantly seen by other threads
• Does not guarantee atomicity
• Operations like count++ (read-modify-write) can still give incorrect results

Deadlock in Java
• Deadlock can occur in a situation when a thread is waiting for an object lock that is acquired
by another thread and a second thread is waiting for an object lock that is acquired by the
first thread.
• Since, both threads are waiting for each other to release the lock, the condition is called
deadlock.
• Example Situation
o Thread 1 holds Lock A and waits for Lock B
o Thread 2 holds Lock B and waits for Lock A
o This causes deadlock

How to Avoid Deadlock

• Avoid Nested Locks: Do not give multiple locks to threads unnecessarily


• Avoid Unnecessary Locks: Lock only important resources
• Use Thread Join Carefully: Use join() with time limit to avoid infinite waiting

Daemon Threads
• A daemon thread is a low-priority background thread that supports user threads and does
not prevent JVM from exiting.
• Used for background tasks like monitoring, logging, and cleanup.
• Runs in background to support user (non-daemon) threads.
• JVM exits automatically when all user threads finish.

Key Points
• Created using Thread class and marked with setDaemon(true)
• Must call setDaemon(true) before start(), otherwise → IllegalThreadStateException
• Common examples: Garbage Collector (GC), Finalizer thread
• Inherits daemon status from parent thread
• Not suitable for tasks requiring completion (e.g., file writing, database updates)
• JVM terminates daemon threads abruptly (no cleanup)

Use Cases of Daemon Threads

• Garbage Collection (GC): Automatically frees unused memory in the background.


• Background Monitoring: Keeps checking system/resources (like memory, connections).
• Logging and Auditing: Records application activities continuously in background.
• Cleanup Operations: Removes temporary files and releases unused resources.
• Scheduler / Timer Tasks: Executes tasks at fixed intervals (like reminders, updates).

Main thread
• When a Java program starts, the Java Virtual Machine (JVM) creates a thread automatically
called the main thread.
• This thread executes the main() method and controls the overall execution flow of the
program.
• It is the parent thread from which all other user-defined threads are created.
• The default name of the main thread is "main".
• The default priority of the main thread is 5.
• It usually finishes last as it may perform cleanup and shutdown tasks.

Create the Main Thread

• The main thread is created automatically when the program starts.


• To control it, use [Link]()
• This method returns the currently executing thread

Relationship Between main() and Main Thread

• JVM first creates the main thread


• The main thread checks and executes the main() method
• main() is mandatory for standalone Java programs

Deadlock Using Main Thread

• Even a single main thread can cause deadlock


• Happens when a thread waits for itself

Thread Groups

• A thread group is a collection of multiple threads


• Threads in a group can work together or wait for each other

ThreadGroup Class

• Used to create and manage groups of threads


• Helps control multiple threads as one unit
• Example: suspend/resume multiple threads together

Features of ThreadGroup

• Forms a tree structure (parent-child groups)


• Each group (except root) has a parent
• A thread can access its own group info only
• Cannot access parent or other groups' details
• Syntax:
public class ThreadGroup extends Object implements [Link]

Constructors of ThreadGroup Class

Methods of ThreadGroup Class


Number of Threads in Parallel
• The number of threads that can run in true parallel depends mainly on CPU cores.
• It is also affected by OS, memory, and type of tasks.

Core Determinants

CPU Cores

• Each core can run one thread at a time


• Maximum parallel threads = number of cores (or more with hyper-threading)

Operating System (OS) Scheduler

• OS manages and schedules threads


• Creates illusion of parallelism using time-slicing
• Limits threads based on system resources

System Memory

• Each thread needs its own memory (stack)


• More threads → more memory usage
• Limits total number of threads

Optimal Number of Threads

CPU-Bound Tasks

• Heavy computation tasks


• Optimal threads ≈ number of CPU cores
• Too many threads → performance decreases (context switching)

I/O-Bound Tasks

• Tasks waiting for I/O (disk, network)


• Can use more threads than CPU cores
• Keeps CPU busy while others wait

Generic Programming
• Introduced in J2SE 5, allows classes, interfaces, and methods to be parameterized by types.
• This approach enables developers to write a single piece of code that can operate on various
data types without having to specify the exact type in advance.
• This makes your code more flexible, reusable, and type-safe.

Why Use Generics?


• Code Reusability: Write one class or method that works with different data types.
• Type Safety: Catch type errors at compile time instead of facing ClassCastException at
runtime.
• Elimination of Casting: Generics remove the need for manual type casting when retrieving
elements from collections.
• Cleaner Code: No need for casting when retrieving objects and also improves overall
program readability.

Type Parameters

• Use placeholders like <T>, <E>, <K>, <V>


• Represent types specified at object creation time

Generic Classes

• Class that works with different data types using type parameters
• Uses < > to define types
• To create objects of a generic class, we use the following syntax:
Syntax:
BaseType<Type> obj = new BaseType<Type>();
• Multiple Type parameters can also be passed in Generic classes.

Generic Methods

• Methods that work with different types of arguments


• Type is decided during method call
• Handled by the compiler

Type Parameter Naming Conventions

• T → Type
• E → Element
• K → Key
• V → Value
• N → Number

Bounded Types & Limitations

• No Primitives: Generics do not work with primitive types like int or char; you must use their
Wrapper Classes like Integer or Character.
• Generic Types Differ Based on their Type Arguments: During compilation, generic type
information is erased which is also known as type erasure.
• Cannot Instantiate Types: You cannot create an instance of a type parameter (e.g., new T())
because the exact type is unknown at runtime.
• Generic Arrays: You cannot create an array of a generic type (e.g., new T[10]).
• Static Context: Static fields or methods cannot use the type parameters of the class.

You might also like