0% found this document useful (0 votes)
5 views50 pages

Java Unit 3,4 copy

The document provides an overview of threads in Java, explaining their importance for concurrent execution and methods for creating them, such as extending the Thread class or implementing the Runnable interface. It discusses thread priorities, synchronization, and communication methods like wait(), notify(), and notifyAll(), emphasizing the need for proper coordination to avoid data inconsistency. Additionally, it introduces AWT components for building graphical user interfaces, highlighting key classes like Frame, Panel, Button, and others.

Uploaded by

silentsoul00200
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)
5 views50 pages

Java Unit 3,4 copy

The document provides an overview of threads in Java, explaining their importance for concurrent execution and methods for creating them, such as extending the Thread class or implementing the Runnable interface. It discusses thread priorities, synchronization, and communication methods like wait(), notify(), and notifyAll(), emphasizing the need for proper coordination to avoid data inconsistency. Additionally, it introduces AWT components for building graphical user interfaces, highlighting key classes like Frame, Panel, Button, and others.

Uploaded by

silentsoul00200
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
You are on page 1/ 50

handle mouse events.

The mouseClicked() method is overridden to change the


background color and trigger a repaint of the applet.

UNIT - 3
Threads
Threads are a fundamental concept in Java that allow concurrent execution of
code. They are lightweight units of execution within a program, enabling multiple
tasks to run concurrently. Here are the key aspects of threads:

Threads enable concurrent execution, where multiple parts of a program can


execute independently and concurrently.

By default, Java programs have at least one thread, known as the main
thread, which is created automatically when the program starts.

Threads can be used to perform tasks in the background, handle multiple


client requests, or improve the responsiveness of a user interface.

Creating Threads:
In Java, there are two main ways to create threads:

Extending the Thread class:

You can create a thread by extending the Thread class and overriding its
run() method, which contains the code to be executed by the thread.

Here's an example:

class MyThread extends Thread {


public void run() {
// Code to be executed by the thread
}
}

To start the thread, create an instance of the MyThread class and call its
start() method.

Implementing the Runnable interface:

JAVA 55
Alternatively, you can create a thread by implementing the Runnable

interface and passing an instance of it to the Thread constructor.

Here's an example:

class MyRunnable implements Runnable {


public void run() {
// Code to be executed by the thread
}
}

To start the thread, create an instance of the MyRunnable class, create a


Thread object by passing the MyRunnable instance to the constructor, and

then call the start() method on the Thread object.

Thread Priority:
Threads in Java can have different priorities, ranging from 1 (lowest) to 10
(highest). The default priority is 5. Here are some points to know about thread
priority:

Thread priorities are used by the thread scheduler to determine the order of
execution when multiple threads are ready to run.

You can set the priority of a thread using the setPriority() method, passing
the desired priority as an argument.

Higher priority threads have a greater chance of being scheduled for


execution, but it is not guaranteed.

Thread priority should be used with caution and should not be solely relied
upon for critical operations.

Blocked States:
Threads can enter into blocked states in certain situations. Here are two
common scenarios:

Sleeping: A thread can enter a blocked state by invoking the Thread.sleep()

method, which causes the thread to pause execution for a specified period
of time.

Waiting: Threads can wait for a specific condition to be met using the wait()

method. This method is typically used in multi-threaded programs where one


thread waits for a signal from another thread before continuing its execution.

JAVA 56
Extending the Thread Class:
As mentioned earlier, one way to create threads is by extending the Thread

class. Here are some points to understand:

When extending the Thread class, you override the run() method to define
the behavior of the thread.

By extending the Thread class, your class becomes a thread itself and can
be instantiated and started like any other thread.

However, this approach limits the flexibility of your class, as it does not allow
for extending other classes.

If you've created a thread by extending the Thread class, you can simply
create an instance of your class and call the start() method on it.

Example:

javaCopy code
MyThread myThread = new MyThread(); // Assuming MyThread extends Thread
myThread.start();

It's important to note that concurrent programming and managing threads can be
complex, as it involves synchronization, thread safety, and coordination between
threads. It's recommended to study these topics further to write robust and
efficient multi-threaded programs.

Runnable Interface
The Runnable interface in Java provides an alternative way to create threads by
encapsulating the code to be executed within a separate object. Here's an
overview of the Runnable interface:

1. Definition and Purpose:


The Runnable interface is defined in the java.lang package and contains a
single method called run() . Its purpose is to represent a task or unit of work
that can be executed by a thread.

2. Implementing the Runnable Interface:


To create a thread using the Runnable interface, follow these steps:

JAVA 57
Create a class that implements the Runnable interface.

Implement the run() method within the class, which contains the code to be
executed by the thread.

Instantiate a Thread object, passing an instance of your class as a


constructor argument.

Call the start() method on the Thread object to start the execution of the
thread.

Here's an example to illustrate the usage of the Runnable interface:

class MyRunnable implements Runnable {


public void run() {
// Code to be executed by the thread
}
}

public class RunnableExample {


public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
}
} create class object
Thread class object thread new Thread (ob of our implemented class)

In the example above, we create a class MyRunnable that implements the


Runnable interface and overrides the run() method. Then, we create an instance
of MyRunnable and pass it to the Thread constructor. Finally, we start the thread by
calling the start() method.
Benefits of Using the Runnable Interface:
Using the Runnable interface has several advantages over extending the Thread

class directly:

Flexibility: Implementing the Runnable interface allows you to separate the


thread's behavior from the class hierarchy. You can still extend other classes
and implement multiple interfaces while defining the thread's behavior in a
separate Runnable object.

Code Reusability: Since the thread's behavior is encapsulated in a Runnable


object, you can reuse the object and share it among multiple threads if
needed.

JAVA 58
Enhanced Thread Pooling: When utilizing thread pooling techniques, such
as the Executor framework, using Runnable objects allows for more efficient
utilization of thread resources.

By utilizing the Runnable interface, you can design your code to be more flexible,
modular, and scalable when dealing with multi-threaded scenarios.

Thread Synchronization:
In multi-threaded programming, thread synchronization is important to ensure
proper coordination and order of execution between threads. It helps prevent
data inconsistency and race conditions. Java provides synchronization
mechanisms to achieve thread synchronization:

1. Synchronized Methods:

By using the synchronized keyword, you can declare a method as


synchronized.

When a thread enters a synchronized method, it acquires the lock on the


object, and other threads must wait until the lock is released.

Example:

public synchronized void synchronizedMethod() {


// Synchronized method code
}

2. Synchronized Blocks:

You can also synchronize specific blocks of code using the synchronized

keyword with an object as a lock.

The lock ensures that only one thread can execute the synchronized
block at a time.

Example:

synchronized (lockObject) {
// Synchronized block code
}

JAVA 59
Synchronize Threads:
To synchronize threads and ensure proper coordination, you can use methods
like wait() , notify() , and notifyAll() . These methods must be called from
within a synchronized context (synchronized method or synchronized block).
Here's an overview:

wait() : Causes the current thread to release the lock and enter a waiting
state until another thread calls notify() or notifyAll() on the same object. It
should be used within a loop to check the condition that caused the thread to
wait.

: Wakes up one of the threads that are waiting on the same object.
notify()

The awakened thread can then compete for the lock and continue execution.

notifyAll() : Wakes up all the threads that are waiting on the same object.

By using synchronization and the methods mentioned above, you can control the
execution order and synchronization between threads, ensuring proper
concurrency and data consistency.

Sync Code Block:


In addition to synchronizing entire methods, Java also allows you to synchronize
specific blocks of code using synchronized code blocks. This gives you more
fine-grained control over synchronization. Here's how you can use sync code
blocks:

1. Syntax:

synchronized (object) {
// Synchronized code block
}

2. Object Lock:

The synchronized code block requires an object as a lock to coordinate


the execution of threads.

The lock can be any object, but it is commonly a shared object that the
threads need to synchronize on.

JAVA 60
Only one thread can hold the lock at a time, and other threads will wait
until the lock is released.

3. Benefits:

Sync code blocks allow you to synchronize only the critical section of
code that needs synchronization, rather than the entire method.

This approach can lead to improved performance by reducing the


amount of time other threads spend waiting for the lock.

Here's an example to illustrate the usage of sync code blocks:

public class SyncCodeBlockExample {


private static int count = 0;
private static final Object lock = new Object();

public static void increment() {


synchronized (lock) {
count++;
}
}

public static void main(String[] args) {


Runnable runnable = () -> {
for (int i = 0; i < 1000; i++) {
increment();
}
};

Thread thread1 = new Thread(runnable);


Thread thread2 = new Thread(runnable);

thread1.start();
thread2.start();

try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println("Count: " + count);


}
}

In the example above, we have a shared count variable that we want to


increment concurrently. We use a synchronized code block with the lock object

JAVA 61
to ensure that only one thread can modify the count variable at a time.
Overriding Synchronized Methods:
When dealing with inheritance and overriding methods in Java, there are a few
things to consider when it comes to synchronized methods:

1. Overriding a Synchronized Method:

If a superclass has a synchronized method, and a subclass overrides


that method, the subclass's method is not automatically synchronized.

The subclass can choose to synchronize the method explicitly if


synchronization is required.

2. Adding Synchronization to Overridden Methods:

If a subclass wants to synchronize an overridden method, it can do so


by using the synchronized keyword in the method declaration.

Example:

class SuperClass {
public synchronized void synchronizedMethod() {
// Superclass synchronized method
}
}

class SubClass extends SuperClass {


@Override
public synchronized void synchronizedMethod() {
// Subclass synchronized method
}
}

In the example above, the SuperClass has a synchronized method. The SubClass

overrides the method and adds the synchronized keyword to synchronize it


again.

Thread Communication
Thread Communication using wait(), notify(), and notifyAll():
In Java, threads can communicate with each other using the methods wait() ,
notify() , and notifyAll() . These methods are part of the Object class and can

JAVA 62
be used to coordinate the execution of threads. Here's an explanation of how
these methods work:

1. wait():

The wait() method causes the current thread to release the lock on the
object and enter a waiting state until another thread calls notify() or
notifyAll() on the same object.

When a thread calls wait() , it relinquishes the lock and allows other
threads to continue execution. The thread remains in a waiting state until
it receives a notification to resume.

Example:

synchronized (sharedObject) {
while (condition) {
sharedObject.wait(); // Waits until notified
}
// Continue execution after receiving notification
}

2. notify():

The notify() method wakes up one thread that is waiting on the same
object. It allows that thread to continue execution after it regains the
lock.

If multiple threads are waiting, the thread that gets the notification is
arbitrary and depends on the thread scheduler.

Example:

synchronized (sharedObject) {
// Update shared data or perform some task
sharedObject.notify(); // Notifies one waiting thread
}

3. notifyAll():

The notifyAll() method wakes up all threads that are waiting on the
same object. It allows all the waiting threads to continue execution after
they regain the lock.

JAVA 63
Example:

synchronized (sharedObject) {
// Update shared data or perform some task
sharedObject.notifyAll(); // Notifies all waiting threads
}

Thread communication using wait() , notify() , and notifyAll() is typically used


in scenarios where one thread needs to wait for a specific condition to be met by
another thread before proceeding.
To ensure proper usage of these methods, the following guidelines are
recommended:

Call wait() , notify() , and notifyAll() only from within synchronized blocks
or synchronized methods.

Use a loop with wait() to check the condition that caused the thread to wait.
This helps prevent spurious wake-ups.

Make sure the threads involved are accessing the same shared object for
synchronization.

Thread communication is a powerful mechanism for coordination and


synchronization between threads. It enables efficient and controlled inter-thread
communication in complex concurrent scenarios.

AWT Components
AWT (Abstract Window Toolkit) is a set of classes and components in Java used
for creating graphical user interfaces (GUIs) for desktop applications. AWT
provides a collection of UI components that allow developers to create windows,
buttons, labels, text fields, and other elements to build interactive user
interfaces. Here are some commonly used AWT components:

1. Frame:

The Frame class represents a top-level window with a title bar and
borders.

It is the main container for building the application window.

JAVA 64
Example:

Frame frame = new Frame("My Frame");

2. Panel:

The Panel class represents a container that can hold other components.

It is used to group and organize components within a window.

Example:

Panel panel = new Panel();

3. Button:

The Button class represents a push button component that triggers an


action when clicked.

It is used to perform specific actions in response to user interaction.

Example:

Button button = new Button("Click me");

4. Label:

The Label class represents a non-editable text component used to


display information or instructions.

It is used for providing static text in the GUI.

Example:

Label label = new Label("Welcome!");

5. TextField:

The TextField class represents a single-line input field where users can
enter text.

JAVA 65
It is used for accepting user input or displaying dynamic text.

Example:

TextField textField = new TextField(20);

6. TextArea:

The TextArea class represents a multi-line text area where users can
enter or view large amounts of text.

It is used for accepting or displaying longer text content.

Example:

TextArea textArea = new TextArea(5, 30);

7. Checkbox:

The Checkbox class represents a component that can be checked or


unchecked.

It is used for presenting options or boolean values.

Example:

Checkbox checkbox = new Checkbox("Remember me");

These are just a few examples of AWT components. AWT provides many more
components such as List, Choice, Scrollbar, Menu, etc., which you can use to
create rich and interactive GUIs in Java.
To use AWT components, you need to import the relevant classes from the
java.awt package.

Component Class and Container Class:


In Java's AWT (Abstract Window Toolkit), the Component class and the Container

class are fundamental classes that form the building blocks of graphical user
interfaces. These classes provide the foundation for creating and managing GUI
components within a window. Let's take a closer look at each class:

JAVA 66
1. Component Class:

The Component class is an abstract class that serves as the base class
for all AWT components.

It represents a visual entity that can be added to a graphical user


interface.

Examples of subclasses of Component include Button, Label, TextField,


etc.

Key methods and properties of the Component class include:

setSize(int width, int height) : Sets the size of the component.

setLocation(int x, int y) : Sets the position of the component within


its container.

setVisible(boolean visible) : Sets the visibility of the component.

setEnabled(boolean enabled) : Sets whether the component is enabled


or disabled.

setBackground(Color color) : Sets the background color of the


component.

setForeground(Color color) : Sets the foreground color (text color) of


the component.

2. Container Class:

The Container class is a subclass of Component and serves as a


container for other components.

It represents a rectangular area that can hold and organize other


components.

Examples of subclasses of Container include Panel, Frame, and


Window.

Key methods and properties of the Container class include:

add(Component comp) : Adds a component to the container.

remove(Component comp) : Removes a component from the container.

JAVA 67
setLayout(LayoutManager manager) : Sets the layout manager for the
container to define how components are arranged.

validate() : Forces the container to re-layout its components.

getComponent(int index): Returns the component at the specified


index within the container.

getComponents() : Returns an array of all the components in the


container.

The Component class and the Container class provide the basic functionality for
creating and managing GUI components in AWT. By subclassing these classes
and utilizing their methods and properties, you can build complex and interactive
user interfaces in Java.

Layout Manager Interface and Default Layouts:


In Java's AWT (Abstract Window Toolkit), the Layout Manager interface and its
various implementations are used to define how components are arranged and
displayed within containers. The Layout Manager provides automatic positioning
and sizing of components, ensuring that they adapt well to different window
sizes and screen resolutions. Let's explore the Layout Manager interface and
some of its default layouts:

1. Layout Manager Interface:

The LayoutManager interface is the base interface for all layout managers
in AWT.

It defines the methods that layout managers must implement to arrange


components within containers.

Key methods of the LayoutManager interface include:

void addLayoutComponent(String name, Component comp) : Adds a named


component to the layout.

void removeLayoutComponent(Component comp) : Removes a component


from the layout.

Dimension preferredLayoutSize(Container parent) : Returns the preferred


size of the layout.

JAVA 68
Dimension minimumLayoutSize(Container parent) : Returns the minimum
size of the layout.

void layoutContainer(Container parent) : Arranges the components


within the container.

2. Default Layouts:
AWT provides several default layout managers that implement the
LayoutManager interface. These layouts handle the positioning and sizing of

components in different ways. Here are some commonly used default


layouts:

FlowLayout:

The FlowLayout arranges components in a left-to-right flow, wrapping


to the next line when the current line is full.

It respects the preferred size of components and maintains their


relative order.

Example:

Container container = new Container();


container.setLayout(new FlowLayout());

BorderLayout:

The BorderLayout divides the container into five regions: North,


South, East, West, and Center.

Components are placed in these regions, and the layout manages


their sizing and positioning.

Example:

Container container = new Container();


container.setLayout(new BorderLayout());

GridLayout:

The GridLayout arranges components in a grid of rows and columns.

All components are evenly sized to fit the available space.

JAVA 69
Example:

Container container = new Container();


container.setLayout(new GridLayout(rows, columns));

GridBagLayout:

The GridBagLayout provides a flexible and powerful layout


management system.

It uses constraints to define the positioning and sizing of


components.

Example:

Container container = new Container();


container.setLayout(new GridBagLayout());

These are just a few examples of default layouts available in AWT. Each
layout manager has its own set of properties and behavior, allowing you to
choose the most appropriate one for your GUI design.

By utilizing the Layout Manager interface and selecting the appropriate layout
manager, you can create well-structured and responsive user interfaces in Java.

Insets and Dimensions:


In Java's AWT (Abstract Window Toolkit), Insets and Dimensions are classes
that provide useful functionality for managing the size and positioning of
components within containers. Let's explore these classes in more detail:

1. Insets:

The Insets class represents the borders of a container.

It defines the distances between the edges of a container and its


components.

Insets are often used to control the spacing between components or to


create padding around a container.

Key methods of the Insets class include:

JAVA 70
int top : Represents the top inset (distance from the top edge of the
container).

int left : Represents the left inset (distance from the left edge of
the container).

int bottom : Represents the bottom inset (distance from the bottom
edge of the container).

int right : Represents the right inset (distance from the right edge of
the container).

Example usage of Insets :

Insets insets = new Insets(top, left, bottom, right);

2. Dimensions:

The Dimension class represents the size of a component or container.

It encapsulates the width and height values.

Dimensions are commonly used to define the preferred, minimum, or


maximum size of components.

Key methods of the Dimension class include:

int width : Represents the width of the dimension.

int height : Represents the height of the dimension.

Example usage of Dimension :

Dimension dimension = new Dimension(width, height);

It's worth noting that both Insets and Dimension are immutable classes,
meaning their values cannot be changed once set. To modify their values,
you would typically create new instances with the desired values.

These classes are often used in conjunction with layout managers to control the
size, spacing, and positioning of components within containers. They provide a
convenient way to manage the visual aspects of GUI design.

JAVA 71
Border Layout:
The BorderLayout is a default layout manager provided by AWT.

It divides the container into five regions: North, South, East, West, and
Center.

Each region can hold only one component.

The components added to the BorderLayout are automatically resized to fit


their respective regions.

Example usage:

Container container = new Container();


container.setLayout(new BorderLayout());
container.add(component, BorderLayout.NORTH);

Flow Layout:
The FlowLayout is a default layout manager provided by AWT.

It arranges components in a left-to-right flow, wrapping to the next line when


the current line is full.

It respects the preferred size of components and maintains their relative


order.

Example usage:

Container container = new Container();


container.setLayout(new FlowLayout());
container.add(component1);
container.add(component2);

Grid Layout:

The GridLayout is a default layout manager provided by AWT.

It arranges components in a grid of rows and columns.

All components are evenly sized to fit the available space.

Example usage:

JAVA 72
Container container = new Container();
container.setLayout(new GridLayout(rows, columns));
container.add(component1);
container.add(component2);

These layout managers are commonly used in Java GUI applications to organize
components within containers. By selecting the appropriate layout manager and
adding components to the specified regions or grid cells, you can create visually
appealing and responsive user interfaces.

Card Layout:
The CardLayout is a default layout manager provided by AWT.

It allows multiple components to be stacked on top of each other, with only


one component visible at a time.

It is often used to create multi-pane interfaces, such as wizards or tabbed


views.

Components are added to the CardLayout using unique names, and you can
switch between components using methods like next() , previous() , or
show() .

Example usage:

Container container = new Container();


container.setLayout(new CardLayout());
container.add(component1, "card1");
container.add(component2, "card2");

Grid Bag Layout:


The GridBagLayout is a flexible layout manager provided by AWT.

It allows components to be arranged in a grid-like structure with


customizable cell sizes and positioning.

It offers fine-grained control over component placement using constraints


such as grid coordinates, weights, and alignments.

Example usage:

JAVA 73
Container container = new Container();
container.setLayout(new GridBagLayout());
GridBagConstraints constraints = new GridBagConstraints();
constraints.gridx = 0;
constraints.gridy = 0;
container.add(component1, constraints);
constraints.gridx = 1;
constraints.gridy = 0;
container.add(component2, constraints);

AWT Events:
AWT provides a set of event classes and interfaces to handle user
interactions with GUI components.

Some commonly used AWT events include:

ActionEvent: Generated when a button or menu item is clicked.

MouseEvent: Generated when the mouse is moved or clicked.

KeyEvent: Generated when a key is pressed or released.

WindowEvent: Generated when a window is opened, closed, or resized.

To handle AWT events, you need to implement event listener interfaces and
register them with the appropriate components using methods like
addActionListener() , addMouseListener() , or addKeyListener() .

Example usage:

button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Handle button click event
}
});

These layout managers and event handling mechanisms in AWT allow you to
create dynamic and interactive user interfaces in your Java applications.

Event Models:
In Java, there are different event models that define how events are generated,
dispatched, and handled in graphical user interfaces (GUIs). The two main event

JAVA 74
models used in Java are the AWT event model and the Swing event model. Let's
explore each of them:

1. AWT Event Model:

The AWT (Abstract Window Toolkit) event model is the original event
model in Java.

It is based on the delegation model, where components delegate event


handling to their registered listeners.

In the AWT event model, events are generated by the operating system
and delivered to the appropriate components.

The event handling process involves three main steps: event generation,
event dispatching, and event handling.

Key classes in the AWT event model include:

EventObject: The base class for all AWT events.

EventListener: The base interface for all AWT event listeners.

EventListenerProxy: A proxy class for event listeners.

EventListenerList: A container class to manage event listeners.

2. Swing Event Model:

The Swing event model is an enhanced version of the AWT event model
and is built on top of it.

It provides a more powerful and flexible event handling mechanism for


Swing components.

The Swing event model introduces the concept of "pluggable look and
feel" (PLAF) and "lightweight" components.

Swing events are dispatched by the Swing event-dispatching thread


(EDT), which ensures that all GUI events are handled in a single thread.

Key classes in the Swing event model include:

EventObject: The base class for all Swing events.

EventListener: The base interface for all Swing event listeners.

JAVA 75
EventListenerList: A container class to manage Swing event
listeners.

SwingUtilities: A utility class for working with Swing components and


threads.

Both event models provide a similar set of event classes and interfaces, but the
Swing event model offers more features and flexibility for GUI development.
To handle events in Java, you need to implement event listener interfaces and
register them with the appropriate components. Common event listener
interfaces include ActionListener, MouseListener, KeyListener, etc. The actual
event handling code is written within the implemented listener methods.
Example usage:

button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Handle button click event
}
});

textField.addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {
// Handle key press event
}

public void keyReleased(KeyEvent e) {


// Handle key release event
}

public void keyTyped(KeyEvent e) {


// Handle key typed event
}
});

By understanding the event models and utilizing event listeners, you can create
interactive and responsive GUI applications in Java.

Listeners:
In Java, listeners are interfaces that define callback methods to handle specific
events. They are used in event-driven programming to capture and respond to
user actions or system events. Here are the key points about listeners:

JAVA 76
Listeners are implemented as interfaces in Java. Each listener interface
defines one or more methods that need to be implemented to handle
specific events.

Listeners follow the Observer design pattern, where the listener objects
register themselves with the event source to receive notifications when the
corresponding events occur.

When an event occurs, the event source invokes the appropriate methods
on the registered listeners, passing relevant information about the event.

Listeners decouple the event source from event handling logic, allowing for
flexible and modular design of event-driven systems.

Here are a few commonly used listeners in Java:

1. ActionListener: Used to handle action events, such as button clicks or menu


item selections.

2. MouseListener: Handles mouse-related events, including mouse clicks,


movements, and enters/exits.

3. KeyListener: Deals with keyboard events, such as key presses, releases,


and typing.

4. ItemListener: Listens to item events, commonly used with checkboxes or


radio buttons.

5. FocusListener: Responds to focus events when a component gains or loses


focus.

6. WindowListener: Handles window-related events, like opening, closing, or


resizing of windows.

Example usage of ActionListener:

button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Handle button click event
}
});

Class Listener:

JAVA 77
The term "Class Listener" refers to a design pattern where a separate class is
created to implement a listener interface. This approach provides better
modularity and separation of concerns. Instead of using anonymous inner
classes or implementing the listener interface in the same class, you create a
separate class dedicated to handling events.
Here's an example of implementing a class listener:

1. Define a listener class that implements the desired listener interface and
overrides the corresponding event handling methods.

public class MyActionListener implements ActionListener {


public void actionPerformed(ActionEvent e) {
// Handle the action event here
}
}

1. Instantiate the listener class and register it with the event source.

MyActionListener listener = new MyActionListener();


button.addActionListener(listener);

Using class listeners allows for cleaner code organization, improved reusability,
and easier maintenance, especially when multiple components need to share
the same event handling logic.

Adapters:
In Java, adapters are classes that provide default implementations for listener
interfaces. They allow you to implement only the methods you need, rather than
having to provide empty implementations for all methods in the interface.
Adapters are useful when you want to listen to specific events and avoid writing
unnecessary code. Here are the key points about adapters:

Adapters are classes that implement a listener interface and provide default
implementations for all the methods in the interface.

The adapter class acts as a bridge between the event source and the
listener, allowing you to override only the methods you are interested in.

JAVA 78
By using adapters, you can avoid implementing all methods of the listener
interface, which can save time and reduce code clutter.

Here's an example to illustrate the usage of an adapter class:

1. Define an adapter class that extends the appropriate adapter class for the
listener interface you want to use. For example, if you want to handle mouse
events, you can extend the MouseAdapter class, which provides empty
implementations for all methods in the MouseListener interface.

public class MyMouseListener extends MouseAdapter {


public void mouseClicked(MouseEvent e) {
// Handle mouse click event
}
}

1. Instantiate the adapter class and register it with the event source.

MyMouseListener mouseListener = new MyMouseListener();


component.addMouseListener(mouseListener);

In this example, the MyMouseListener class extends the MouseAdapter class


and overrides the mouseClicked() method to handle mouse click events. By doing
so, you don't have to provide empty implementations for other methods in the
MouseListener interface.

Using adapters simplifies event handling code, especially when you are
interested in handling a specific subset of events provided by a listener interface.
It allows you to focus on the events that matter to you without having to
implement unnecessary methods.

Action Event Methods:


The ActionEvent class represents an action event, which is typically generated
by user actions like button clicks or menu item selections. It provides useful
methods to retrieve information about the event. Here are some commonly used
methods of the ActionEvent class:

1. getSource(): Returns the object that generated the event. Useful when
multiple components share the same ActionListener.

JAVA 79
Object source = event.getSource();

1. getActionCommand(): Returns the command string associated with the


event source. Useful when using the same ActionListener for multiple
components and distinguishing between them based on the command.

String command = event.getActionCommand();

1. getModifiers(): Returns the modifiers associated with the event, such as Ctrl,
Shift, or Alt keys.

int modifiers = event.getModifiers();

Focus Event Methods:


The FocusEvent class represents focus-related events, such as when a
component gains or loses focus. It provides methods to obtain information about
the event. Here are some commonly used methods of the FocusEvent class:

1. getSource(): Returns the object that generated the event.

Object source = event.getSource();

1. isTemporary(): Returns true if the focus change is temporary, such as when


a popup or dialog receives temporary focus.

boolean temporary = event.isTemporary();

Key Event Methods:


The KeyEvent class represents keyboard events, including key presses,
releases, and typed characters. It provides methods to retrieve information about
the event. Here are some commonly used methods of the KeyEvent class:

1. getKeyCode(): Returns the unique code for the key that triggered the event.

JAVA 80
int keyCode = event.getKeyCode();

1. getKeyChar(): Returns the character representation of the key that triggered


the event.

char keyChar = event.getKeyChar();

1. isShiftDown(), isCtrlDown(), isAltDown(): Returns true if the Shift, Ctrl, or Alt


key was pressed during the event.

boolean shiftDown = event.isShiftDown();


boolean ctrlDown = event.isCtrlDown();
boolean altDown = event.isAltDown();

These methods allow you to extract relevant information from ActionEvent,


FocusEvent, and KeyEvent objects and perform appropriate actions based on
the user's input or component focus.

Mouse Events:
Mouse events in Java are triggered by user interactions with the mouse, such as
clicking, moving, or scrolling. There are several types of mouse events available
in Java, including:

1. MouseListener: This interface provides methods to handle basic mouse


events. Some commonly used methods include:

mouseClicked(MouseEvent e): Invoked when the mouse is clicked


(pressed and released) on a component.

mousePressed(MouseEvent e): Invoked when a mouse button is


pressed on a component.

mouseReleased(MouseEvent e): Invoked when a mouse button is


released on a component.

mouseEntered(MouseEvent e): Invoked when the mouse enters a


component.

JAVA 81
mouseExited(MouseEvent e): Invoked when the mouse exits a
component.

2. MouseMotionListener: This interface provides methods to handle mouse


motion events. Some commonly used methods include:

mouseMoved(MouseEvent e): Invoked when the mouse is moved


without any buttons being pressed.

mouseDragged(MouseEvent e): Invoked when the mouse is moved with


a button being pressed.

3. MouseWheelListener: This interface provides a method to handle mouse


wheel events.

mouseWheelMoved(MouseWheelEvent e): Invoked when the mouse


wheel is rotated.

Here's an example of implementing a MouseListener and MouseMotionListener:

class MyMouseListener implements MouseListener, MouseMotionListener {


public void mouseClicked(MouseEvent e) {
// Handle mouse clicked event
}

public void mousePressed(MouseEvent e) {


// Handle mouse pressed event
}

public void mouseReleased(MouseEvent e) {


// Handle mouse released event
}

public void mouseEntered(MouseEvent e) {


// Handle mouse entered event
}

public void mouseExited(MouseEvent e) {


// Handle mouse exited event
}

public void mouseMoved(MouseEvent e) {


// Handle mouse moved event
}

public void mouseDragged(MouseEvent e) {


// Handle mouse dragged event
}
}

JAVA 82
Window Events:
Window events are related to the state and behavior of windows, such as
opening, closing, resizing, or moving windows. In Java, you can handle window
events using the WindowListener interface. Some commonly used methods in
the WindowListener interface include:

windowOpened(WindowEvent e): Invoked when a window is first opened.

windowClosing(WindowEvent e): Invoked when the user attempts to close


the window.

windowClosed(WindowEvent e): Invoked when the window has been closed.

windowIconified(WindowEvent e): Invoked when the window is minimized


(iconified).

windowDeiconified(WindowEvent e): Invoked when the window is restored


from a minimized state.

windowActivated(WindowEvent e): Invoked when the window becomes the


active window.

windowDeactivated(WindowEvent e): Invoked when the window is no longer


the active window.

To handle window events, you can implement the WindowListener interface:

class MyWindowListener implements WindowListener {


public void windowOpened(WindowEvent e) {
// Handle window opened event
}

public void windowClosing(WindowEvent e) {


// Handle window closing event
}

public void windowClosed(WindowEvent e) {


// Handle window closed event
}

public void windowIconified(WindowEvent e) {


// Handle window iconified event
}

public void windowDeiconified(WindowEvent e) {


// Handle window deiconified event

JAVA 83
}

public void windowActivated(WindowEvent e) {


// Handle window activated event
}

public void windowDeactivated(WindowEvent e) {


// Handle window deactivated event
}
}

You can then attach the window listener to a window object using the add
WindowListener() method.

frame.addWindowListener(new MyWindowListener());

By implementing the appropriate listener interfaces and their methods, you can
respond to user interactions with the mouse and handle various window-related
events in your Java programs.

UNIT - 4
Input/Output Streams:
In Java, input and output streams are used to handle the flow of data between a
program and an external source or destination, such as files, network
connections, or standard input/output. Streams provide a convenient way to read
data from and write data to these sources or destinations. Here are the key
points about input and output streams:
Input Streams: Input streams are used to read data from a source. They
provide methods to read different types of data, such as bytes, characters, or
objects, from the source. Some commonly used input stream classes include:

FileInputStream: Reads data from a file as a sequence of bytes.

FileReader: Reads data from a file as a sequence of characters.

ObjectInputStream: Reads serialized objects from a source.

JAVA 84
Example of reading from a file using FileInputStream:

try (FileInputStream fis = new FileInputStream("file.txt")) {


int byteData;
while ((byteData = fis.read()) != -1) {
// Process the byte data
}
} catch (IOException e) {
e.printStackTrace();
}

Output Streams: Output streams are used to write data to a destination. They
provide methods to write different types of data to the destination. Some
commonly used output stream classes include:

FileOutputStream: Writes data to a file as a sequence of bytes.

FileWriter: Writes data to a file as a sequence of characters.

ObjectOutputStream: Writes serialized objects to a destination.

Example of writing to a file using FileOutputStream:

try (FileOutputStream fos = new FileOutputStream("file.txt")) {


String data = "Hello, World!";
byte[] byteData = data.getBytes();
fos.write(byteData);
} catch (IOException e) {
e.printStackTrace();
}

Byte Streams vs. Character Streams: Byte streams are used for reading and
writing binary data, while character streams are used for reading and writing
textual data. Character streams automatically handle character encoding and
decoding, making them suitable for working with text files.
Example of using character streams:

try (FileReader reader = new FileReader("file.txt")) {


int charData;
while ((charData = reader.read()) != -1) {
// Process the character data
}
} catch (IOException e) {

JAVA 85
e.printStackTrace();
}

These are some of the basic concepts and classes related to input and output
streams in Java. They provide a flexible and efficient way to handle data input
and output in various scenarios.

Stream Filters:
Stream filters in Java provide a way to transform data as it passes through an
input or output stream. They are used to perform operations such as
compression, encryption, or data manipulation on the data being read from or
written to a stream. Stream filters are implemented using the decorator design
pattern, where a filter class wraps around an existing stream and modifies its
behavior.
The java.io package provides several classes for stream filters, including:

1. FilterInputStream and FilterOutputStream: These are abstract classes that


serve as the base classes for input and output stream filters. They provide a
default implementation that simply passes the data through unchanged. You
can extend these classes to create your own custom stream filters.

2. BufferedInputStream and BufferedOutputStream: These are examples of


stream filters that provide buffering capabilities. They wrap around an
existing input or output stream and improve performance by reducing the
number of I/O operations. BufferedInputStream reads data from an
underlying stream in chunks and stores it in an internal buffer, while
BufferedOutputStream writes data to an internal buffer before flushing it to
the underlying stream.

Example of using BufferedInputStream:

try (FileInputStream fis = new FileInputStream("file.txt");


BufferedInputStream bis = new BufferedInputStream(fis)) {
int byteData;
while ((byteData = bis.read()) != -1) {
// Process the byte data
}
} catch (IOException e) {
e.printStackTrace();
}

JAVA 86
Data Input and Output Stream:
DataInputStream and DataOutputStream classes are used for reading and
writing primitive data types from/to a stream. They provide methods to read and
write data in a machine-independent format. These classes are particularly
useful when you need to exchange data between different platforms or systems.
Example of using DataOutputStream:

try (FileOutputStream fos = new FileOutputStream("file.dat");


DataOutputStream dos = new DataOutputStream(fos)) {
int intValue = 42;
double doubleValue = 3.14;
dos.writeInt(intValue);
dos.writeDouble(doubleValue);
} catch (IOException e) {
e.printStackTrace();
}

Example of using DataInputStream:

try (FileInputStream fis = new FileInputStream("file.dat");


DataInputStream dis = new DataInputStream(fis)) {
int intValue = dis.readInt();
double doubleValue = dis.readDouble();
// Process the read values
} catch (IOException e) {
e.printStackTrace();
}

The DataInputStream and DataOutputStream classes allow you to write and


read data in a specific format, ensuring consistency across different platforms.
These are some of the concepts and classes related to stream filters, buffered
streams, and data input/output streams in Java. They provide additional
functionalities and enhancements to the basic input/output stream operations.

Print Stream:
PrintStream is a class in Java that provides methods for printing formatted
representations of data to an output stream. It is commonly used to write text
data to the console or to a file. PrintStream extends the OutputStream class and
provides additional print and println methods for convenient printing.

JAVA 87
Example of using PrintStream to write to console:

import java.io.PrintStream;

public class PrintStreamExample {


public static void main(String[] args) {
PrintStream ps = System.out;
ps.println("Hello, World!");
ps.printf("The value of pi is %.2f", Math.PI);
ps.close();
}
}

In this example, we create a PrintStream object ps which represents the


standard output stream. We use the println method to print a line of text to the
console, and the printf method to print formatted text, including the value of pi
with two decimal places.

Random Access File:


RandomAccessFile is a class in Java that allows random access to the contents
of a file. Unlike other stream classes, RandomAccessFile supports both reading
and writing operations at any position within the file. It provides methods for
seeking to a specific position, reading data from that position, and writing data to
that position.
Example of using RandomAccessFile to read and write data:

import java.io.IOException;
import java.io.RandomAccessFile;

public class RandomAccessFileExample {


public static void main(String[] args) {
try (RandomAccessFile file = new RandomAccessFile("data.txt", "rw")) {
// Writing data
file.writeInt(42);
file.writeDouble(3.14);

// Seeking to a specific position


file.seek(0);

// Reading data
int intValue = file.readInt();
double doubleValue = file.readDouble();

System.out.println("Read values: " + intValue + ", " + doubleValue);

JAVA 88
} catch (IOException e) {
e.printStackTrace();
}
}
}

In this example, we create a RandomAccessFile object file with the file name
"data.txt" and mode "rw" (read-write). We write an integer value and a double
value to the file using the writeInt and writeDouble methods. Then, we seek to
the beginning of the file using the seek method. Finally, we read the values back
from the file using the readInt and readDouble methods.
RandomAccessFile provides flexibility in accessing specific parts of a file,
making it useful for scenarios where you need to read or modify data at arbitrary
positions within a file.

JDBC (Database connectivity with MS-Access, Oracle, MS-


SQL Server)
JDBC (Java Database Connectivity) is a Java API that provides a standard way
to interact with relational databases. It enables Java applications to connect to a
database, execute SQL queries, retrieve and manipulate data, and manage
database transactions. Here's an overview of JDBC and how it can be used to
connect to different databases:

1. JDBC Architecture:

DriverManager: The central class that manages the JDBC drivers. It is


responsible for establishing a connection to the database.

Connection: Represents a connection to a database. It provides


methods to create statements, manage transactions, and close the
connection.

Statement: Used to execute SQL queries or updates in the database.

ResultSet: Represents the result of a query. It allows you to retrieve and


manipulate data returned from the database.

2. Database Connectivity Steps:

Load the JDBC driver: Before connecting to a database, you need to


load the appropriate JDBC driver class using Class.forName() . Each

JAVA 89
database vendor provides its own JDBC driver that you need to include
in your project.

Establish a connection: Use the DriverManager.getConnection() method to


establish a connection to the database by providing the database URL,
username, and password.

Execute SQL statements: Create a Statement or PreparedStatement object


from the Connection and execute SQL queries or updates using
executeQuery() or executeUpdate() methods.

Process the results: If executing a query, retrieve the results using the
ResultSet object and process the data as needed.

Close the resources: Close the ResultSet, Statement, and Connection


objects after you have finished working with them.

Example of connecting to a database and executing a query using JDBC:

import java.sql.*;

public class JDBCTest {


public static void main(String[] args) {
String jdbcUrl = "jdbc:mysql://localhost:3306/mydatabase";
String username = "myuser";
String password = "mypassword";

try (Connection conn = DriverManager.getConnection(jdbcUrl, username, pass


word);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {

while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
System.out.println("ID: " + id + ", Name: " + name);
}

} catch (SQLException e) {
e.printStackTrace();
}
}
}

1. Database-specific Drivers:

JAVA 90
MS-Access: To connect to an MS-Access database, you need to use the
JDBC-ODBC Bridge driver. This driver acts as a bridge between the
JDBC API and the ODBC API. You'll need to set up the ODBC data
source for your MS-Access database and use it in the JDBC URL.

Oracle: To connect to an Oracle database, you need to download and


include the Oracle JDBC driver in your project. The JDBC URL typically
includes the database host, port, service name, and
username/password.

MS-SQL Server: To connect to an MS-SQL Server database, you need


to download and include the SQL Server JDBC driver in your project.
The JDBC URL typically includes the database host, port, database
name, and username/password.

Note: The specific details of connecting to each database may vary, so refer to
the documentation and JDBC driver documentation provided by the respective
database vendor for the exact configurations and usage.

These are some of the concepts and steps involved in using JDBC for database
connectivity in Java, including connecting to different databases such as MS-
Access, Oracle, and MS-SQL Server.

Object Serialization
Object serialization in Java refers to the process of converting an object into a
stream of bytes, which can be written to a file or transmitted over a network.
Serialization allows objects to be saved or transmitted and later deserialized
back into objects. This is useful for scenarios such as persisting object state,
sending objects over a network, or storing objects in a database.
To make a class serializable, it needs to implement the Serializable interface,
which is a marker interface with no methods. By default, all of the class's non-
transient instance variables are serialized. Here's an example:

import java.io.*;

public class SerializationExample {


public static void main(String[] args) {
// Create an object to serialize
Person person = new Person("John Doe", 30);

// Serialize the object to a file

JAVA 91
try (FileOutputStream fileOut = new FileOutputStream("person.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut)) {
out.writeObject(person);
System.out.println("Object serialized successfully.");
} catch (IOException e) {
e.printStackTrace();
}

// Deserialize the object from the file


try (FileInputStream fileIn = new FileInputStream("person.ser");
ObjectInputStream in = new ObjectInputStream(fileIn)) {
Person deserializedPerson = (Person) in.readObject();
System.out.println("Deserialized object: " + deserializedPerson);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}

class Person implements Serializable {


private String name;
private int age;

public Person(String name, int age) {


this.name = name;
this.age = age;
}

// Getters and setters

@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}

In this example, the Person class implements the Serializable interface, which
allows instances of the class to be serialized and deserialized. The Person object
is serialized by writing it to a file using ObjectOutputStream , and then it is
deserialized by reading from the file using ObjectInputStream . The deserialized
object is cast back to the Person type.

Sockets
Sockets in Java provide a way to establish communication channels between
two different programs running on the same machine or over a network. They
facilitate client-server communication by allowing data to be sent and received

JAVA 92
across network connections. Java provides the Socket and ServerSocket classes
to handle network communication using sockets.

Client Socket: The Socket class represents a client-side socket that initiates
a connection to a server. It provides methods to establish a connection, send
data to the server, and receive data from the server.

Server Socket: The ServerSocket class represents a server-side socket that


listens for incoming client connections. It provides methods to accept client
connections and create individual Socket objects for each client connection.
The server can then send and receive data with the client using the Socket

object.

Here's an example of a simple client-server communication using sockets:

// Server code
import java.io.*;
import java.net.*;

public class Server {


public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(1234);
Socket clientSocket = serverSocket.accept();
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), tru
e);
BufferedReader in = new BufferedReader(new InputStreamReader(clientSo
cket.getInputStream()))) {

System.out.println("Server: Client connected");

String inputLine;
while ((inputLine = in.readLine()) != null) {
System.out.println("Server: Received from client: " + inputLine);
out.println

("Server: Received your message: " + inputLine);


}
} catch (IOException e) {
e.printStackTrace();
}
}
}

// Client code
import java.io.*;
import java.net.*;

public class Client {


public static void main(String[] args) {

JAVA 93
try (Socket socket = new Socket("localhost", 1234);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.g
etInputStream()));
BufferedReader stdIn = new BufferedReader(new InputStreamReader(Syste
m.in))) {

String userInput;
while ((userInput = stdIn.readLine()) != null) {
out.println(userInput);
System.out.println("Client: Sent to server: " + userInput);
String response = in.readLine();
System.out.println("Client: Received from server: " + response);
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

In this example, the server listens on port 1234 using a ServerSocket . When a
client connects, it accepts the connection and creates input and output streams
to communicate with the client. The server receives messages from the client,
prints them, and sends a response back.
The client creates a Socket object to connect to the server's IP address and port.
It also creates input and output streams to communicate with the server. The
client reads input from the user, sends it to the server, and displays the response
received from the server.

Development of client Server applications


1. Define the Application Protocol:

Decide on the communication protocol, such as TCP/IP, HTTP, or


WebSocket, that will be used for client-server communication. This
choice will influence the libraries and frameworks you use.

2. Implement the Server-Side Application:

Create a Java class to represent the server application.

Use the ServerSocket class from the java.net package to listen for
incoming client connections.

JAVA 94
Accept client connections by calling the accept() method of the
ServerSocket class, which returns a Socket object representing the client

connection.

Spawn a new thread or use a thread pool to handle each client request
separately, allowing multiple clients to connect concurrently.

Receive and parse requests from clients by reading from the input
stream of the Socket object.

Perform necessary processing or interact with a database or other


external resources to handle the client request.

Generate an appropriate response and send it back to the client by


writing to the output stream of the Socket object.

3. Implement the Client-Side Application:

Create a Java class to represent the client application.

Use the Socket class from the java.net package to establish a


connection with the server by providing the server's IP address and port
number.

Send requests to the server by writing to the output stream of the Socket

object.

Wait for the server's response by reading from the input stream of the
Socket object.

Process the response received from the server and take appropriate
action based on the application logic.

4. Test and Debug:

Run both the server-side and client-side applications and verify that they
can communicate properly.

Test various scenarios, such as handling multiple client connections,


sending different types of requests, and handling error conditions.

Debug any issues or errors that arise during testing, such as incorrect
data transmission or unexpected behavior.

5. Enhance and Scale the Application:

JAVA 95
Add additional functionality to the client and server applications as
needed.

Consider using higher-level frameworks or libraries, such as Java


Servlets or Spring Boot, to simplify development and provide additional
features.

Optimize the code for performance and scalability, ensuring efficient


resource usage and minimizing response times.

Implement proper error handling and exception management to handle


edge cases and ensure robustness.

6. Deploy and Maintain the Application:

Deploy the server-side application on a server or hosting platform that


allows for incoming client connections.

Distribute the client-side application to end-users or provide instructions


for accessing and installing it.

Monitor the application's performance, handle maintenance tasks, and


apply updates or patches as needed.

Ensure security by following best practices, such as input validation,


authentication, and secure communication protocols.

Java provides built-in classes and libraries for socket-based communication,


making it relatively straightforward to develop client-server applications.
However, depending on the complexity of your application and specific
requirements, you may consider using frameworks like Java Servlets, Spring
Boot, or Netty, which offer higher-level abstractions and additional features to
simplify client-server development.

Design of multithreaded server


Designing a multithreaded server involves creating a server application that can
handle multiple client connections concurrently using threads. Here's a basic
outline of the design:

1. Create a Server Socket:

Use the ServerSocket class from the java.net package to create a server
socket that listens for incoming client connections on a specific port.

JAVA 96
Choose an appropriate port number for your server.

2. Accept Client Connections:

Create a loop to continuously accept client connections using the


accept() method of the ServerSocket class.

For each client connection, create a new thread or use a thread pool to
handle the client's requests separately.

3. Handle Client Requests:

For each client connection, create a new thread or task to handle the
client's requests.

Inside the thread or task, use the Socket object representing the client
connection to communicate with the client.

Read data from the input stream of the Socket to receive client requests.

Process the client's requests according to your application's logic.

Send responses back to the client by writing data to the output stream of
the Socket .

4. Manage Thread Safety:

Ensure thread safety when accessing shared resources or data


structures by using appropriate synchronization mechanisms, such as
locks or synchronized blocks/methods.

If multiple client threads need to access shared resources


simultaneously, implement thread-safe data structures or use
concurrency utilities provided by Java, such as ConcurrentHashMap or
Atomic classes.

5. Graceful Thread Termination:

Handle proper termination of client threads when clients disconnect or


the server is shutting down.

Implement a mechanism to detect client disconnections and clean up


any resources associated with the disconnected client.

Provide a way to gracefully stop the server, which includes stopping new
client connections and waiting for existing client threads to finish their

JAVA 97
work before exiting.

6. Error Handling and Logging:

Implement proper error handling to handle exceptions that may occur


during client communication, such as network errors or invalid client
requests.

Use logging frameworks, such as Log4j or Java's built-in logging


framework, to log important events, errors, and debugging information
for troubleshooting purposes.

7. Scalability and Performance Considerations:

If your server needs to handle a large number of concurrent


connections, consider using a thread pool to limit the number of active
threads and manage resources efficiently.

Optimize your code and algorithms to minimize response times and


improve overall performance.

Consider using non-blocking I/O and asynchronous programming


models, such as Java NIO or frameworks like Netty, to handle a high
number of connections with lower resource usage.

Remember to thoroughly test your multithreaded server application to ensure it


handles multiple client connections correctly and performs well under various
scenarios. Pay attention to thread safety, synchronization, and error handling to
avoid concurrency issues and provide a reliable server experience.
Note: The implementation details of a multithreaded server can vary depending
on your specific requirements, the Java version you're using, and the
libraries/frameworks employed. The outline provided above serves as a general
guide to get started with designing a multithreaded server in Java.

Remote Method invocation


Remote Method Invocation (RMI) is a mechanism in Java that allows objects in
one Java Virtual Machine (JVM) to invoke methods on objects located in another
JVM. It enables distributed computing by providing a way for Java programs to
communicate and interact across different JVMs, even on different machines.

Here's an overview of how RMI works:

JAVA 98
1. Define Remote Interface:

Create an interface that defines the methods that can be invoked


remotely. This interface should extend the java.rmi.Remote interface, and
each method should throw java.rmi.RemoteException .

2. Implement Remote Class:

Create a class that implements the remote interface. This class


represents the actual object that will be accessed remotely.

Extend the java.rmi.server.UnicastRemoteObject class to provide the


necessary remote object functionality.

Implement the methods defined in the remote interface.

3. Start RMI Registry:

The RMI registry acts as a lookup service for remote objects. It


maintains a registry of remote objects and their associated names.

Start the RMI registry using the rmiregistry command or


programmatically using the LocateRegistry.createRegistry() method.

4. Register Remote Object:

Register the remote object with the RMI registry using the rebind()

method of the java.rmi.Naming class.

Associate a unique name with the remote object in the registry.

5. Create Client Application:

Create a separate Java application that will act as the client.

Obtain a reference to the remote object from the RMI registry using the
lookup() method of the java.rmi.Naming class.

6. Invoke Remote Methods:

Use the obtained reference to the remote object to invoke methods as if


it were a local object.

The RMI framework handles the underlying communication and


marshalling of method arguments and return values between the client
and server JVMs.

JAVA 99
7. Handle Exceptions:

Both the remote interface and the remote object implementation should
throw java.rmi.RemoteException to handle communication-related
exceptions that may occur during remote method invocation.

8. Security Considerations:

Configure security settings to ensure secure communication between


the client and server JVMs.

Provide appropriate access control and authentication mechanisms to


protect the integrity and confidentiality of the remote method invocations.

RMI simplifies distributed computing in Java by abstracting the complexity of


network communication and providing a transparent way to invoke methods on
remote objects. It enables the development of distributed applications where
objects in different JVMs can interact seamlessly.

Note: RMI has been part of the Java platform for many years, but there are
alternative technologies available today for distributed computing in Java, such
as Java Remote Method Invocation over Internet Inter-ORB Protocol (RMI-IIOP),
Java Message Service (JMS), and web services.

Java Native interfaces and Development of a JNI based


application
Java Native Interface (JNI) is a programming framework that allows Java code to
call native code written in languages like C or C++. JNI enables developers to
integrate existing native code libraries or take advantage of platform-specific
functionality in their Java applications. Here's an overview of JNI and the
development process of a JNI-based application:

1. Understanding JNI Basics:

JNI provides a set of functions and rules for communication between


Java and native code.

Java classes can define native methods, which are declared using the
native keyword and implemented in native code.

The Java Native Interface allows for passing data and invoking native
functions from Java code and vice versa.

JAVA 100
2. Write Native Code:

Create the native code implementation in a language like C or C++.

The native code should match the function signatures declared in the
native methods of the corresponding Java classes.

Include the necessary header files, such as jni.h , which provide the
JNI function prototypes and macros.

3. Create JNI Wrapper:

Create a Java class that serves as a wrapper for the native code. This
class will contain the native method declarations.

Use the native keyword to declare the methods that will be


implemented in the native code.

Load the native library that contains the native code using the
System.loadLibrary() method.

4. Generate Header File:

Use the Java javac command with the h option to generate the C/C++
header file for the Java class that contains native method declarations.

This header file will be used in the native code implementation.

5. Implement Native Methods:

Implement the native methods in the native code file (.c or .cpp) using
the function signatures defined in the generated header file.

Use the JNI function calls to interact with Java objects, invoke Java
methods, get/set field values, and handle exceptions.

JNI provides functions like JNIEnv for accessing Java environment,


jclass for obtaining class references, jmethodID for identifying Java
methods, and various other utility functions for data conversion.

6. Compile and Link:

Compile the native code file (.c or .cpp) using a C/C++ compiler and
generate a shared library file (e.g., a DLL on Windows or a shared
object on Unix-like systems).

JAVA 101
Ensure that the necessary JNI library files are included during
compilation and linking.

7. Load and Run the JNI-Based Application:

Place the generated shared library file in a location accessible to the


Java application.

Load the native library using the System.loadLibrary() method in the Java
code before invoking the native methods.

The JVM will load the shared library and make the native methods
available for invocation.

8. Test and Debug:

Run the Java application and test the functionality of the JNI-based
code.

Debug any issues that arise during execution, such as incorrect data
handling or compatibility problems between Java and native code.

9. Manage Memory and Resource Cleanup:

JNI requires manual memory management for objects and resources


allocated in native code.

Use JNI functions like NewObject , NewArray , and NewGlobalRef to allocate


memory in native code and DeleteLocalRef or DeleteGlobalRef to release
the allocated memory.

10. Performance Considerations:

Optimize the native code for performance, especially if it involves


computationally intensive tasks or time-critical operations.

Take advantage of native code optimizations, such as leveraging


platform-specific libraries or utilizing low-level programming techniques.

JNI allows you to leverage existing native code libraries or access platform-
specific functionality from your Java applications. It provides a bridge between
Java and native code, enabling you to combine the portability of Java with the
power and
flexibility of native languages.

JAVA 102
Note: When working with JNI, it's important to exercise caution as it involves
direct interaction with native code, which can introduce potential security risks
and compatibility issues.

Java Collection API Interfaces


1. Collection Interface:

Root interface of the Collection API hierarchy.

Defines common methods for all collections.

2. List Interface:

Represents an ordered collection with duplicate elements.

Key implementations: ArrayList, LinkedList.

3. Set Interface:

Represents a collection with unique elements (no duplicates).

Key implementations: HashSet, LinkedHashSet.

4. Queue Interface:

Represents a collection for holding elements prior to processing.

Follows FIFO or priority-based order.

Key implementations: LinkedList, PriorityQueue.

5. Map Interface:

Represents a collection of key-value pairs.

Key implementations: HashMap, LinkedHashMap, TreeMap.

Other Classes and Concepts:


1. Vector Class:

Dynamic array-like data structure, similar to ArrayList.

Synchronized (thread-safe).

Methods for element manipulation and access.

2. Stack Class:

Subclass of Vector, implements a LIFO stack.

JAVA 103
Methods for stack operations like push, pop, and peek.

3. Hashtable Class:

Implements a hashtable data structure for key-value pairs.

Synchronized (thread-safe).

Methods for element manipulation and access.

4. Enumerations:

Interface for iterating over a collection of elements sequentially.

5. Set Interface:

Represents a collection with unique elements (no duplicates).

6. List Interface:

Represents an ordered collection with duplicate elements.

7. Map Interface:

Represents a collection of key-value pairs.

8. Iterators:

Objects for iterating over elements in a collection.

Methods like hasNext and next for sequential iteration.

These concepts and classes form the foundation for working with collections in
Java, allowing you to store, manipulate, and iterate over groups of elements
efficiently.

JAVA 104

You might also like