8. ClassNotes
8. ClassNotes
The java.io package is used to handle input and output operations. Java IO has various classes that handle input
and output sources. A stream is a sequence of data.
Java input stream classes can be used to read data from input sources such as keyboard or a file. Similarly
output stream classes can be used to write data on a display or a file again.
Before understanding IO streams, let us discuss streams. A Stream is also a sequence of data. It is neither a
data structure nor a store of data. For example, a river stream is where water flows from source to destination.
Similarly, these are data streams; data flows through one point to another.
The java.io package helps the user perform all input-output operations. Java IO package is primarily focused on
input-output files, network streams, internal memory buffers, etc. Data is read and written from Java
IO's InputStream and OutputStream classes.
In other words, IO streams in Java help to read the data from an input stream, such as a file and write the data
into an output stream, such as the standard display or a file again. It represents the source as input and the
destination as output. It can handle all types of data, from primitive values to advanced objects.
The java.io package consists of output and input streams used to write and read data to files or other output and
input sources.
Input Streams.
Output Streams.
Error Streams.
Java supports three streams that are automatically attached with the console.
Input Streams
As we know, an input source consists of data that needs to be read in order to extract information from it. Input
Streams help us read data from the input source. They are an abstract class that provides a programming
interface for all input streams.
Input streams are opened implicitly as soon as they are created. We use a close() method on the source object to
close the input stream.
Output Streams
The executed program's output must be stored in a file for further use. Output streams help us write data to an
output source(such as a file). Similarly to input streams, output streams are abstract classes that provide a
programming interface for all output streams.
The output stream is opened as soon as it is created and explicitly closed using the "close() "method.
Error Streams
Error streams are the same as output streams. In some ide’s, the error is displayed in different colors (other than
the color of the output color). It gives output on the console the same as output streams.
In daily work, we do not enter input into programs manually. Also, the program's result needs to be stored
somewhere for further use.
So, Java IO streams provide input and output streams that help us extract data from files and write the data into
them. Normally, we can create, delete, and edit files using Java.io.
In short, all file manipulation is done using "Java IO streams, " which also handle user input functionality.
Depending on the types of operations, streams are divided into 2 primary classes.
Input Stream
It is an abstract superclass of the java.io package and is used to read the data from an input source. In other
words, it means reading data from files, using a keyboard, etc. We can create an object of the input stream class
using the new keyword. The input stream class has several types of constructors.
The following code takes the file name as a string, to read the data stored in the file.
The method above returns the data of the next byte in the input stream. The value returned is between 0and 255.
If no byte is read, the code returns -1, indicating the file's end.
The method above returns the number of bytes that can be read from the input stream.
The method above closes the current input stream and releases any associated system resources .
It marks the current position in the input stream. The readlimit argument tells the input stream to read that many
bytes before the mark position becomes invalid.
It tells whether a particular input stream supports the mark() and reset() method. It returns true if the particular
input stream supports the mark and reset methods or returns false.
It reads up to len bytes of data from the input stream and returns the total number of bytes stored in the buffer.
Here, the “off” is the start offset in buffer array b where the data is written, and the “len” represents the
maximum number of bytes to read.
It repositions the stream to the last called mark position. The reset method does nothing for input stream class
except throwing an exception.
Examples
1. In the below example, we will use FileInputStream class to read the input file: input.txt.
Scaler Topics
From InterviewBit
Code:
import java.io.*;
class Main {
// initializing x to 0
int x = 0;
// while loop untill the end of the file.
while ((x = f.read()) != -1) {
// printing the character
System.out.print((char) x);
}
// closing a file
f.close();
} catch (Exception e) {
// printing exception
System.out.println(e);
}
}
}
Output:
Scaler Topics
From InterviewBit
2. In the below example, we will use BufferedInputStream class to read the file.
Code:
import java.io.*;
class Main {
int x = 0;
// while loop untill the end of the file.
while ((x = f2.read()) != -1) {
// printing the character
System.out.print((char) x);
}
System.out.println();
// closing a file
f2.close();
} catch (Exception e) {
// printing exception
System.out.println(e);
}
}
}
Output:
Available bytes: 31
Scaler Topics
From InterviewBit
3. In the below example, we will use the ByteArrayInputStream class to read the file.
Code:
import java.io.*;
class Main {
x = b1.read();
while (x != -1) {
System.out.print((char) x);
x = b1.read();
}
System.out.println();
// close the input stream
b1.close();
} catch (Exception e) {
// printing exception
System.out.println(e);
}
}
}
Output:
Scaler Topics
From InterviewBit
Output Stream
It is an abstract superclass of the java.io package that writes data to an output resource, which is, in other words,
writing the data into files. We can create an object of the output stream class using the new keyword. The output
stream class has several types of constructors.
OutputStream Hierarchy
This method closes the current output stream and releases any associated system resources. The closed stream
cannot be reopened, and operations cannot be performed on it.
It flushes the current output stream and forces any buffered output to be written out.
This method writes the b.length bytes from the specified byte array to the output stream.
It writes up to len bytes of data for the output stream. Here, the “off” is the start offset in buffer array b, and
the “len” represents the maximum number of bytes to be written in the output stream.
The method above writes the specific bytes to the output stream. It does not return a value.
Some methods that are inherited from class java.lang.Object. These methods are used for both input stream and
output stream purposes.
Example: clone, equals, finalise, getclass, hashCode, notify, notifyAll, toString, wait.
Examples
1. In the example below, we will use the FileOutputStream class to read the file.
Code:
import java.io.*;
class Main {
Output:
Scaler Topics
2. In the example below, we will use the BufferedOutputStream class to read the file.
Code:
import java.io.*;
class Main {
// declaring a f1 as BufferedOutputStream
BufferedOutputStream f2 = new BufferedOutputStream(f1);
Output:
Scaler Topics
3. In the below example, we will use the ByteArrayOutputStream class to read the file.
Code:
import java.io.*;
class Main {
// declaring ByteArrayOutputStream
ByteArrayOutputStream b1 = new ByteArrayOutputStream();
// closing a file
b1.close();
} catch (Exception e) {
// printing exception
System.out.println(e);
}
}
}
Output:
In Java, streams provide a powerful way to process data in a declarative manner, especially when dealing with
collections or I/O operations. Two important concepts related to streams are filtering and piping:
Filtering Streams
Filtering streams allows you to selectively process elements based on certain criteria. In Java, you can filter
streams using the filter() method, which takes a Predicate as an argument. Here’s how it works:
java
Copy code
List<String> names = Arrays.asList("Alice", "Bob", "Charlie",
"David", "Emma");
Piping Streams
Piping streams, also known as chaining operations, involves connecting multiple stream operations together to
form a pipeline. Each operation in the pipeline processes the data and passes it to the next operation. This allows
for a concise and readable way to transform data sequentially.
java
Copy code
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Filter even numbers, then map to their squares, and then print
numbers.stream()
.filter(num -> num % 2 == 0)
.map(num -> num * num)
.forEach(System.out::println); // Output: 4, 16, 36, 64, 100
Here, .filter(num -> num % 2 == 0) filters out odd numbers, .map(num -> num
* num) squares the even numbers, and .forEach(System.out::println) prints
each squared number.
2. Intermediate and Terminal Operations: Stream operations are categorized as
intermediate (like filter, map, sorted) and terminal (like forEach, collect,
reduce). Intermediate operations return a new stream, allowing for chaining, while
terminal operations produce a result or side-effect.
When dealing with I/O streams, you can apply similar principles to filter and process data:
java
Copy code
import java.io.*;
import java.util.stream.*;
reader.lines()
.filter(line -> line.contains("Java"))
.forEach(System.out::println);
reader.close();
}
}
In this example, .filter(line -> line.contains("Java")) filters lines from the file
data.txt that contain the substring "Java".
Key Points
By leveraging filtering and piping in Java streams, you can efficiently process and manipulate data, whether it's
from collections, files, or other sources. This functional approach enhances readability and maintainability of
code.
1.3 Reflection
Java Reflection is a process of examining or modifying the run time behavior of a class at run time.
The java.lang.Class class provides many methods that can be used to get metadata, examine and change the run
time behavior of a class.
The java.lang and java.lang.reflect packages provide classes for java reflection.
Where it is used
java.lang.Class class
There are 3 ways to get the instance of Class class. They are as follows:
FileName: Test.java
1. class Simple{}
2.
3. public class Test{
4. public static void main(String args[]) throws Exception {
5. Class c=Class.forName("Simple");
6. System.out.println(c.getName());
7. }
8. }
Output:
Simple
2) getClass() method of Object class
It returns the instance of Class class. It should be used if you know the type. Moreover, it can be used with
primitives.
FileName: Test.java
1. class Simple{}
2.
3. class Test{
4. void printName(Object obj){
5. Class c=obj.getClass();
6. System.out.println(c.getName());
7. }
8. public static void main(String args[]){
9. Simple s=new Simple();
10.
11. Test t=new Test();
12. t.printName(s);
13. }
14.}
15.
Output:
Simple
3) The .class syntax
If a type is available, but there is no instance, then it is possible to obtain a Class by appending ".class" to the
name of the type. It can be used for primitive data types also.
FileName: Test.java
1. class Test{
2. public static void main(String args[]){
3. Class c = boolean.class;
4. System.out.println(c.getName());
5.
6. Class c2 = Test.class;
7. System.out.println(c2.getName());
8. }
9. }
Output:
boolean
Test
Determining the class object
The following methods of Class class are used to determine the class object:
2) public boolean isArray(): determines if this Class object represents an array class.
FileName: Test.java
1. class Simple{}
2. interface My{}
3.
4. class Test{
5. public static void main(String args[]){
6. try{
7. Class c=Class.forName("Simple");
8. System.out.println(c.isInterface());
9.
10. Class c2=Class.forName("My");
11. System.out.println(c2.isInterface());
12.
13. }catch(Exception e){System.out.println(e);}
14.
15. }
16.}
Output:
false
true
Pros and Cons of Reflection
Java reflection should always be used with caution. While the reflection provides a lot of advantages, it has
some disadvantages too. Let's discuss the advantages first.
Pros: Inspection of interfaces, classes, methods, and fields during runtime is possible using reflection, even
without using their names during the compile time. It is also possible to call methods, instantiate a clear or to set
the value of fields using reflection. It helps in the creation of Visual Development Environments and class
browsers which provides aid to the developers to write the correct code.
Cons: Using reflection, one can break the principles of encapsulation. It is possible to access the private
methods and fields of a class using reflection. Thus, reflection may leak important data to the outside world,
which is dangerous. For example, if one access the private members of a class and sets null value to it, then the
other user of the same class can get the NullReferenceException, and this behaviour is not expected.
Another demerit is the overhead in performance. Since the types in reflection are resolved dynamically, JVM
(Java Virtual Machine) optimization cannot take place. Therefore, the operations performed by reflections are
usually slow.
1.4 Threading
In Java, threads are a fundamental part of concurrent programming, allowing programs to execute multiple tasks
concurrently.
Typically, we can define threads as a subprocess with lightweight with the smallest unit of processes and also
has separate paths of execution. The main advantage of multiple threads is efficiency (allowing multiple things
at the same time. For example, in MS Word. one thread automatically formats the document while another
thread is taking user input. Another advantage is quick response, if we use multiple threads in a process and if a
thread gets stuck due to lack of resources or an exception, the other threads can continue to execution, allowing
the process (which represents an application) to continue to be responsive.
As we can observe in, the above diagram a thread runs inside the process and there will be context-based
switching between threads there can be multiple processes running in OS, and each process again can have
multiple threads running simultaneously. The Multithreading concept is popularly applied in games,
animation…etc.
1. Process-Based Multitasking
2. Thread-Based Multitasking
In this type of Multitasking, processes are heavyweight and each process was allocated by a separate memory
area. And as the process is heavyweight the cost of communication between processes is high and it takes a long
time for switching between processes as it involves actions such as loading, saving in registers, updating maps,
lists, etc.
2. Thread-Based Multitasking
As we discussed above Threads are provided with lightweight nature and share the same address space, and the
cost of communication between threads is also low.
1. New State
2. Active State
3. Waiting/Blocked State
4. Timed Waiting State
5. Terminated State
We can see the working of different states in a Thread in the above Diagram, let us know in detail each and
every state:
1. New State
By default, a Thread will be in a new state, in this state, code has not yet been run and the execution process is
not yet initiated.
2. Active State
A Thread that is a new state by default gets transferred to Active state when it invokes the start() method, his
Active state contains two sub-states namely:
Runnable State: In This State, The Thread is ready to run at any given time and it’s the job of the Thread
Scheduler to provide the thread time for the runnable state preserved threads. A program that has obtained
Multithreading shares slices of time intervals which are shared between threads hence, these threads run for
some short span of time and wait in the runnable state to get their schedules slice of a time interval.
Running State: When The Thread Receives CPU allocated by Thread Scheduler, it transfers from the
“Runnable” state to the “Running” state. and after the expiry of its given time slice session, it again moves back
to the “Runnable” state and waits for its next time slice.
3. Waiting/Blocked State
If a Thread is inactive but on a temporary time, then either it is a waiting or blocked state, for example, if there
are two threads, T1 and T2 where T1 needs to communicate to the camera and the other thread T2 already using
a camera to scan then T1 waits until T2 Thread completes its work, at this state T1 is parked in waiting for the
state, and in another scenario, the user called two Threads T2 and T3 with the same functionality and both had
same time slice given by Thread Scheduler then both Threads T1, T2 is in a blocked state. When there are
multiple threads parked in a Blocked/Waiting state Thread Scheduler clears Queue by rejecting unwanted
Threads and allocating CPU on a priority basis.
Sometimes the longer duration of waiting for threads causes starvation, if we take an example like there are two
threads T1, T2 waiting for CPU and T1 is undergoing a Critical Coding operation and if it does not exist the
CPU until its operation gets executed then T2 will be exposed to longer waiting with undetermined certainty, In
order to avoid this starvation situation, we had Timed Waiting for the state to avoid that kind of scenario as in
Timed Waiting, each thread has a time period for which sleep() method is invoked and after the time expires the
Threads starts executing its task.
5. Terminated State
A thread will be in Terminated State, due to the below reasons:
Sometimes Threads may be terminated due to unusual events like segmentation faults, exceptions…etc. and
such kind of Termination can be called Abnormal Termination.
As we are familiar, we create Main Method in each and every Java Program, which acts as an entry point for the
code to get executed by JVM, Similarly in this Multithreading Concept, Each Program has one Main Thread
which was provided by default by JVM, hence whenever a program is being created in java, JVM provides the
Main Thread for its Execution.
We can run Threads in Java by using Thread Class, which provides constructors and methods for creating and
performing operations on a Thread, which extends a Thread class that can implement Runnable Interface. We
use the following constructors for creating the Thread:
Thread
Thread(Runnable r)
Thread(String name)
Thread(Runnable r, String name)
Output
Thread Started Running...
Sample code to create Thread by using Runnable Interface:
Java
import java.io.*;
import java.util.*;
Output
Thread is Running Successfully
Sample Code to create Thread in Java using Thread(String
name):
Java
import java.io.*;
import java.util.*;
// getting data of
// Thread through String
String s = t.getName();
System.out.println(s);
}
}
Output
Hello Geeks!
Sample Java Code which creates Thread Object by using
Thread(Runnable r, String name):
Java
import java.io.*;
import java.util.*;
Output
My Thread
Thread is created and running successfully...
Output
post-spanning, state of t1 is NEW
post invoking of start() method, state of t1 is RUNNABLE
state of t2 Thread, post-calling of start() method is RUNNABLE
The state for t1 after it invoked join method() on thread t2 TIMED_WAITING
State of Thread t2 after invoking to method sleep() is TIMED_WAITING
State of Thread t2 after join() is TERMINATED
state of Thread t1 after completing the execution is RUNNABLE
Java Native Interface (JNI) is a framework that allows Java code running in the Java Virtual Machine (JVM) to
interoperate with applications and libraries written in other languages, particularly in C or C++. JNI enables
Java applications to call native methods and access native libraries, and conversely, allows native code to invoke
Java methods.
1. Purpose:
o JNI facilitates integration of Java applications with native code when Java's built-in
capabilities (like platform-specific functionality or performance-critical operations) are
insufficient.
2. Components:
o Java Native Method Interface: This is the set of methods and rules for making native method
calls from Java code.
o JNI API: A set of programming interfaces for Java and native code interaction, including
functions for loading native libraries, calling native methods, and handling data conversion
between Java and native types.
3. Native Method Declaration:
o In Java, native methods are declared with the native keyword and are implemented in
native code (C/C++).
java
Copy code
public class NativeExample {
// Native method declaration
public native void nativeMethod();
c
Copy code
#include <jni.h>
#include "NativeExample.h" // Generated header from javah tool
5. JNI Environment:
o The JNIEnv (Java Native Interface Environment) provides methods for accessing JVM
functionality, manipulating Java objects, and performing type conversions.
o Use the javah tool to generate C/C++ header files (*.h) for the native methods declared in
Java.
sh
Copy code
javah -jni NativeExample
o Write C/C++ code that implements the native methods as declared in the generated header
file.
o Compile the native code into a shared library compatible with your platform.
o Execute your Java application, which will call the native methods as needed.
JNI is a powerful tool for extending Java applications to interact with native libraries and access platform-
specific capabilities. It is commonly used in areas such as multimedia processing, hardware interactions, and
performance optimization where direct native access is necessary. However, due to its complexity and potential
pitfalls, it requires careful design and testing to ensure robustness and stability in Java applications.
java
Copy code
public class NativeExample {
// Native method declaration
public native void nativeMethod();
o Explanation:
native void nativeMethod(): This declares a native method that is implemented in
native code (C/C++).
static { System.loadLibrary("nativeLibraryName"); }: This static block loads the
native library (nativeLibraryName) that contains the implementation of
nativeMethod().
sh
Copy code
javah -jni NativeExample
o This generates a header file that declares the native method signature in C/C++.
c
Copy code
#include <jni.h>
#include "NativeExample.h" // Generated JNI header
o Explanation:
JNIEXPORT void JNICALL
Java_NativeExample_nativeMethod(JNIEnv *env, jobject
obj): This is the C function that implements the native method declared in
NativeExample.java.
printf("Native method called\n");: This prints a message
indicating that the native method has been called.
sh
Copy code
gcc -shared -o libnativeLibraryName.so -I$JAVA_HOME/include -
I$JAVA_HOME/include/linux NativeExample.c
sh
Copy code
java NativeExample
o Output:
sql
Copy code
Native method called
Explanation of Steps:
Performance: JNI allows Java applications to leverage the performance of native code for tasks
requiring low-level access or high computational intensity.
Integration: JNI facilitates seamless integration with existing native libraries and system-level APIs.
Complexity: JNI introduces complexity and requires careful management of memory and resource
handling to avoid leaks and crashes.
JNI provides Java developers with a powerful mechanism to extend their applications to interact with native
code, enabling them to integrate with platform-specific functionality and existing libraries written in languages
like C or C++. However, JNI should be used judiciously due to its complexity and potential pitfalls related to
memory management and platform dependencies.
Java Swing tutorial is a part of Java Foundation Classes (JFC) that is used to create window-based
applications. It is built on the top of AWT (Abstract Windowing Toolkit) API and entirely written in java.
The javax.swing package provides classes for java swing API such as JButton, JTextField, JTextArea,
JRadioButton, JCheckbox, JMenu, JColorChooser etc.
There are many differences between java awt and swing that are given
below
No Java AWT Java Swing
.
3) AWT doesn't support pluggable look and feel. Swing supports pluggable look and feel.
4) AWT provides less components than Swing. Swing provides more powerful
components such as tables, lists, scrollpanes,
colorchooser, tabbedpane etc.
5) AWT doesn't follows MVC(Model View Controller) where model Swing follows MVC.
represents data, view represents presentation and controller acts as an
interface between model and view.
What is JFC
The Java Foundation Classes (JFC) are a set of GUI components which simplify the development of desktop
applications.
The methods of Component class are widely used in java swing that are
given below.
Method Description
Let's see a simple swing example where we are creating one button and adding it on the JFrame object inside the main()
method.
File: FirstSwingExample.java
import javax.swing.*;
public class FirstSwingExample {
public static void main(String[] args) {
JFrame f=new JFrame();//creating instance of JFrame
File: Simple.java
import javax.swing.*;
public class Simple {
JFrame f;
Simple(){
f=new JFrame();//creating instance of JFrame
The setBounds(int xaxis, int yaxis, int width, int height)is used in the above example that sets the position of the
button.
We can also inherit the JFrame class, so there is no need to create the instance of JFrame class explicitly.
File: Simple2.java
import javax.swing.*;
public class Simple2 extends JFrame{//inheriting JFrame
JFrame f;
Simple2(){
JButton b=new JButton("click");//create button
b.setBounds(130,100,100, 40);
The Java Collection Framework provides a unified architecture for representing and manipulating collections of
objects. It includes interfaces, implementations, and algorithms that allow Java developers to work efficiently
with collections of objects. Here’s an introduction to the Java Collection Framework:
1. Interfaces:
o Collection: Represents a group of objects, including lists, sets, and queues. Examples include List, Set,
and Queue.
o List: Ordered collection (sequence) that allows duplicate elements. Examples include ArrayList,
LinkedList, Vector.
o Set: Collection that does not allow duplicate elements. Examples include HashSet, LinkedHashSet,
TreeSet.
o Queue: Collection used for holding elements prior to processing. Examples include PriorityQueue,
LinkedList.
2. Implementations:
o Concrete implementations of collection interfaces that provide different behavior and performance
characteristics.
o Examples: ArrayList, LinkedList, HashSet, TreeMap, etc.
3. Algorithms:
o Utility methods provided by Collections class for performing operations on collections such as sorting,
searching, shuffling, etc.
o Examples: sort(), binarySearch(), shuffle(), reverse(), etc.
o Uniformity: Provides a unified architecture for working with collections, making it easier to learn and
use different data structures.
o Efficiency: Offers optimized implementations of common data structures, reducing the need for custom
implementations.
o Flexibility: Supports a wide range of data structures and algorithms, allowing developers to choose the
most appropriate collection for their needs.
Here’s a simple example demonstrating the use of ArrayList and HashSet from the Java Collection Framework:
java
Copy code
import java.util.*;
System.out.println("Elements in ArrayList:");
for (String lang : list) {
System.out.println(lang);
}
System.out.println("\nElements in HashSet:");
for (String fruit : set) {
System.out.println(fruit);
}
}
}
Explanation:
Common Operations
Thread Safety: Most collection implementations are not synchronized by default. Use
Collections.synchronizedXXX() methods or concurrent collections for thread-safe
operations.
Generics: The use of generics ensures type safety and avoids casting issues (List<String>,
Set<Integer>, etc.).
Enum Example:
The 4 suits in a deck of playing cards may be 4 enumerators named Club, Diamond, Heart, and Spade, belonging to an
enumerated type named Suit. Other examples include natural enumerated types (like the planets, days of the week, colors,
directions, etc.).
One thing to keep in mind is that, unlike classes, enumerations neither inherit other classes nor can get extended(i.e become
superclass). We can also add variables, methods, and constructors to it. The main objective of an enum is to define our own
data types(Enumerated Data Types).
enum Color {
RED,
GREEN,
BLUE;
}
// Driver method
public static void main(String[] args)
{
Color c1 = Color.RED;
System.out.println(c1);
}
}
Output
RED
The first line inside the enum should be a list of constants and then other things like methods, variables, and constructors.
According to Java naming conventions, it is recommended that we name constant with all capital letters
Java
import java.util.Scanner;
// An Enum class
enum Day {
SUNDAY,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY;
}
// Constructor
public Test(Day day) { this.day = day; }
Java
enum Color {
RED,
GREEN,
BLUE;
// Driver method
public static void main(String[] args)
{
Color c1 = Color.RED;
System.out.println(c1);
}
}
Output
RED
2. Loop through Enum
We can iterate over the Enum using values( ) and loop. values() function returns an array of Enum values as constants using
which we can iterate over the values.
Java
// Enum Declared
enum Color {
RED,
GREEN,
BLUE;
}
// Driver Class
class GFG {
// Main Function
public static void main(String[] args)
{
// Iterating over all the values in
// enum using for loop
for (Color var_1 : Color.values()) {
System.out.println(var_1);
}
}
}
Output
RED
GREEN
BLUE
// Driver Class
class GFG {
// Enum Declared
enum Color {
RED,
GREEN,
BLUE,
Yellow;
}
// Main Function
public static void main(String[] args)
{
Color var_1=Color.Yellow;
All enums implicitly extend java.lang.Enum class. As a class can only extend one parent in Java, an enum cannot extend
anything else.
toString() method is overridden in java.lang.Enum class, which returns enum constant name.
Enum can contain a constructor and it is executed separately for each enum constant at the time of the enum class loading.
We can’t create enum objects explicitly and hence we can’t invoke the enum constructor directly.
Enum can contain both concrete methods and abstract methods. If an enum class has an abstract method, then each instance
of the enum class must implement it.
Java
Stack
In Java, Stack is a class that represents a last-in, first-out (LIFO) stack of objects. It extends the Vector class with additional
methods that allow a vector to be treated as a stack. Here's an overview of how Stack works and how you can use it in Java:
1. LIFO Principle:
o Stack follows the Last In, First Out principle, meaning the last element added to the stack is the first one
to be removed.
2. Extends Vector:
o Internally, Stack is implemented as a subclass of Vector, which means it inherits all the methods and
properties of Vector.
3. Methods:
o push(E item): Pushes an item onto the top of the stack.
o pop(): Removes the object at the top of the stack and returns it.
o peek(): Looks at the object at the top of the stack without removing it.
o empty(): Checks if the stack is empty.
o search(Object o): Searches for an object in the stack and returns its position relative to the top of the
stack (1-based index).
java
Copy code
import java.util.Stack;
o push(E item): Adds an element ("Java", "Python", "C++") to the top of the stack.
o pop(): Removes and returns the top element of the stack ("C++").
o peek(): Returns the top element without removing it ("Python").
o empty(): Checks if the stack is empty (false in this case).
o search(Object o): Finds the position of an object ("Java") in the stack (2 in this case).
o Simple Interface: Provides straightforward methods (push(), pop(), peek()) for stack operations.
o Versatility: Can be used in various scenarios such as parsing expressions, backtracking algorithms, and
undo mechanisms.
o Extensibility: Being a subclass of Vector, it inherits all vector operations, making it suitable for
scenarios requiring dynamic resizing.
Considerations
In summary, Stack in Java provides a convenient way to manage elements in a last-in, first-out manner. It's
useful for various programming tasks where LIFO operations are required, offering simplicity and effectiveness
in managing collections of objects.
Queue
In Java, Queue is an interface that represents a collection designed for holding elements prior to processing. It
follows the First-In, First-Out (FIFO) principle, meaning that elements are inserted at the end of the queue and
removed from the beginning. The Queue interface is part of the Java Collections Framework and provides
several implementations for different use cases. Here’s an overview of Queue and its key implementations in
Java:
Queue Interface
1. Methods:
o boolean add(E e): Inserts the specified element into the queue if possible; throws an exception if the
element cannot be added.
o boolean offer(E e): Inserts the specified element into the queue if possible; returns true if successful,
false if the element cannot be added.
o E remove(): Retrieves and removes the head of the queue; throws an exception if the queue is empty.
o E poll(): Retrieves and removes the head of the queue, or returns null if the queue is empty.
o E element(): Retrieves, but does not remove, the head of the queue; throws an exception if the queue is
empty.
o E peek(): Retrieves, but does not remove, the head of the queue, or returns null if the queue is empty.
2. Implementations:
o LinkedList: Implements Queue interface and provides FIFO queue operations. It also allows null
elements and is not synchronized.
o PriorityQueue: Provides a priority queue based on the natural ordering or a Comparator. Elements are
ordered based on their natural ordering or the custom Comparator.
o add(E e) and offer(E e): Both methods add an element to the queue. add() throws an exception if the
element cannot be added (e.g., if the capacity is full in a bounded queue), while offer() returns false if the
element cannot be added.
o remove() and poll(): Both methods remove and return the head of the queue. remove() throws an
exception if the queue is empty, while poll() returns null if the queue is empty.
o element() and peek(): Both methods retrieve the head of the queue without removing it. element()
throws an exception if the queue is empty, while peek() returns null if the queue is empty.
o Efficiency: Provides efficient FIFO operations (add, remove, peek, etc.) for managing elements.
o Flexibility: Offers different implementations (LinkedList, PriorityQueue) for various requirements
(simple FIFO or priority-based processing).
o Thread Safety: Certain implementations (LinkedBlockingQueue from java.util.concurrent) provide
thread-safe operations for concurrent environments.
Considerations
In summary, Queue in Java provides a versatile way to manage collections of elements with FIFO or priority-
based processing requirements. It's widely used in applications requiring orderly processing of elements, such as
task scheduling, breadth-first search algorithms, and message queues.
In Java, a Set is a collection that does not allow duplicate elements. It models the mathematical set abstraction
and provides methods for adding, removing, and checking the presence of elements. Java provides several
implementations of the Set interface in the Java Collections Framework, each with its own characteristics and
use cases. Here's an overview of sets in Java and some of the common implementations:
Set Interface
The Set interface extends the Collection interface and adds the following features:
1. No Duplicate Elements:
o A Set does not allow duplicate elements. If you attempt to add an element that already exists in the set,
the add operation will return false.
2. Unordered Collection:
o Unlike a List, which maintains the order of elements based on insertion sequence, a Set does not
guarantee any specific order of elements.
3. Common Methods:
o add(E e): Adds the specified element to the set if it is not already present.
o remove(Object o): Removes the specified element from the set if it is present.
o contains(Object o): Returns true if the set contains the specified element.
o size(): Returns the number of elements in the set.
o isEmpty(): Returns true if the set contains no elements.
Java provides several implementations of the Set interface. Here are some of the commonly used ones:
1. HashSet:
o Implements Set interface using a hash table. It does not guarantee the order of elements and allows null
elements.
Example:
java
Copy code
Set<String> hashSet = new HashSet<>();
hashSet.add("Java");
hashSet.add("Python");
hashSet.add("C++");
2. TreeSet:
o Implements NavigableSet interface and stores elements in a sorted order (natural ordering or custom
Comparator).
Example:
java
Copy code
Set<String> treeSet = new TreeSet<>();
treeSet.add("Apple");
treeSet.add("Banana");
treeSet.add("Orange");
3. LinkedHashSet:
o Extends HashSet and maintains insertion order of elements. It provides predictable iteration order.
o Example:
java
Copy code
Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("Red");
linkedHashSet.add("Green");
linkedHashSet.add("Blue");
Example Usage of Set Interface
java
Copy code
import java.util.HashSet;
import java.util.Set;
add(E e): Adds elements to the set. Returns true if the element was added, false if it already existed in the set.
remove(Object o): Removes the specified element from the set if it exists.
contains(Object o): Checks if the set contains the specified element.
Iteration: Uses enhanced for loop to iterate through elements in the set.
No Duplicates: Ensures each element in the set is unique, which is useful for maintaining a collection of distinct
objects.
Efficient Lookups: Provides efficient operations for adding, removing, and checking the presence of elements.
Flexible Implementations: Offers different implementations (HashSet, TreeSet, LinkedHashSet) with varying
characteristics (ordering, uniqueness, etc.) to suit different use cases.
Considerations
Ordering: Consider whether you need elements to be ordered (use TreeSet or LinkedHashSet) or not (use
HashSet).
Null Elements: Some implementations (HashSet, LinkedHashSet) allow null elements, while others (TreeSet)
do not.
In summary, sets in Java are useful for managing collections of unique elements and provide efficient operations
for adding, removing, and checking the existence of elements. Understanding the characteristics and differences
between implementations (HashSet, TreeSet, LinkedHashSet) helps in choosing the appropriate set for
your specific application requirements.
Maps
In Java, Map is an interface in the Java Collections Framework that maps keys to values. It represents a
collection of key-value pairs where each key is unique and is used to retrieve the corresponding value. Maps are
commonly used to store and manipulate data in scenarios where quick access to values based on keys is
required. Here’s an overview of Map interface and its common implementations in Java:
Map Interface
1. Basic Operations:
o put(K key, V value): Associates the specified value with the specified key in the map. If the map
previously contained a mapping for the key, the old value is replaced.
o get(Object key): Returns the value to which the specified key is mapped, or null if the map contains no
mapping for the key.
o remove(Object key): Removes the mapping for the specified key from the map if present.
o containsKey(Object key): Returns true if the map contains a mapping for the specified key.
o containsValue(Object value): Returns true if the map maps one or more keys to the specified value.
o size(): Returns the number of key-value mappings in the map.
o isEmpty(): Returns true if the map contains no key-value mappings.
o Use entrySet() to get a Set view of the mappings contained in the map. Each element in this set is a
Map.Entry<K, V>, which represents a key-value pair.
Java provides several implementations of the Map interface, each with its own characteristics:
1. HashMap:
o Implements Map interface using a hash table. It provides constant-time performance for most operations
(average-case complexity).
o Example:
java
Copy code
import java.util.HashMap;
import java.util.Map;
2. TreeMap:
o Implements NavigableMap interface and stores key-value pairs in sorted order (natural ordering or
custom Comparator).
o Example:
java
Copy code
import java.util.Map;
import java.util.TreeMap;
3. LinkedHashMap:
o Extends HashMap and maintains insertion order of keys. Provides predictable iteration order.
o Example:
java
Copy code
import java.util.LinkedHashMap;
import java.util.Map;
o Key-Value Pairing: Provides a convenient way to associate keys with values, allowing efficient
retrieval and manipulation based on keys.
o Efficient Operations: Depending on the implementation (HashMap, TreeMap, LinkedHashMap), maps
offer efficient operations for adding, removing, and looking up entries.
o Flexibility: Offers different implementations (HashMap for fast access, TreeMap for sorted storage,
LinkedHashMap for predictable iteration order) to suit different use cases.
Considerations
o Null Keys and Values: Some map implementations (HashMap, LinkedHashMap, TreeMap) may
allow null keys and values depending on the specific implementation.
o Concurrency: If thread safety is required, consider using concurrent implementations
(ConcurrentHashMap from java.util.concurrent).
In summary, Map in Java is a powerful interface for managing key-value pairs and provides several
implementations to cater to different requirements such as fast access, sorted storage, or predictable iteration
order. Understanding the characteristics and differences between these implementations helps in choosing the
appropriate Map for your specific application needs.
Utility Classes
In Java, utility classes are classes that contain only static methods and constants, often providing utility
functionalities that are commonly used across different parts of an application. These utility classes are typically
used to group related utility methods together for ease of maintenance and reuse. Here's an overview of utility
classes in Java and their common characteristics:
o Static Methods: Utility classes typically consist of static methods that can be invoked without creating
an instance of the class. This makes them convenient for providing stateless operations.
o Private Constructor: Utility classes often have a private constructor to prevent instantiation. This
ensures that the class cannot be instantiated accidentally or subclassed.
o Final Class: Utility classes are commonly declared as final to prevent subclassing, which aligns with
their static nature and avoids unintended inheritance.
o Commonly Used for Helper Methods: They are used to group related helper methods that perform
generic operations such as string manipulation, date formatting, mathematical calculations, file
operations, etc.
Example of a Utility Class
Here’s an example of a utility class that provides methods for basic mathematical operations:
java
Copy code
public final class MathUtils {
private MathUtils() {
// Private constructor to prevent instantiation
}
o Code Reusability: Utility classes promote code reuse by encapsulating common operations in a single
place, reducing redundancy and promoting consistent behavior across an application.
o Simplifies Code Organization: They help in organizing utility methods logically, making it easier for
developers to find and use them when needed.
o Promotes Static Typing: Static methods in utility classes promote compile-time type checking,
enhancing code reliability and maintainability.
Considerations
o Thread Safety: Since utility classes typically contain stateless methods, they are generally thread-
safe when used correctly.
o Avoid Overuse: Avoid overusing utility classes for unrelated methods. Ensure that methods
grouped in a utility class share a common purpose or theme.
In summary, utility classes in Java are valuable for encapsulating reusable static methods and constants. They
promote code organization, improve maintainability, and enhance code reuse across applications. When
designing utility classes, it's important to follow best practices such as making the class final with a private
constructor to ensure proper usage and prevent unintended instantiation.