0% found this document useful (0 votes)
11 views194 pages

Java

The document compares Java and C++ programming languages, highlighting their similarities and differences in execution, features, and applications. Java is platform-independent and primarily used for software development, while C++ is known for its efficiency and speed, making it suitable for game development and system-level programming. Additionally, it explains the roles of JDK, JRE, and JVM in Java application development and execution.

Uploaded by

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

Java

The document compares Java and C++ programming languages, highlighting their similarities and differences in execution, features, and applications. Java is platform-independent and primarily used for software development, while C++ is known for its efficiency and speed, making it suitable for game development and system-level programming. Additionally, it explains the roles of JDK, JRE, and JVM in Java application development and execution.

Uploaded by

shaikhismaeel.cs
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 194

Similarities and Difference between Java

and C++
Nowadays Java and C++ programming languages are
vastly used in competitive coding. Due to some
awesome features, these two programming languages
are widely used in industries as well as comepetitive
programming. C++ is a widely popular language
among coders for its efficiency, high speed, and
dynamic memory utilization. Java is widely used in the
IT industry's , It is incomparable to any other
programming language in terms of software
development. Let us go through the various points to
compare these popular coding languages:
Similarities between Java and C++
1. Execution: At compile-time, Java source code
or .java file is converted into bytecode or .class file. At
runtime, JVM (Java Virtual Machine) will load
the .class file and will convert it to machine code with
the help of an interpreter. After compilation of method
calls (using the Just-In-Time (JIT) compiler), JVM will
execute the optimized code. So Java is both compiled
as well as an interpreted language. On the other hand,
C++ executes the code by using only a compiler. The
C++ compiler compiles and converts the source code
into the machine code. That's why C++ is faster than
Java but not platform-independent.
Below is the illustration of how Java and C++ codes are
executed:
The execution of a Java code is as follows:
Execution of a C++ Code
2. Features: C++ and Java both have several Object
Oriented programming features which provide many
useful programming functionalities. Some features are
supported by one and some by the other. Even though
both languages use the concept of OOPs, neither can
be termed 100% object-oriented languages. Java uses
primitive data types and thus cannot be termed as
100% Object-Oriented Language. C++ uses some data
types similar to primitive ones and can implement
methods without using any data type. And thus, it is
also deprived of the 100% Object-Oriented title.
Below is the table which shows the features supported
and not supported by both the programming
languages:

C+ Jav
Features + a

Abstraction Yes Yes

Encapsulation Yes Yes

Single Inheritance Yes Yes

Multiple Inheritance Yes No

Polymorphism Yes Yes

Static Binding Yes Yes

Dynamic Binding Yes Yes

Operator Overloading Yes No

Header Files Yes No

Pointers Yes No
C+ Jav
Features + a

Global Variables Yes No

Template Class Yes No

Interference and
No Yes
Packages

API No Yes

Syntax Difference:
Syntax of c++:
1
#include <iostream>
2
using namespace std;
3

4
int main() {
5
6
cout << "GFG!";
7
return 0;
8
}
syntax of java
1
/*package whatever //do not write package name here
*/
2

3
import java.io.*;
4

5
class GFG {
6
public static void main (String[] args) {
7
System.out.println("GFG!");
8
}
9
}
Applications: Both C++ and Java have vast areas of
application. Below are the applications of both
languages:
 Applications of C++ Programming language:

1. Suitable for Developing large software (like


passenger reservation systems).
2. MySQL is written in C++.
3. For fast execution, C++ is majorly used in
Game Development.
4. Google Chromium browser, file system, and
cluster data processing are all written in C++.
5. Adobe Premiere, Photoshop, and Illustrator;
these popular applications are scripted in C+
+.
6. Advanced Computations and Graphics- real-
time physical simulations, high-performance
image processing.
7. C++ is also used in many advanced types of
medical equipment like MRI machines, etc.
 Applications of Java Programming language:

1. Desktop GUI Applications development.


2. Android and Mobile application development.
3. Applications of Java are in embedded
technologies like SIM cards, disk players, TV,
etc.
4. Java EE (Enterprise Edition) provides an API
and runtime environment for running large
enterprise software.
5. Network Applications and Web services like
Internet connection, Web App Development.
Environment: C++ is a Platform dependent while Java
is a platform-independent programming language. We
have to write and run C++ code on the same platform.
Java has the WORA (Write Once and Run
Everywhere) feature by which we can write our code
in one platform once and we can run the code
anywhere.
Differences between Java and C++ are as follows:

Sl.
No Parameter
. s Java C++

C++ was
developed by
Java was
Bjarne
developed by
Stroustrup at
1. Founder James Gosling
Bell Labs in 1979
at Sun
as an extension
Microsystems.
of the C
language.
Sl.
No Parameter
. s Java C++

First On May 23,


2. In October 1985
Release 1995

Java SE 18 was C++20 released


Stable
3. released on 22 on 15th
Release
March 2022 December 2020

Official
4. oracle.com/java isocpp.org
Website

C++ was
Java was Influenced by
Influenced by Influenced by
Influenced
5. Ada 83, Pascal, Ada, ALGOL 68,
By:
C++, C#, etc. C, ML, Simula,
languages. Smalltalk, etc.
languages.

6. Influenced Java was C++ was


to: influenced to influenced to
develop develop C99,
BeanShell, C#, Java, JS++, Lua,
Clojure, Groovy, Perl, PHP,
Hack, J#, Kotlin, Python, Rust,
PHP, Python, Seed7, etc.
Scala, etc.
Sl.
No Parameter
. s Java C++

languages. languages.

Platform- Platform
independent, dependent,
Platform
Java bytecode should be
7. Dependenc
works on any compiled for
y
operating different
system. platforms.

C++ is platform-
It can run on
dependent.
8. Portability any OS hence it
Hence it is not
is portable.
portable.

Java is both
C++ is a
Compiled and
9. Compilation Compiled
Interpreted
Language.
Language.

Memory
Memory Memory
1 Management is
Managemen Management in
0. System
t C++ is Manual.
Controlled.

1 Virtual It doesn't have It has Virtual


1. Keyword a Virtual
Sl.
No Parameter
. s Java C++

Keyword. keywords.

It supports only
single
inheritance. It supports both
1 Multiple Multiple single and
2. Inheritance inheritances are multiple
achieved Inheritance.
partially using
interfaces.

It supports only
method It supports both
1 overloading and method and
Overloading
3. doesn't allow operator
operator overloading.
overloading.

It has limited It strongly


1
Pointers support for supports
4.
pointers. pointers.

1 Libraries It doesn't It supports direct


5. support direct system library
native library calls, making it
calls but only suitable for
Sl.
No Parameter
. s Java C++

Java Native system-level


Interfaces. programming.

Libraries have a C++ libraries


wide range of have
1
Libraries classes for comparatively
6.
various high- low-level
level services. functionalities.

It supports It doesn’t
Documentat documentation support
1
ion comments (e.g., documentation
7.
Comment /**.. */) for comments for
source code. source code.

C++ doesn’t
have built-in
Java provides support for
1 Thread built-in support threads,
8. Support for depends on
multithreading. third-party
threading
libraries.

1 Type Java is only an C++ is both a


9. object-oriented procedural and
Sl.
No Parameter
. s Java C++

an object-
programming oriented
language. programming
language.

Java uses the C++


(System uses cin for
Input-
2 class): System. input
Output
0. in for input and cout for an
mechanism
and System.ou output
t for output. operation.

Java doesn't
2 goto C++ supports
support goto
1. Keyword goto keyword.
Keyword

Java doesn't
C++ supports
2 Structures support
Structures and
2. and Unions Structures and
Unions.
Unions.

Java supports C++ supports


2 Parameter only the Pass by both Pass by
3. Passing Value Value and pass
technique. by reference.
Sl.
No Parameter
. s Java C++

All classes in
Java are
subclasses of A fresh
2 Inheritance the Object inheritance tree
4. Tree class, hence is always
Java only ever created in C++.
follows a single
inheritance tree.

It supports both
2 Global It supports no global scope and
5. Scope global scope. namespace
scope.

Automatic It supports
Object object manual object
2
Managemen management management
6.
t with garbage using new and
collection. delete.

Call by C++ both


Java supports
2 Value and supports call by
only call by
7. Call by value and call by
value.
reference reference.

2 Hardware Java is not so C++ is nearer to


Sl.
No Parameter
. s Java C++

interactive with
8. hardware.
hardware.

Game engines,
Machine
Internet and
learning,
Android games,
Operating
Mobile
systems, Google
applications,
Search Engine,
Healthcare and
Web browsers,
2 Language research
Virtual Reality
9. Used for computation,
(VR), Game
Cloud
development,
applications,
Medical
Internet of
technology,
Things (IoT)
Telecommunicati
devices, etc.
ons, Databases,
etc.

Mozilla Firefox,
Wikipedia,
Amazon, Apple
LinkedIn,
3 Application OS, Spotify,
Android OS,
0. built Adobe
Uber, and
Photoshop, and
Minecraft,
YouTube.
Differences Between JDK, JRE and JVM
Understanding the difference between JDK, JRE, and
JVM plays a very important role in understanding
how Java works and how each component contributes to the
development and execution of Java applications. The main
difference between JDK, JRE, and JVM is:
 JDK: Java Development Kit is a software development
environment used for developing Java applications and
applets.
 JRE: JRE stands for Java Runtime Environment, and it
provides an environment to run only the Java program
onto the system.
 JVM: JVM stands for Java Virtual Machine and is
responsible for executing the Java program.
JDK vs JRE vs JVM

Aspect JDK JRE JVM

Used to Used to run


Executes Java
develop Java Java
bytecode
Purpose applications applications

JVM is OS-
Platform- Platform- specific, but
Platform dependent dependent bytecode is
Dependenc (OS specific) (OS specific) platform-
y independent

Includes JRE + JVM + ClassLoader,


Development Libraries JIT Compiler,
tools (javac, Garbage
Aspect JDK JRE JVM

debugger,
(e.g., rt.jar) Collector
etc.)

Running a Convert
Writing and
Java bytecode into
compiling Java
application on native
code
Use Case a system machine code

Note: The JVM is platform-independent in the sense that the


bytecode can run on any machine with a JVM, but the actual
JVM implementation is platform-dependent. Different operating
systems (e.g., Windows, Linux, macOS) require different JVM
implementations that interact with the specific OS and
hardware
We have discussed the core differences, now let's take a
closer look at each component. Let, us discuss them in
brief first and interrelate them with the image below
proposed.

JDK (Java Development Kit)


The JDK is a software development kit that provides the
environment to develop and execute the java application. It
includes two things:
 Development Tools (to provide an environment to develop
your java programs)
 JRE (to execute your java program)
Note:
 JDK is only for development (it is not needed for running
Java programs)
 JDK is platform-dependent (different version for windows,
Linux, macOS)

Working of JDK
The JDK enables the development and execution of Java
programs. Consider the following process:
 Java Source File (e.g., Example.java): You write the
Java program in a source file.
 Compilation: The source file is compiled by the Java
Compiler (part of JDK) into bytecode, which is stored in
a .class file (e.g., Example.class).
 Execution: The bytecode is executed by the JVM (Java
Virtual Machine), which interprets the bytecode and runs
the Java program.
Note: From above, media operation computing during the
compile time can be interpreted.

The following actions occur at runtime as listed below:


 Class Loader
 Byte Code Verifier
 Interpreter
o Execute the Byte Code
o Make appropriate calls to the underlying hardware

JRE ((Java Runtime Environment))


The JRE is an installation package that provides an environment
to only run (not develop) the Java program (or application)
onto your machine. JRE is only used by those who only want to
run Java programs that are end-users of your system.
Note:
 JRE is only for end-users (not for developers).
 JRE is platform-dependent (different versions for different
OS)
Working of JRE
When you run a Java program, the following steps occur:
 Class Loader: The JRE’s class loader loads the .class file
containing the bytecode into memory.
 Bytecode Verifier: JRE includes a bytecode verifier to
ensure security before execution
 Interpreter: JVM uses an interpreter + JIT compiler to
execute bytecode for optimal performance
 Execution: The program executes, making calls to the
underlying hardware and system resources as needed.

JVM (Java Virtual Machine)


The JVM is a very important part of both JDK and JRE because it
is contained or inbuilt in both. Whatever Java program you run
using JRE or JDK goes into JVM and JVM is responsible for
executing the java program line by line, hence it is also known
as an interpreter.
Note:
 JVM is platform -dependent (different JVMs for window,
linux, macOS).
 Bytecode (.class files) is platform-independent (same file
runs in any JVM).
 While JVM includes an interpreter, modern
implementations primarily use JIT compilation for faster
execution
Working of JVM
It is mainly responsible for three activities.
 Loading
 Linking
 Initialization
Topic 1: -
JAVA Collections
Java Collection Framework is unlikely any group of individual
objects which are represented as a single unit be it of any type
is known as the collection of objects. Earlier in Java, there was
no such thing defined which holds true for it so there arises a
need in the next versions of any such concept. So, in JDK 1.2, a
separate framework was defined known as “Collection
Framework” which holds all the collection classes and
interfaces in it.

The Collection interface (java.util.Collection) and Map


interface (java.util.Map) are the two main “root” interfaces of
Java collection classes.

What is a Framework?
A framework is a set of classes and interfaces which provide a
ready-made architecture. In order to implement a new feature
or a class, there is no need to define a framework itself.
However, an optimal object-oriented design often includes a
framework with a collection of classes such that all the classes
perform the same kind of task.
Need for a Separate Collection Framework
Before Collection Framework (or before JDK 1.2) was
introduced, the standard methods for grouping Java objects (or
collections) were Arrays or Vectors, or Hashtables. All of
these collections had no common interface. Therefore, though
the main aim of all the collections is the same, the
implementation of all these collections was defined
independently and had no correlation among them. And also, it
is very difficult for the users to remember all the
different methods, syntax, and constructors present in every
collection class.
Are you confused between Collection, Collection
Interface, Collections Class, Collection Framework?
 Collection: A group of individual objects that represent a
single entity is known as a collection. It is the common
word that you used in your daily life. But if we are
discussing Java programming language then it will become
Java Collection Framework.
 Collection Framework: To represent a group of objects
as a single entity in the Java programming language we
need classes and interfaces defined by the Collection
Framework. If are from C++ background that you can
compare Collection with Containers and Collection
Framework with STL(Standard Template Library).
 Collection Interface: Interfaces specify what a class
must do and not how. It is the blueprint of the class. It is
the root interface of the Collection Framework that defines
the most common methods that can be used for any
collection objects. Or you can say it represents the
individual object as a single entity.
 Collections Class: It is present in java.util package and
is a member of Collection Framework. This class provides
many utility methods for the collection object.
Now you must be wondering why to include such a concept or
what difficulties were faced before java version 1.2 that lead
birth to the concept of Collection. In order to figure it out, the
three main advantages are mentioned below:
1. Reduces the programming effort as the programmer does
not have to worry about designing the Collection class and
on the same hand java being object-oriented language
advances in achieving abstraction by not writing the
Collection class.
2. Increases program speed and quality as the programmer
now is not worried about thinking best implementation for
a specific data structure as of now for a programmer
scope widens up and at the same time is now only worried
about the best implementation that can drastically boost
the performance of program irrespective of data structure
constraints encountered earlier.
3. The API that implements these interfaces are now having
common sets of methods be it of any interfaces such as
Collection, Set, List, and Map.
Note: All these collections can be imported using:
import java.util.*;
However, single classes can also be imported by replacing *
with the class name as shown
import java.util.ArrayList;
import java.util.LinkedList;
The following figure illustrates the hierarchy of the collection
framework.

Now, discussing and visualizing Collection at the physical level


by discussing its hierarchy. The utility
package, java.util contains all the classes and interfaces that
are required by the collection framework. The collection
framework contains an interface named an iterable interface
which provides the iterator to iterate through all the collections.
This interface is extended by the main collection interface
which acts as a root for the collection framework. All the
collections extend this collection interface thereby extending
the properties of the iterator and the methods of this interface.
Iterable interface is the root interface for the entire collection
framework. The collection interface extends the iterable
interface. Therefore, inherently, all the interfaces and classes
implement this interface. The main functionality of this
interface is to provide an iterator for the collections.
The following 6 interfaces are described below first later on
been discussed with clean java programs as in implementation.
 Collection interface
 List interface
 Queue interface
 Deque interface (Double-ended queue)
 Set interface
 Map
Collection Interface contains all the basic methods which
every collection has like adding the data into the collection,
removing the data, clearing the data, etc. All these methods
are implemented in this interface because these methods are
implemented by all the classes irrespective of their style of
implementation.
List Interface
It is a child interface of the collection interface. This interface is
dedicated to the data of the list type in which we can store all
the ordered collections of the objects. This deals with the index
or position-specific functions like getting an element or setting
an element. It deals with the arrays and lists types of
operations like ArrayList, LinkedList, Vector, and Stack.
1. ArrayList provides us with dynamic arrays in Java. The size
of an ArrayList is increased automatically if the collection grows
or shrinks if the objects are removed from the collection.
 Add an Element in an ArrayList
 Remove an Element in an ArrayList
 Update an Element in an ArrayList
2. LinkedList is class is an implementation of a doubly-linked
list data structure.
 Access Elements of a LinkedList
 Add Elements to a LinkedList
 Check if the LinkedList contains the Element
 Change Elements of a LinkedList
 Remove All Elements from a LinkedList
 Iterate over LinkedList using Iterator
For more, go through Java LinkedList
3. Vector provides us with dynamic arrays in Java. This is a
legacy class. It is a thread-safe class. This is not recommended
being used in a single-threaded environment as it might cause
extra overheads. However, to overcome this in Vectors place
one can readily use ArrayList.
 Adding Elements in Vector
 Removing Elements From Vector
 Replacing Elements in the Vector
 Check if Vector Has Elements or not
 Iterating the Vector Elements in Reverse Order
 Copying Elements of One Java Vector to Another
 Maximum and Minimum Element From Vector
For more, go through Java Vector
4. Stack is a class is based on the basic principle of last-in-
first-out. This is a legacy class. This inherits from a Vector class.
It is also a thread-safe class. This is not recommended being
used in a single-threaded environment as it might cause extra
overheads. However, to overcome this in Vectors place one can
readily use ArrayDeque.
 Check Whether the Stack is Empty or Not
 Search Element in Stacks
 Access Top Element at the Top of Stack
5. AbstractList class in Java provides a skeletal
implementation of the List interface to minimize the effort
required to implement this interface backed by a Random
Access data store (such as an array). For sequential access data
(such as a linked list), AbstractSequentialList should be used in
preference to this class. It implements the Collection interface
and the AbstractCollection class.
6. AbstractSequentialList class provides a skeletal
implementation of the List interface to minimize the effort
required to implement this interface backed by a “sequential
access” data store (such as a linked list). For random access
data (such as an array), AbstractList should be used in
preference to this class. It is used to implement an
unmodifiable list, for which one needs to only extend this
AbstractList Class and implement only the get() and the size()
methods.
Set Interface
It is an unordered collection of objects in which duplicate values
cannot be stored. This set interface is implemented by various
classes like HashSet, TreeSet, LinkedHashSet, etc.

HashSet is an inherent implementation of the hash table data


structure or Hashing. The objects that we insert into the
HashSet do not guarantee to be inserted in the same order. The
objects are inserted based on their hash code.
 Traversing through a HashSet
 Printing Elements of a HashSet
 Getting Random Element from a HashSet
 Finding Maximum and Minimum Element from HashSet
AbstractSet class implements the Collection interface and
extends the AbstractCollection class. It provides a skeletal
implementation of the Set interface. This class does not
override any of the implementations from the
AbstractCollection class, but merely adds implementations for
equals() and hashCode() method.
Copy On Write ArraySet uses an
internal CopyOnWriteArrayList for all of its operations. It was
introduced in JDK 1.5, we can say that it is a thread-safe version
of Set. To use this class, we need to import it from
'java.util.concurrent' package.
Concurrent HashMap class is introduced in JDK 1.5 belongs to
'java.util.concurrent' package, which implements
ConcurrentMap as well as to Serializable interface also.
ConcurrentHashMap is an enhancement of HashMap as we
know that while dealing with Threads in our application
HashMap is not a good choice because performance-wise
HashMap is not up to the mark.
LinkedHashSet is very similar to a HashSet. The difference is
that this uses a doubly linked list to store the data and retains
the ordering of the elements.
 Accessing LinkedHashSet elements by Index
 Removing elements from a LinkedHashSet
EnumSet is one of the specialized implementations of the Set
interface for use with the enumeration type as it extends
AbstractSet class and implements Set Interface. It does not
allow null objects and is not synchronized resulting in a high-
performance Set even faster than HashSet. Also, It uses a fail-
safe iterator, so it won’t
throw ConcurrentModificationException if the collection is
modified while iterating.
SortedSet Interface
It is present in java.util package extends the Set interface
present in the collection framework. It is an interface that
implements the mathematical set. This interface contains the
methods inherited from the Set interface and adds a feature
that stores all the elements in this interface to be stored in a
sorted manner.
1. TreeSet class is an implementation of the Self-balancing
binary tree-like a red-black tree. The ordering of the elements is
maintained by a set using their natural ordering.
 Difference between EnumSet and TreeSet
 Creating a TreeSet with a List

2. Navigable Set class inherits from the Sorted Set interface.


It behaves like a Sorted Set with the exception that we have
navigation methods available in addition to the sorting
mechanisms of the Sorted Set.
3. ConcurrentSkipListSet class implements the Collection
interface and the AbstractSet class. It provides a scalable and
concurrent version of NavigableSet in Java. The implementation
of ConcurrentSkipListSet is based on ConcurrentSkipListMap.
The elements in ConcurrentSkipListSet are sorted by default in
their natural ordering or by a Comparator provided at set
creation time, depending on which constructor is used.
Map Interface
Map is a data structure that supports the key-value pair
mapping for the data. It is mainly used in the scenarios where
Students are needed to be searched or removed or added
according to the roll numbers or in item-frequency scenarios
where items are stored along with their frequencies.
HashTable class implements a hash table, which maps keys to
values. Any non-null object can be used as a key or as a
value. To successfully store and retrieve objects from a hash
table, the objects used as keys must implement the hashCode
method and the equals method.
 Adding Elements to HashTable
 Removing Elements from HashTable
 How HashTable Works?
HashMap uses a technique called Hashing. It provides the
basic implementation of the Map interface of Java. It stores the
data in (Key, Value) pairs. To access a value in a HashMap, we
must know its key.
 Add an Element to HashMap
 Remove an Element from HashMap
LinkedHashMap is very similar to a HashSet because the
primary difference is that this uses a doubly linked list to store
the data and retains the ordering of the elements.
 Difference between LinkedHashMap vs TreeMap vs
HashMap
 Hashing in Java
 Iterating in a LinkedHashMap
 Checking Size of LinkedHashMap
ConcurrentHashMap class implements ConcurrentMap as
well as Serializable interface also. ConcurrentHashMap is an
enhancement of HashMap as we know that while dealing with
Threads in our application HashMap is not a good choice
because performance-wise HashMap is not up to the mark.
SortedMap interface extends the Map interface and provides a
total ordering of its elements (elements can be traversed in
sorted order of keys). This class implements the TreeMap
interface.
NavigableMap interface belongs to 'java.util' package, and It
is an extension of SortedMap which provides convenient
navigation methods like lowerKey, floorKey, ceilingKey, and
higherKey, and along with this popular navigation method it
also provides ways to create a Sub Map from an existing Map in
Java. For Example, headMap whose keys are less than the
specified key, tailMap whose keys are greater than the specified
key, and a subMap that strictly contains keys that fall between
toKey and fromKey.
ConcurrentMap interface been introduced in JDK 1.5
represents a Map that is capable of handling concurrent access
to it without affecting the consistency of entries in a map.
ConcurrentMap interface present in
'java.util.concurrent package'. It provides some extra methods
apart from what it inherits from the SuperInterface i.e.
java.util.Map. It has inherited the Nested Interface
Map.Entry<K, V>
TreeMap is used to implement the Map interface and
NavigableMap along with the AbstractMap Class. The map is
sorted according to the natural ordering of its keys, or by
a Comparator provided at map creation time, depending on
which constructor is used. This proves to be an efficient way of
sorting and storing the key-value pairs. The storing order
maintained by the Tree Map must be consistent with equals just
like any other sorted map, irrespective of the explicit
comparators. The Tree Map implementation is not synchronized
in the sense that if a map is accessed by multiple threads,
concurrently and at least one of the threads modifies the map
structurally, it must be synchronized externally.
AbstractMap class directly implements the Map interface to
provide a structure to it, by doing so it makes the further
implementations easier. As the name suggests Abstract Map is
an abstract class by definition, therefore it cannot be used to
create objects. The concrete classes that inherit from Abstract
Map can be used to create objects.
EnumMap is a specialized implementation of the Map interface
for enumeration types. It extends Abstract Map and implements
the Map interface in Java. It belongs to 'java.util' package. It is
an ordered collection, and they are maintained in the natural
order of their keys.
Queue Interface
Queue maintains the FIFO (First In First Out) order similar to a
real-world queue line. This interface is dedicated to storing all
the elements where the order of the elements matter consisting
of the Priority Queue, LinkedList, and Array Deque.
ArrayBlockingQueue class is a bounded blocking queue
backed by an array. By bounded, it means that the size of the
Queue is fixed. Once created, the capacity cannot be changed.
Attempts to put an element into a full queue will result in the
operation blocking.
 Access the Element from the front of ArrayBlockingQueue
 Access Element Sequentially from an ArrayBlockingQueue
 Insert the Specified Element to the ArrayBlockingQueue
 Convert ArrayBlockingQueue to a String
ArrayDeque is implemented in the collection framework
provides us with a way to apply a re-sizable array. This is a
special kind of array that grows and allows users to add or
remove an element from both sides of the queue.
 Add Elements to ArrayDeque
 Access Elements from Array Deque
 Remove Elements from Array Deque
Abstract Queue provides skeletal implementations of some
queue operations. The implementations in this class are
appropriate when the base implementation does not allow null
elements. Methods add, remove, and element are based on
offer, poll, and peek, respectively, but throw exceptions instead
of indicating failure via false or null returns.
Blocking Queue interface in Java is added in Java 1.5 along
with various other concurrent Utility classes like Concurrent
HashMap, Counting Semaphore, CopyOnWriteArrrayList, etc.
Blocking Queue interface supports flow control (in addition to
queue) by introducing blocking if either Blocking Queue is full
or empty. A thread trying to enqueue an element in a full queue
is blocked until some other thread makes space in the queue,
either by dequeuing one or more elements or clearing the
queue completely. Similarly, it blocks a thread trying to delete
from an empty queue until some other threads insert an item.
Blocking Queue does not accept a null value. If we try to
enqueue the null item, then it throws NullPointerException.
Priority Queue is based on the priority heap. It is used when
the objects are supposed to be processed based on priority. It is
known that a queue follows the First-In-First-Out algorithm, but
sometimes the elements of the queue are needed to be
processed according to the priority and this class is used in
these cases.
 To Check the Presence of an Element in a PriorityQueue
 Access PriorityQueue using peek() method
 Insert Element to PriorityQueue using add() method
PriorityBlockingQueue is an unbounded blocking queue that
uses the same ordering rules as class Priority Queue and
supplies blocking retrieval operations. Since it is unbounded,
adding elements may sometimes fail due to resource
exhaustion resulting in OutOfMemoryError. This class does not
permit null elements.
DelayQueue class belongs to 'java.util.concurrent' package
implementing the BlockingQueue interface. DelayQueue is a
specialized PriorityQueue that orders elements based on their
delay time. It means that only those elements can be taken
from the queue whose time has expired.
ConcurrentLinkedQueue class also belongs
to 'java.util.concurrent' package. It was introduced in JDK 1.5. It
is used to implement Queue with the help of LinkedList
concurrently. It is an unbounded thread-safe implementation of
Queue which inserts elements at the tail of the Queue in a
FIFO(first-in-first-out) fashion. It can be used when an
unbounded Queue is shared among many threads. This class
does not permit null elements. Iterators are weakly consistent.
This class and its iterator implement all of the optional methods
of the Queue and Iterator interfaces.
LinkedBlockingQueue is an optionally-bounded blocking
queue based on linked nodes. It means that the
LinkedBlockingQueue can be bounded, if its capacity is given,
else the LinkedBlockingQueue will be unbounded. The capacity
can be given as a parameter to the constructor of
LinkedBlockingQueue. This queue orders elements FIFO (first-
in-first-out). It means that the head of this queue is the oldest
element of the elements present in this queue. The tail of this
queue is the newest element of the elements of this queue. The
newly inserted elements are always inserted at the tail of the
queue, and the queue retrieval operations obtain elements at
the head of the queue.
LinkedTransferQueue class again belonging to
'java.util.concurrent' package. It implements the TransferQueue
and provides an unbounded functionality based on linked
nodes. The elements in the LinkedTransferQueue are ordered in
FIFO order, with the head pointing to the element that has been
on the Queue for the longest time and the tail pointing to the
element that has been on the queue for the shortest time.
Because of its asynchronous nature, size() traverses the entire
collection, so it is not an O(1) time operation. It may also give
inaccurate size if this collection is modified during the traversal

Deque Interface
Deque or double-ended queue is a data structure where we can
add and remove the elements from both ends of the queue.
This interface extends the queue interface. The class which
implements this interface namely are LinkedList and
ArrayDeque, and Collection class. Here in the Collection class, it
has implementation of basic algorithms like binary search,
sort(), max(), min(), reverse(), fill(), etc. These algorithms are
implemented on the above-mentioned collection frameworks.
ArrayDeque class in Java provides a way to apply resizable-
array in addition to the implementation of the Deque interface.
It is also known as Array Double Ended Queue(Array Deck). This
is a special kind of array that grows and allows users to add or
remove an element from both sides of the queue.
BlockingDeque interface gets its name because it blocks
illegal operations such as insertion into a full queue or deletion
from an empty queue, all of these properties are inbuilt into the
structure of this interface. Since it is a deque (double-ended
queue) that is the insertion, deletion, and traversal operations
can be performed from both ends. The BlockingDeque is an
interface, so we cannot declare any objects with it.
ConcurrentLinkedDeque class implements the Collection
interface and the AbstractCollection class. It belongs
to java.util.concurrent package. It is used to implement Deque
with the help of LinkedList concurrently. It does not permit null
elements and iterators and spliterators are weakly consistent.
Now basic knowledge about the collection to write some basic
programs invoking the interfaces with the help of object in the
collection is over. It's time a move a bit deeper down so here a
new concept is popularly known as Generics comes into play to
jump into. Writing codes with little knowledge is dangerous.
The idea is to allow type (Integer, String, … etc, and user-
defined types) to be a parameter to methods, classes, and
interfaces. For example, classes like HashSet, Array List,
HashMap, etc use generics very well. The generic classes can
only be used for non-primitive types and wrapper classes. Here
are the major advantages of Generics in Java:
1. Write once for any primitive data type as in any generic
function code can be written down for computation over
non-primitive and later on during compilation these data
types are passed in a generic function.
2. It can be class like Array List, LinkedList, Stack, Vector, etc.
It can be an interface like Set, Map, etc as discussed
above, or it can include functions like sort(), max(), min(),
etc. Hence, they are extensively used in Collection.
3. They are type-safe as they commit errors at compile time.
Unlike arrays, different instantiations of a generic type is not
compatible with each other, not even explicitly. This
incompatibility may be softened by the wildcard if? is used as
an actual type parameter.
The question mark (?) is known as the wildcard in generic
programming. It represents an unknown type. The wildcard can
be used in a variety of situations such as the type of parameter,
field, or local variable; sometimes as a return type.

User cases
 Example 1: A simple java program to demonstrate the
compile-time errors without the involvement of generics
into play. Here 'ClassCastException' is thrown by the
program

// Importing all classes from


// java.util package
import java.util.*;

// Class 1
// Object class
class GenericClass {

// Object of this class


Object x;
Object y;
}

// Class 2
// Main class
class GFG {
// Min driver method
public static void main(String[] args)
{
// Creating a generic class object
// Here 'go' is name of object
GenericClass go = new GenericClass();
// Compiles fine because
// p being an object accepts integer
go.x = 007;

// Compiles fine because


// p being an object accepts string
go.y = "JamesBond";

// Forcefully explicitly typecasting the


// integer value to string
String str = (String)go.x;

// This throws a ClassCastException


// as go.x object is an integer and
// cannot be casted to a string
}
 }

Example 2: Generic concept been introduced


though it will not append output illustrating
incompatible type-casting of data types.

// Importing all classes from


// java.util package
import java.util.*;
// Class 1
// Sample generic class
class GenericClass {

T x;
S y;
}

// Class 2
// Main class
class GFG {

// Main driver method


public static void main(String[] args)
{

// Creating an object of above generic class


// in the main method()
// Declaring object of Integer and String type
GenericClass go
= new GenericClass();

// It will successfully compile as it


// is an object will will accept
// integer and String types
go.x = 007;
go.y = "JamesBond";

// Forcefully explicitly typecasting it to


// string type which will
// generate a compiler eror
String str = (String)go.x;
}
}

Now jumping to the most important topic along with which


most concepts revolve during implementation, yes you guess it
right geek Collection interface. The Collection is the root
interface for most of the interfaces in the Collection framework
in Java
Therefore, Collection has sub-interfaces like Set, List, and
Queue which implements the Collection interface which in turn
is inherited from the Iterable interface. This is the root interface
for the entire collection framework. The Collection interface
extends the Iterable interface. Therefore, inherently, all the
interfaces and classes implement this interface. The main
functionality of this interface is to provide an iterator for the
collections. Therefore, this interface contains only one abstract
method which is the iterator. It returns the

Iterator ();
Now, dwelling onto different methods present in Collection such
as add(), removes (), remove All(), contains(), contains All(),
stream() and many more. In order to use so, one has to
get a clear understanding of the same. Let's peek out for
various functions causing bulk operations namely
1. contains All () Method
2. add All () Method
3. remove All () Method
4. retain All () Method

Function 1: containsAll() Method


boolean containsAll(Collection c)
This method takes a collection c and checks whether all the
elements of c is present in this Collection or not. The method
returns True if all elements in the collection c, are present in
this Collection otherwise it returns False.

Function 2: addAll() Method


boolean addAll(Collection c)
This method is used to append all the elements from the
collection c passed as a parameter to the end of this collection
keeping in mind the order of return by the collection's iterator.
The method returns true if at least one action of append is
performed.

Function 3: removeAll() Method


boolean removeAll(Collection c)
This method is used to remove from this collection all of its
elements that are contained in the specified collection c. This
method returns true if this list changed as a result of the call.
This method uses a wildcard character signifying that a
collection of any data types can be passed into
the removeAll() method. These are the methods been most
frequently used.

Function 4: retainAll() Method


This method of ArrayList is used to remove all the array list’s
elements that are not contained in the specified collection or
retains all matching elements in the current ArrayList instance
that match all elements from the Collection list passed as a
parameter to the method.
public boolean retainAll(Collection C)
Rest there are other methods to been put in the usage such as
removeIf() and retainAll(). Do visit them thoroughly in order to
get a better understanding of them too before we now finally
end up this article with iterating through Collections. Let's try to
implement a few of them to get a better understanding of
them.

// Method 1 - containsAll()
// Java Program to illustrate
// containsAll() method

// Importing all classes frm


// java.util package
import java.util.*;

// Class
class GfG {

// Main driver method


public static void main(String args[])
{
// Creating an empty list
// Declaring an Integer type object
List list1 = new ArrayList();

// Adding custom elements to the List


// using add() method
list1.add(10);
list1.add(20);
list1.add(30);
list1.add(40);
list1.add(50);

// Creating another empty list of Integer type


List list2 = new ArrayList();

// Again, adding custom elements to the List


// using add() method
list2.add(10);
list2.add(30);

// Returns true if all the elements of list2


// is present in list1
// If not, return false
System.out.println(list1.containsAll(list2));
}
}

// Method 2 - addAll()
// Java program to illustrate
// boolean addAll(Collection c)

// Importing all classes from


// java.util package
import java.util.*;

// Class
class GFG {

// Main driver method


public static void main(String args[])
{

// Creating an empty array list1


// Declaring an object of Integer type
List list1 = new ArrayList();

// Adding custom elements in the list1


// using add() method
list1.add(10);
list1.add(20);
list1.add(30);
list1.add(40);

// Again, creating an empty array list2


// Declaring an object of Integer type
List list2 = new ArrayList(5);

// Adding custom elements in the list1


// using add() method
list2.add(40);
list2.add(50);

// inserting all elements where


// list2 will get printed after list1
list1.addAll(list2);

// Print the updated List


System.out.println(list1);
}
}

// Method 3 - reomoveAll()
// Java program to demonstrate
// removeAll() method
import java.util.*;

// Class
public class GFG {

// Main driver method


public static void main(String[] argv)
{

// Creating object of ArrayList


List list1 = new ArrayList();

// Populating the list1


list1.add(10);
list1.add(20);
list1.add(30);

// Creating another object of ArrayList


// Declaring object of Integer type
List list2 = new ArrayList();

// Populating the list1


list2.add(10);
list2.add(20);

// Removing elements from list1


// specified in list2
// using removeAll() method
list1.removeAll(list2);

// Printing the list1


System.out.println(list1);
}
}

// Method 4 - retainAll() method


// Java program to demonstrate
// retainAll() method

// Importing all classes from


// java.util package
import java.util.*;

// Class
public class GFG {

// Main driver method


public static void main(String[] argv)
{
// Creating an object of ArrayList
// Declaring object of Integer type
List list1 = new ArrayList();

// Populating the list1


// Custom inputs
list1.add(10);
list1.add(20);
list1.add(30);
// Creating an another object of ArrayList
// Declaring an object of Integer type
List list2 = new ArrayList();

// Populating the list1


list2.add(10);
list2.add(20);

// Retains elements of list1 specified in list2


// using retainAll() method
list1.retainAll(list2);

// Printing the list1


System.out.println(list1);
}
}

Now lets us discuss all the methods been proposed for iteration
are as follows:

1. Iterators Method
2. For-each loop Method
3. forEach() Method
4. Streams Method
There are various ways of traversing through a collection
namely:
Method 1: Iterators Method
The Iterable interface provides an iterator() method which
implements the Iterator interface. The iterator interface
provides 3 functions(hasNext(), next(), remove()) which are
used to iterate through the collection and also remove
elements from the collection. The collection interface extends
the iterable interface. Therefore, inherently, all the interfaces
and classes implement this interface. The main functionality of
this interface is to provide an iterator for the collections.
Therefore, this interface contains only one abstract method
which is the iterator.

Method 2: For-each loop Method


For-each is another array traversing technique like for loop,
while loop, do-while loop introduced in Java5. It’s commonly
used to iterate over an array or a Collections class (eg,
ArrayList).
for (type var : array)
{
statements using var;
}

Method 3: forEach() is also present in the Iterable interface.


Therefore, it can be implemented by all the collections in the
framework that extends the Iterable interface. Whenever is
needed to traverse over a collection we have to create an
Iterator to iterate over the collection, and then we can have our
business logic inside a loop for each of the elements inside the
collection.
public void forEach(Consumer action)

Method 4: Streams Method is the most recommended


method to traverse through a collection or perform any
operation of filter or segregation. This was added in Java 8.
There are several benefits of using stream:
 It can be parallel and infinite.
 No storage.
 Functions can be pipelined.
 The most important benefit of Streams is that we can
create them from the collection, arrays, Files Lines, and
even from methods in Stream.

// Method 1
// Traversing a collection
// using for-each loop
import java.util.*;

// Main class
class GFG {

// Main driver method


public static void main(String[] args)
{

// Creating an List object


// Declaring object of Integer type
List l = Arrays.asList(10, 15, 7, 20, 40);

// Now, traversal part


// using for-each method

// Looking for element x in List


for (Integer x : l)

// Print and dispaly all elements


// in the List
System.out.print(x + " ");
}
}

// Method 2
// Traverssing a collection
// using forEach() method

// Importing all classes from


// java.util package
import java.util.*;

// Class
class GFG {
// Main driver method
public static void main(String[] args)
{

// ArrayList containing elements


List l = Arrays.asList(10, 15, 7, 20, 40);

// Traversing a collection
// using forEach() method
l.forEach(x -> System.out.println(x));
}
}

// Method 3
// Traversing a collection
// using stream() method

// Importing all classes from


// java.util package
import java.util.*;

// Class
class GFG {
// Main driver method
public static void main(String[] args)
{

// Creating List object of Integer type


// Custom input entries
List l = Arrays.asList(10, 15, 7, 20, 40);

// Traversing a collection
// using stream method
// to print elements with a space
l.stream().forEach(x -> System.out.print(x + " "));

// New line
System.out.println();

// Step 1: Creating a stream over the ArrayList


// Step 2: Filtering the elements greater than 10
// Step 3: Filtering the even elements
// Step 4: Printing the list with space
l.stream()
.filter(x -> x > 10)
.filter(x -> x % 2 == 0)
.forEach(x -> System.out.print(x + " "));
}
}

Topic 2: - Arrays in Java


Arrays in Java are one of the most fundamental data
structures that allow us to store multiple values of the
same type in a single variable. They are useful for
storing and managing collections of data. Arrays in Java
are objects, which makes them work differently
from arrays in C/C++ in terms of memory management.
For primitive arrays, elements are stored in a
contiguous memory location, For non-primitive
arrays, references are stored at contiguous locations,
but the actual objects may be at different locations in
memory.
Key features of Arrays:
 Contiguous Memory Allocation (for Primitives): Java
array elements are stored in continuous memory
locations, which means that the elements are
placed next to each other in memory.
 Zero-based Indexing: The first element of the array
is at index 0.
 Fixed Length: Once an array is created, its size is
fixed and cannot be changed.
 Can Store Primitives & Objects: Java arrays can hold
both primitive types (like int, char, Boolean, etc.)
and objects (like String, Integer, etc.)
Example: This example demonstrates how to initialize
an array and traverse it using a for loop to print each
element.
public class Main {
public static void main (String[] args)
{

// initializing array
int[] arr = { 1, 2, 3, 4, 5 };

// size of array
int n = arr.length;

// traversing array
for (int i = 0; i < n; i++)
System.out.print(arr[i] + " ");
}
}
Basics of Arrays in Java
There are some basic operations we can start with as
mentioned below:
1. Array Declaration
To declare an array in Java, use the following syntax:
type[] arrayName;
 type: The data type of the array elements
(e.g., int, String).
 arrayName: The name of the array.
Note: The array is not yet initialized.
2. Create an Array
To create an array, you need to allocate memory for it using
the new keyword:
// Creating an array of 5 integers
int[] numbers = new int[5];
This statement initializes the numbers array to hold 5 integers.
The default value for each element is 0.
3. Access an Element of an Array
We can access array elements using their index, which starts
from 0:
// Setting the first element of the array
numbers[0] = 10;
// Accessing the first element
int firstElement = numbers[0];
The first line sets the value of the first element to 10. The
second line retrieves the value of the first element.
4. Change an Array Element
To change an element, assign a new value to a specific index:
// Changing the first element to 20
numbers[0] = 20;
5. Array Length
We can get the length of an array using the length property:
// Getting the length of the array
int length = numbers.length;
Now, we have completed with basic operations so let us go
through the in-depth concepts of Java Arrays, through the
diagrams, examples, and explanations.
In-Depth Concepts of Java Array
Following are some important points about Java arrays.
Array Properties
 In Java, all arrays are dynamically allocated.
 Arrays may be stored in contiguous
memory [consecutive memory locations].
 Since arrays are objects in Java, we can find their length
using the object property length. This is different from
C/C++, where we find length using size of.
 A Java array variable can also be declared like other
variables with [] after the data type.
 The variables in the array are ordered, and each has an
index beginning with 0.
 Java array can also be used as a static field, a local
variable, or a method parameter.
An array can contain primitives (int, char, etc.) and object (or
non-primitive) references of a class, depending on the definition
of the array. In the case of primitive data types, the actual
values might be stored in contiguous memory locations (JVM
does not guarantee this behavior). In the case of class
objects, the actual objects are stored in a heap segment.
Note: This storage of arrays helps us randomly access the
elements of an array [Support Random Access].
Creating, Initializing, and Accessing an Arrays in Java
For understanding the array we need to understand how it
actually works. To understand this follow the flow mentioned
below:
 Declare
 Initialize
 Access

1. Declaring an Array
The general form of array declaration is
Method 1:
type var-name[];
Method 2:
type[] var-name;
The element type determines the data type of each element
that comprises the array. Like an array of integers, we can also
create an array of other primitive data types like char, float,
double, etc., or user-defined data types (objects of a class).
Note: It is just how we can create is an array variable, no
actual array exists. It merely tells the compiler that this
variable (int Array) will hold an array of the integer type.
Now, let us provide memory storage to this created array.
2. Initialization an Array in Java
When an array is declared, only a reference of an array is
created. The general form of new as it applies to one-
dimensional arrays appears as follows:
var-name = new type [size];
Here, type specifies the type of data being
allocated, size determines the number of elements in the array,
and var-name is the name of the array variable that is linked to
the array. To use new to allocate an array, you must specify
the type and number of elements to allocate.

Example:
// declaring array
int intArray[];
// allocating memory to array
intArray = new int[20];
// combining both statements in one
int[] intArray = new int[20];
Note: The elements in the array allocated by new will
automatically be initialized to zero (for numeric
types), false (for boolean), or null (for reference types). Do
refer to default array values in Java.
Obtaining an array is a two-step process. First, you must
declare a variable of the desired array type. Second, you must
allocate the memory to hold the array, using new, and assign it
to the array variable. Thus, in Java, all arrays are
dynamically allocated.

Array Literal in Java


In a situation where the size of the array and variables of the
array are already known, array literals can be used.
// Declaring array literal
int[] intArray = new int[]{ 1,2,3,4,5,6,7,8,9,10 };
 The length of this array determines the length of the
created array.
 There is no need to write the new int[] part in the latest
versions of Java.

3. Accessing Java Array Elements using for Loop


Now, we have created an Array with or without the values
stored in it. Access becomes an important part to operate over
the values mentioned within the array indexes using the points
mentioned below:
 Each element in the array is accessed via its index.
 The index begins with 0 and ends at (total array size)-1.
 All the elements of array can be accessed using Java for
Loop.
Let us check the syntax of basic for loop to traverse an array:
// Accessing the elements of the specified array
for (int i = 0; i < arr.length; i++)
System.out.println("Element at index " + i + " : "+ arr[i]);

// Java program to illustrate creating an array


// of integers, puts some values in the array,
// and prints each value to standard output.

Class GFG {
public static void main (String[] args)
{
// declares an Array of integers.
int[] arr;

// allocating memory for 5 integers.


arr = new int[5];
// initialize the elements of the array
// first to last(fifth) element
arr[0] = 10;
arr[1] = 20;
arr[2] = 30;
arr[3] = 40;
arr[4] = 50;

// accessing the elements of the specified array


for (int i = 0; i < arr.length; i++)
System.out.println("Element at index "
+ i + " : " + arr[i]);
}
}
Output:-
Element at index 0 : 10
Element at index 1 : 20
Element at index 2 : 30
Element at index 3 : 40
Element at index 4 : 50

Types of Arrays in Java


Java supports different types of arrays:
1. Single-Dimensional Arrays
These are the most common type of arrays, where elements
are stored in a linear order.
// A single-dimensional array
int[] singleDimArray = {1, 2, 3, 4, 5};

2. Multi-Dimensional Arrays
Arrays with more than one dimension, such as two-dimensional
arrays (matrices).
// A 2D array (matrix)
int[][] multiDimArray = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9} };
You can also access java arrays using for each loops.

Arrays of Objects in Java


An array of objects is created like an array of primitive-type
data items in the following way.
Syntax:
Method 1:
ObjectType[] arrName;
Method 2:
ObjectType arrName[];
Example of Arrays of Objects
Example: Here we are taking a student class and creating an
array of Student with five Student objects stored in the array.
The student objects have to be instantiated using the
constructor of the Student class, and their references should be
assigned to the array elements.

// Java program to illustrate creating


// an array of objects

class Student {
public int roll_no;
public String name;

Student (int roll_no, String name){


this.roll_no = roll_no;
this.name = name;
}
}

public class Main {


public static void main(String[] args){

// declares an Array of Student


Student [] arr;
// allocating memory for 5 objects of type Student.
arr = new Student [5];

// initialize the elements of the array


arr[0] = new Student (1, "aman");
arr[1] = new Student (2, "vaibhav");
arr[2] = new Student (3, "shikar");
arr[3] = new Student (4, "dharmesh");
arr[4] = new Student (5, "mohit");

// accessing the elements of the specified array


for (int i = 0; i < arr.length; i++)
System.out.println("Element at " + i + " : { "
+ arr[i].roll_no + " "
+ arr[i].name+" }");
}
}
Output:-
Element at 0 : { 1 aman }
Element at 1 : { 2 vaibhav }
Element at 2 : { 3 shikar }
Element at 3 : { 4 dharmesh }
Element at 4 : { 5 mohit }

2. Example: An array of objects is also created like


// Java program to illustrate creating
// an array of objects

class Student{
public String name;

Student(String name){
this.name = name;
}

@Override
public String toString(){
return name;
}
}

public class Main{


public static void main (String[] args){

// declares an Array and initializing the


// elements of the array
Student[] myStudents = new Student[]{
new Student("Dharma"),new Student("sanvi"),
new Student("Rupa"),new Student("Ajay")
};
// accessing the elements of the specified array
for(Student m:myStudents){
System.out.println(m);
}
}
}
Output:-
Dharma
sanvi
Rupa
Ajay

What happens if we try to access elements outside the


array size?
JVM throws ArrayIndexOutOfBoundsException to indicate
that the array has been accessed with an illegal index. The
index is either negative or greater than or equal to the size of
an array.
Below code shows what happens if we try to access
elements outside the array size:
// Code for showing error "ArrayIndexOutOfBoundsException"

public class GFG {


public static void main(String[] args)
{
int[] arr = new int[4];
arr[0] = 10;
arr[1] = 20;
arr[2] = 30;
arr[3] = 40;

System.out.println(
"Trying to access element outside the size of array");
System.out.println(arr[5]);
}
}
Output:-
Trying to access element outside the size of array
Exception in thread "main"
java.lang.ArrayIndexOutOfBoundsException: Index 5 out of
bounds for length 4
at GFG.main(GFG.java:13)

Multidimensional Arrays in Java


Multidimensional arrays are arrays of arrays with each
element of the array holding the reference of other arrays. A
multidimensional array is created by appending one set of
square brackets ([]) per dimension.
Syntax:
There are 2 methods to declare Java Multidimensional Arrays
as mentioned below:
// Method 1
datatype [][] arrayrefvariable;
// Method 2
datatype arrayrefvariable[][];
Declaration:
// 2D array or matrix
int[][] intArray = new int[10][20];
// 3D array
int[][][] intArray = new int[10][20][10];
Java Multidimensional Arrays Examples
Example: Let us start with basic two-dimensional Array
declared and initialized.
// Java Program to demonstrate
// Multidimensional Array
import java.io.*;

class GFG {
public static void main(String[] args){

// Two Dimensional Array


// Declared and Initialized
int[][] arr = new int[3][3];
// Number of Rows
System.out.println("Rows : " + arr.length);

// Number of Columns
System.out.println("Columns : " + arr[0].length);
}
}
Output:-
Rows:3
Columns:3

Example: Now, after declaring and initializing the array we will


check how to Traverse the Multidimensional Array using for loop

// Java Program to Multidimensional Array

// Driver Class
public class multiDimensional {
// main function
public static void main(String args[])
{
// declaring and initializing 2D array
int arr[][] = { { 2, 7, 9 }, { 3, 6, 1 }, { 7, 4, 2 } };

// printing 2D array
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++)
System.out.print(arr[i][j] + " ");

System.out.println();
}
}
}
Output:-
279
361
742
Passing Arrays to Methods: -
Like variables, we can also pass arrays to methods. For
example, the below program passes the array to
method sum to calculate the sum of the array's values.

// Java program to demonstrate


// passing of array to method

public class Test {


// Driver method
public static void main(String args[])
{
int arr[] = { 3, 1, 2, 5, 4 };

// passing array to method m1


sum(arr);
}

public static void sum(int[] arr)


{
// getting sum of array values
int sum = 0;

for (int i = 0; i < arr.length; i++)


sum += arr[i];

System.out.println("sum of array values : " + sum);


}
}

Output
sum of array values: 15

Returning Arrays from Methods


As usual, a method can also return an array. For example, the
below program returns an array from method m1.
// Java program to demonstrate
// return of array from method

class Test {
// Driver method
public static void main(String args[])
{
int arr[] = m1();

for (int i = 0; i < arr.length; i++)


System.out.print(arr[i] + " ");
}

public static int[] m1()


{
// returning array
return new int[] { 1, 2, 3 };
}
}
Output
1 2 3

Java Array Members


Now, as you know that arrays are objects of a class, and a
direct superclass of arrays is a class Object.
The members of an array type are all of the following:
 The public final field length contains the number of
components of the array. Length may be positive or zero.
 All the members are inherited from class Object; the only
method of Object that is not inherited is its clone method.
 The public method clone() overrides the clone method in
class Object and throws no checked exceptions.

Arrays Types and Their Allowed Element Types

Array Types Allowed Element Types

Any type which can be


Primitive Type Arrays implicitly promoted to
declared type.

Either declared type objects or


Object Type Arrays
it's child class objects.

Its child-class objects are


Abstract Class Type Arrays
allowed.

Its implementation class


Interface Type Arrays
objects are allowed.

Cloning Arrays in Java


1. Cloning of Single-Dimensional Array
When you clone a single-dimensional array, such as Object[],
a shallow copy is performed. This means that the new array
contains references to the original array's elements rather than
copies of the objects themselves. A deep copy occurs only with
arrays containing primitive data types, where the actual values
are copied.
// Java program to demonstrate
// cloning of one-dimensional arrays

class Test {
public static void main(String args[])
{
int intArray[] = { 1, 2, 3 };

int cloneArray[] = intArray.clone();

// will print false as shallow copy is created


System.out.println(intArray == cloneArray);

for (int i = 0; i < cloneArray.length; i++) {


System.out.print(cloneArray[i] + " ");
}
}
}
Output
false
123

2. Cloning Multidimensional Array


A clone of a multi-dimensional array (like Object [][]) is a
"shallow copy," however, which is to say that it creates only a
single new array with each element array a reference to an
original element array, but subarrays are shared.

// Java program to demonstrate


// cloning of multi-dimensional arrays

class Test {
public static void main(String args[])
{
int intArray[][] = { { 1, 2, 3 }, { 4, 5 } };
int cloneArray[][] = intArray.clone();

// will print false


System.out.println(intArray == cloneArray);

// will print true as shallow copy is created


// i.e. sub-arrays are shared
System.out.println(intArray[0] == cloneArray[0]);
System.out.println(intArray[1] == cloneArray[1]);
}
}
Output
false
true
true

Common Operations
The below table demonstrates the common array operations

Operati
on Example

Sort Arrays.sort(arr);

Arrays.binarySearch(arr,
Search key);

Copy int[] copy =


Operati
on Example

Arrays.copyOf(arr, len);

Fill Arrays.fill(arr, 0);

Advantages of Java Arrays


 Efficient Access: Accessing an element by its index is
fast and has constant time complexity, O(1).
 Memory Management: Arrays have fixed size, which
makes memory management straightforward and
predictable.
 Data Organization: Arrays help organize data in a
structured manner, making it easier to manage related
elements.

Disadvantages of Java Arrays


 Fixed Size: Once an array is created, its size cannot be
changed, which can lead to memory waste if the size is
overestimated or insufficient storage if underestimated.
 Type Homogeneity: Arrays can only store elements of
the same data type, which may require additional
handling for mixed types of data.
 Insertion and Deletion: Inserting or deleting elements,
especially in the middle of an array, can be costly as it
may require shifting elements.

Common Mistakes to Avoid


The common mistakes that can occur when working with arrays
in Java are listed below:
 Accessing Out-of-Bounds Index: When we try to
access an index that is outside the range of the array (i.e,
negative index or index greater than or equal to the
array's length) it will thrown an index
ArrayIndexOutOfBoundsException. Always ensure that the
index is within the bounds, which should be between 0
and array.length - 1.
 Assuming Array Size Can Change: Java arrays are fixed
in size. Attempting to change the size of an array (like
adding or removing elements) is not possible directly.
 Not Initializing Array Elements: If we create an array
but do not explicitly initialize its elements, Java will
automatically assign default values based on the type
o For numeric types (e.g., int, double): The default
value is 0.
o For boolean arrays: The default value is false.
o For object arrays: The default value is null.

Best Practices
The best practices when working with arrays in Java are listed
below:
 Use For-Each Loop When Possible (Avoids Index
Errors): The for-each loop is a best way to iterate over
arrays elements without worrying about index-out-of-
bounds errors. It reduces the risk of accessing invalid
indices.
 Check Array Length Before Accessing
Elements: Always check the length of the array
using array.length before attempting to access an
element. This helps prevent
ArrayIndexOutOfBoundsException.
 Use Arrays.copyOf() Instead of Manual Copying: If
you need to copy an array, use Arrays.copyOf() rather
than manually copying each element with a loop.
 Prefer ArrayList if Dynamic Resizing is Needed: If
you require a collection that can dynamically resize (add
or remove elements), consider using ArrayList instead of
an array.

Topic 3: - Java Constructors


In Java, constructors play an important role in object
creation. A constructor is a special block of code that is
called when an object is created. Its main job is to
initialize the object, to set up its internal state, or to
assign default values to its attributes. This process
happens automatically when we use the "new" keyword
to create an object.
Characteristics of Constructors:
 Same Name as the Class: A constructor has the
same name as the class in which it is defined.
 No Return Type: Constructors do not have any
return type, not even void. The main purpose of a
constructor is to initialize the object, not to return a
value.
 Automatically Called on Object Creation: When an
object of a class is created, the constructor is called
automatically to initialize the object’s attributes.
 Used to Set Initial Values for Object
Attributes: Constructors are primarily used to set
the initial state or values of an object’s attributes
when it is created.
Now, let us look at a simple example to understand how
a constructor works in Java.
Example: This program demonstrates how a constructor
is automatically called when an object is created in Java.
// Java Program to demonstrate
// Constructor usage
import java.io.*;

// Driver Class
class Geeks {

// Constructor
Geeks()
{
super();
System.out.println("Constructor Called");
}

// main function
public static void main(String[] args)
{
Geeks geek = new Geeks();
}
}

Output
Constructor Called

Note: It is not necessary to write a constructor for a


class. It is because the Java compiler creates a default
constructor (constructor with no arguments) if your
class doesn't have any.
Constructor vs Method in Java
The below table demonstrates the key difference
between Java Constructor and Java Methods.

Features Constructor Method

Constructors
Methods can
must have the
have any
same name as
valid name
Name the class name

Methods
have the
Constructors return type
do not return or void if
any type does not
return any
Return Type value.

Constructors
are called Methods are
automatically called
with new explicitly
Invocation keyword

Constructors Methods are


are used to used to
initialize perform
Purpose objects operations

Now let us come up with the syntax for the constructor


being invoked at Now let us come up with the syntax for
the constructor being invoked at the time of object or
instance creation.

class Geek
{
......
// A Constructor
Geek() {
}
.......
}
// We can create an object of the above class
// using the below statement. This statement
// calls above constructor.
Geek obj = new Geek();
or instance creation.

The first line of a constructor is a call


to super() or this(), (a call to a constructor of a super-
class or an overloaded constructor), if you don't type in
the call to super in your constructor the compiler will
provide you with a non-argument call to super at the
first line of your code, the super constructor must be
called to create an object:
Note: If you think your class is not a subclass it actually
is, every class in Java is the subclass of a
class object even if you don't say extends object in your
class definition.
Why Do We Need Constructors in Java
Constructors play a very important role, it ensures that
an object is properly initialized before use.
What happens when we don't use constructors:
Without constructors:
 Objects might have undefined or default values.
 Extra initialization methods would be required.
 Risk of improper object state
[Think of a Box. If we talk about a box class then it will
have some class variables (say length, breadth, and
height). But when it comes to creating its object (i.e Box
will now exist in the computer's memory), then can a
box be there with no value defined for its dimensions?
The answer is No.
So, constructors are used to assign values to the class
variables at the time of object creation, either explicitly
done by the programmer or by Java itself (default
constructor)].

When Java Constructor is Called?


Each time an object is created using a new() keyword, at
least one constructor (it could be the default
constructor) is invoked to assign initial values to
the data members of the same class. Rules for writing
constructors are as follows:
 The constructor of a class must have the same
name as the class name in which it resides.
 A constructor in Java cannot be abstract, final,
static, or Synchronized.
 Access modifiers can be used in constructor
declaration to control its access i.e which other
class can call the constructor.
So, we have learned constructors are used to initialize
the object's state. Like methods , a constructor also
contains a collection of statements (i.e. instructions)
that are executed at the time of object creation.

Types of Constructors in Java


Now is the correct time to discuss the types of the
constructor, so primarily there are three types of
constructors in Java are mentioned below:

 Default Constructor
 Parameterized Constructor
 Copy Constructor

1. Default Constructor in Java


 A constructor that has no parameters is known as
default constructor. A default constructor is
invisible. And if we write a constructor with no
arguments, the compiler does not create a default
constructor. Once you define a constructor (with or
without parameters), the compiler no longer
provides the default constructor. Defining a
parameterized constructor does not automatically
create a no-argument constructor, we must
explicitly define if needed. The default constructor
can be implicit or explicit.
 Implicit Default Constructor: If no constructor is
defined in a class, the Java compiler automatically
provides a default constructor. This constructor
doesn’t take any parameters and initializes the
object with default values, such as 0 for
numbers, null for objects.
 Explicit Default Constructor: If we define a
constructor that takes no parameters, it's called an
explicit default constructor. This constructor
replaces the one the compiler would normally
create automatically. Once you define any
constructor (with or without parameters), the
compiler no longer provides the default constructor
for you.
 Example: This program demonstrates the use of a
default constructor, which is automatically called
when an object is created.
// Java Program to demonstrate
// Default Constructor
import java.io.*;

// Driver class
class Geeks{

// Default Constructor
Geeks() {
System.out.println("Default constructor");

// Driver function
public static void main(String[] args)
{
Geeks hello = new Geeks();
}
}
Output: - Default constructor
(Note: Default constructor provides the default values
to the object like 0, null, false etc. depending on the
type.)

2. Parameterized Constructor in Java


A constructor that has parameters is known as
parameterized constructor. If we want to initialize fields
of the class with our own values, then use a
parameterized constructor.
Example: This program demonstrates the use of a
parameterized constructor to initialize an object's
attributes with specific values.

// Java Program for Parameterized Constructor


import java.io.*;

class Geeks {

// data members of the class


String name;
int id;

Geeks(String name, int id) {


this.name = name;
this.id = id;
}
}

class GFG
{
public static void main(String[] args)
{
// This would invoke the parameterized constructor
Geeks geek1 = new Geeks("Sweta", 68);
System.out.println("GeekName: " + geek1.name
+ " and GeekId: " + geek1.id);
}
}

Output
GeekName: Sweta and GeekId: 68

(Remember: Does constructor return any value?


There are no "return value" statements in the
constructor, but the constructor returns the current
class instance. We can write 'return' inside a
constructor).

3. Copy Constructor in Java


Unlike other constructors copy constructor is passed
with another object which copies the data available
from the passed object to the newly created object.
Note: Java does not provide a built-in copy constructor
like C++. We can create our own by writing a
constructor that takes an object of the same class as a
parameter and copies its fields.
Example: This example, demonstrates how a copy
constructor can be used to create a new object by
copying the values of another object's attributes.
// Java Program for Copy Constructor
import java.io.*;

class Geeks {

// data members of the class


String name;
int id;

// Parameterized Constructor
Geeks(String name, int id)
{
this.name = name;
this.id = id;
}

// Copy Constructor
Geeks(Geeks obj2)
{
this.name = obj2.name;
this.id = obj2.id;
}
}

class GFG {
public static void main(String[] args)
{
// This would invoke the parameterized constructor
System.out.println("First Object");
Geeks geek1 = new Geeks("Sweta", 68);
System.out.println("GeekName: " + geek1.name
+ " and GeekId: " + geek1.id);

System.out.println();

// This would invoke the copy constructor


Geeks geek2 = new Geeks(geek1);
System.out.println(
"Copy Constructor used Second Object");
System.out.println("GeekName: " + geek2.name
+ " and GeekId: " + geek2.id);
}
}

Constructor Overloading
This is a key concept in OOPs related to constructors
is constructor overloading. This allows us to create
multiple constructors in the same class with different
parameter lists.
Example: This example, demonstrates constructor
overloading, where multiple constructors perform the
same task (initializing an object) with different types or
numbers of arguments.
// Java Program to illustrate constructor overloading
// using same task (addition operation) for different
// types of arguments
import java.io.*;

class Geeks {

// constructor with one argument


Geeks(String name)
{
System.out.println("Constructor with one "
+ "argument - String: " + name);
}

// constructor with two arguments


Geeks(String name, int age)
{

System.out.println(
"Constructor with two arguments: "
+ " String and Integer: " + name + " " + age);
}

// Constructor with one argument but with different


// type than previous
Geeks(long id)
{
System.out.println(
"Constructor with one argument: "
+ "Long: " + id);
}
}

class GFG {
public static void main(String[] args)
{
// Creating the objects of the class named 'Geek'
// by passing different arguments

// Invoke the constructor with one argument of


// type 'String'.
Geeks geek2 = new Geeks("Sweta");

// Invoke the constructor with two arguments


Geeks geek3 = new Geeks("Amiya", 28);

// Invoke the constructor with one argument of


// type 'Long'.
Geeks geek4 = new Geeks(325614567);
}
}
[Output: - Constructor with one argument - String:
Sweta
Constructor with two arguments: String and Integer:
Amiya 28
Constructor with one argument: Long: 325614567]
Common Mistakes to Avoid
Some common mistakes to avoid when working with
constructors in Java are listed below:
 Forgetting super() in Child Classes: Always call the
parent constructor (super()) if the parent class has
no default constructor, or it will lead to compilation
errors.
 Excessive Work in Constructors: Keep constructors
simple and focused on initialization, avoiding heavy
logic that slows down object creation.
 Not Handling Null Checks: Always validate
parameters to avoid NullPointerException when
constructing objects.

Properties of Constructors in Java


 A constructors in Java is a special method that is
used to initialize objects. The constructor is called
when an object of a class is created. It can be used
to set initial values for object attributes. In Java, a
constructor is a block of codes similar to the
method. It is called when an instance of the class is
created. At the time of calling the constructor,
memory for the object is allocated in the memory. It
is a special type of method that is used to initialize
the object. Every time an object is created using the
new() keyword, at least one constructor is called.
There are certain properties of constructors in java.
 Properties of Constructors
 1. Constructor Should not have any return type
 If we declare a return type for a constructor, JVM
considers it a normal method. The basic aim of the
constructor is to assign a value to the object. It
implicitly returns the address of the object.
 2. The access specifier of the constructor can be
private, default, protected, public
 If the access specifier of the constructor is private,
then an object of the corresponding class can be
created in the context of the same class but not in
the context of another class. If the access specifier
of the constructor is the default, then an object of
the corresponding class can be created in the
context of classes that are in the same package.
Similarly, if the constructor is declared as
protected, the object of the corresponding class can
be created in the context of classes of the same
package as well as inherited classes of other
packages. And, if the constructor is declared as
public, the object of the corresponding class can be
created in the context of any class.
 3. A constructor cannot be declared as final and
synchronized
 The main purpose of using the final keyword is to
restrict the overriding of a method (basically
overriding is done in the context of inherited
classes). As there is no concept of overriding
constructors, declaring the final is useless.
Using synchronized for a constructor raises a
syntax error. Because only the thread that creates
an object should have access to it while it is being
constructed.

class GFG {
 final GFG() {}
 public static void main(String[] args)
 {
 System.out.println("GFG!");
 }
 }

 Output: -
(./GFG.java:2: error: modifier final not allowed here
 final GFG() {})
 1 error
 This snippet of code results in a compile-time error.
 4. A constructor can invoke another constructor of
the same class
 By using this() as the first statement in a constructor we
can call another constructor of the same class. If we want to
invoke a parameterized constructor, we can pass a
parameter list as an argument: this(parameter list). super()
is used to invoke the constructor of the superclass. JVM
implicitly declares the first statement as super() for the no-
argument constructor, and this() for the default constructor.

Topic 4:- Java Multithreading


Threads are the backbone of multithreading. We are living in the
real world which in itself is caught on the web surrounded by lots
of applications. With the advancement in technologies, we cannot
achieve the speed required to run them simultaneously unless we
introduce the concept of multi-tasking efficiently. It is achieved by
the concept of thread.

Real-life Example of Java Multithreading


(Suppose you are using two tasks at a time on the computer, be it using
Microsoft Word and listening to music. These two tasks are called
processes . So you start typing in Word and at the same time start music
app, this is called multitasking . Now you committed a mistake in a Word
and spell check shows exception, this means Word is a process that is
broken down into sub-processes. Now if a machine is dual-core then one
process or task is been handled by one core and music is been handled by
another core).

In the above example, we come across both multiprocessing and


multithreading. These are somehow indirectly used to achieve
multitasking. In this way the mechanism of dividing the tasks is called
multithreading in which every process or task is called by a thread where
a thread is responsible for when to execute, when to stop and how long to
be in a waiting state. Hence, a thread is the smallest unit of processing
whereas multitasking is a process of executing multiple tasks at a time.

Multitasking is being achieved in two ways:

1. Multiprocessing: Process-based multitasking is a heavyweight


process and occupies different address spaces in memory. Hence,
while switching from one process to another, it will require some
time be it very small, causing a lag because of switching. This
happens as registers will be loaded in memory maps and the list will
be updated.

2. Multithreading: Thread-based multitasking is a lightweight


process and occupies the same address space. Hence, while
switching cost of communication will be very less.

Below is the Lifecycle of a Thread been illustrated

1. New: When a thread is just created.

2. Runnable: When a start () method is called over thread processed


by the thread scheduler.

 Case A: Can be a running thread

 Case B: Can not be a running thread

3. Running: When it hits case 1 means the scheduler has selected it


to be run the thread from runnable state to run state.

4. Blocked: When it hits case 2 meaning the scheduler has selected


not to allow a thread to change state from runnable to run.

5. Terminated: When the run() method exits or stop() method gets


called.
If we do incorporate threads in operating systems one can perceive that
the process scheduling algorithms in operating systems are strongly deep-
down working on the same concept incorporating thread in Gantt
charts . A few of the most popular are listed below which wraps up all of
them and are used practically in software development.

 First In First Out

 Last In First Out

 Round Robin Scheduling

Now Imagine the concept of Deadlock in operating systems with


threads - how the switching is getting computed over internally if one
only has an overview of them.

So far we have understood multithreading and thread conceptually, so we


can conclude advantages of multithreading before moving to any
other concept or getting to programs in multithreading.
 The user is not blocked as threads are independent even if there is
an issue with one thread then only the corresponding process will be
stopped rest all the operations will be computed successfully.

 Saves time as too many operations are carried over at the same
time causing work to get finished as if threads are not used the only
one process will be handled by the processor.

 Threads are independent though sharing the same address space.

So, we have touched all main concepts of multithreading but the question
striving in the head is left. why do we need it, where to use it and how?
Now, we will discuss all three scenarios why multithreading is needed and
where it is implemented via the help of programs in which we will be
further learning more about threads and their methods. We need
multithreading in four scenarios as listed.

 Thread Class

 Mobile applications

o Asynchronous thread

 Web applications

 Game Development

Note: By default, we only have one main thread which is responsible for
main thread exception as you have encountered even without having any
prior knowledge of multithreading

Two Ways to Implement Multithreading

 Using Thread Class

 Using Runnable Interface

Method 1: Using Thread Class


Java provides Thread class to achieve programming invoking
threads thereby some major methods of thread class are shown
below in the tabular format with which we deal frequently along
the action performed by them.
Methods Action Performed

isDaemon() It checks whether the current thread is daemon or not


Methods Action Performed

start() It starts the execution of the thread

It does the executable operations statements in the


run()
body of this method over a thread

It is a static method that puts the thread to sleep for a


sleep()
certain time been passed as an argument to it

wait() It sets the thread back in waiting state.

It gives out a notification to one thread that is in


notify()
waiting state

It gives out a notification to all the thread in the


notifyAll()
waiting state

setDaemon
It set the current thread as Daemon thread
()

stop() It is used to stop the execution of the thread

resume() It is used to resume the suspended thread.

Pre-requisites: Basic syntax and methods to deal with threads


Now let us come up with how to set the name of the thread. By
default, threads are named thread-0, thread-1, and so on. But
there is also a method that is often used as setName() method.
Also corresponding to it there is a method getName() which
returns the name of the thread be it default or settled already by
using setName() method. The syntax is as follows:
Syntax:
(a) Returning the name of the thread
public String getName() ;
(b) Changing the name of the thread
public void setName(String name);
Taking a step further, let us dive into the implementation part to
understand more concepts about multithreading. So, there are
basically two ways of implementing multithreading:
Illustration: Consider if one has to multiply all elements by 2
and there are 500 elements in an array.

// Case 1
// Java Program to illustrate Creation and execution of
// thread via start() and run() method in Single inheritance

// Class 1

// Helper thread Class extending main Thread Class

class MyThread1 extends Thread {

// Method inside MyThread2


// run() method which is called as

// soon as thread is started

public void run()

// Print statement when the thread is called

System.out.println("Thread1 is running");

// Class 2

// Main thread Class extending main Thread Class

class MyThread2 extends Thread {

// Method inside MyThread2

// run() method which is called

// as soon as thread is started

public void run()

// run() method which is called as soon as thread is

// started

// Print statement when the thread is called

System.out.println("Thread2 is running");

// Class 3
// Main Class

class GFG {

// Main method

public static void main(String[] args)

// Creating a thread object of our thread class

MyThread1 obj1 = new MyThread1();

MyThread2 obj2 = new MyThread2();

// Getting the threads to the run state

// This thread will transcend from runnable to run

// as start() method will look for run() and execute

// it

obj1.start();

// This thread will also transcend from runnable to

// run as start() method will look for run() and

// execute it

obj2.start();

// Case 2
// Java Program to illustrate Difference between Runnable
// & Non-runnable Threads And Single Inheritance
// Class 1

// Helper thread Class extending main Thread Class

class MyThread1 extends Thread {

// Method inside MyThread2

// run() method which is called as soon as thread is

// started

public void run() {

// Print statement when the thread is called

System.out.println("Thread 1 is running");

// Class 2

// Main thread Class extending main Thread Class

class MyThread2 extends Thread {

// Method

public void show() {

// Print statement when thread is called

System.out.println("Thread 2");

// Class 3

// Main Class

class GFG {
// Main method

public static void main(String[] args) {

// Creating a thread object of our thread class

MyThread1 obj1 = new MyThread1();

MyThread2 obj2 = new MyThread2();

// Getting the threads to the run state

// This thread will transcend from runnable to run

// as start() method will look for run() and execute

// it

obj1.start();

// This thread will now look for run() method which is absent

// Thread is simply created not runnable

obj2.start();

// Java Program to illustrate difference between


// start() method thread vs show() method

// Class 1

// Helper thread Class extending main Thread Class

class MyThread1 extends Thread {

// Method inside MyThread2


// run() method which is called as soon as thread is

// started

public void run() {

// Print statement when the thread is called

System.out.println("Thread 1 is running");

// Class 2

// Main thread Class extending main Thread Class

class MyThread2 extends Thread {

// Method

public void show() {

// Print statement when thread is called

System.out.println("Thread 2");

// Class 3

// Main Class

class GFG {

// Main method

public static void main(String[] args) {

// Creating a thread object of our thread class


MyThread1 obj1 = new MyThread1();

MyThread2 obj2 = new MyThread2();

// Getting the threads to the run state

// This thread will transcend from runnable to run

// as start() method will look for run() and execute

// it

obj1.start();

// This thread is simply a function call as

// no start() method is executed so here only

// thread is created only followed by call

obj2.show();

Output:
Case 1:
Thread1 is running
Thread2 is running

Here we have created our two thread classes for each thread. In the main
method , we are simply creating objects of these thread classes where
objects are now threads. So in main , we call thread using start() method
over both the threads. Now start() method starts the thread and lookup for
their run() method to run. Here both of our thread classes were having
run() methods, so both threads are put to run state from runnable by the
scheduler, and output on the console is justified.

Case 2:
Thread 1 is running
Here we have created our two thread classes for each thread. In the main
method, we are simply creating objects of these thread classes where
objects are now threads. So in main, we call thread using start() method
over both the threads. Now start() method starts the thread and lookup
their run() method to run. Here only class 1 is having the run() method to
make the thread transcend from runnable to run state to execute whereas
thread 2 is only created but not put to run state by the scheduler as its
corresponding run() method was missing. Hence, only thread 1 is called
rest thread 2 is created only and is in the runnable state later blocked by
scheduler because the corresponding run() method was missing.

Case 3:
Thread 2
Thread 1 is running

Method 2: Using Runnable Interface


Another way to achieve multithreading in java is via the
Runnable interface. Here as we have seen in the above
example in way 1 where Thread class is extended. Here
Runnable interface being a functional interface has its own
run() method. Here classes are implemented to the Runnable
interface. Later on, in the main() method, Runnable reference is
created for the classes that are implemented in order to make
bondage with Thread class to run our own corresponding run()
methods.
Further, while creating an object of Thread class we will pass
these references in Thread class as its constructor allows only
one runnable object, which is passed as a parameter while
creating Thread class object in a main() method. Now lastly just
like what we did in Thread class, start() method is invoked over
the runnable object who are now already linked with Thread
class objects, so the execution begins for our run() methods in
case of Runnable interface. It is shown in the program below as
follows:

// Java Program to illustrate Runnable Interface in threads


// as multiple inheritance is not allowed
// Importing basic packages

import java.io.*;

import java.util.*;

// Class 1

// Helper class implementing Runnable interface

class MyThread1 implements Runnable {

// run() method inside this class

public void run()

// Iterating to get more execution of threads

for (int i = 0; i < 5; i++) {

// Print statement whenever run() method

// of this class is called

System.out.println("Thread1");

// Getting sleep method in try block to

// check for any exceptions

try {

// Making the thread pause for a certain

// time using sleep() method

Thread.sleep(1000);

// Catch block to handle the exceptions

catch (Exception e) {
}

// Class 2

// Helper class implementing Runnable interface

class MyThread2 implements Runnable {

// run() method inside this class

public void run()

for (int i = 0; i < 5; i++) {

// Print statement whenever run() method

// of this class is called

System.out.println("Thread2");

// Getting sleep method in try block to

// check for any exceptions

try {

// Making the thread pause for a certain

// time

// using sleep() method

Thread.sleep(1000);

// Catch block to handle the exceptions


catch (Exception e) {

// Class 3

// Main class

public class GFG {

// Main driver method

public static void main(String[] args)

// Creating reference of Runnable to

// our classes above in main() method

Runnable obj1 = new MyThread1();

Runnable obj2 = new MyThread2();

// Creating reference of thread class

// by passing object of Runnable in constructor of

// Thread class

Thread t1 = new Thread(obj1);

Thread t2 = new Thread(obj2);

// Starting the execution of our own run() method

// in the classes above

t1.start();

t2.start();
}

Output
Thread2
Thread1
Thread2
Thread1
Thread2
Thread1
Thread2
Thread1
Thread2
Thread1
Points to remember: Whenever you wanted to create
threads, there are only two ways:
1. Extending the class
2. Implementing the interface which is runnable
Make sure to create an object of threads in which you have to
pass the object of runnable.

Special Methods of Threads


Now let us discuss various methods that are there for threads.
Here we will be discussing major methods in order to have a
practical understanding of threads and multithreading which
are sequential as follows:
1. start() Method
2. suspend() Method
3. stop() Method
4. wait() Method
5. notify() Method
6. notifyAll() Method
7. sleep() Method
 Output Without sleep() Method
 Output with sleep() method in Serial Execution
Processes (Blocking methods approach)
 Output with sleep() method in Parallel Execution
Processes (Unblocking methods approach)
8. join() Method
(Note: For naive users in multithreading where threads are
backbone go through Program 4 to get very basics of threads,
how to start, make it hold, or terminate then only toggle to
program 1 and rest as follows.)

// Example 1
// Java Program to illustrate Output Without sleep() Method

// Class 1
// Helper Class 1
class Shot extends Thread {

// Method 1
public void show() {

// Iterating to print more number of times


for (int i = 0; i < 5; i++) {
// Print statement whenever method
// of this class is called
System.out.println("Shot");

}
}
}

// Class 2
// Helper Class 2
class Miss extends Thread {

// Method 2
public void show() {

// Iterating to print more number of times


for (int i = 0; i < 5; i++) {

// Print statement whenever method


// of this class is called
System.out.println("Miss");

}
}

}
// Class 3
// Main class
public class GFG {

// Method 3
// Main method
public static void main(String[] args) {

// Creating objects in the main() method


// of class 1 and class 2
Shot obj1 = new Shot();
Miss obj2 = new Miss();

// Calling methods of the class 1 and class 2


obj1.show();
obj2.show();

}
}

[Output:
Case 1:
Shot
Shot
Shot
Shot
Shot
Miss
Miss
Miss
Miss
Miss]

// Example 2
// Java Program to illustrate Output Using sleep() Method
// in Serial Execution

// Class 1
// Helper Class 1
class Shot extends Thread {

// Method 1
// public void show() {
public void show()
{

// Iterating to print more number of times


for (int i = 0; i < 5; i++) {

// Print statement
System.out.println("Shot");

// Making thread to sleep using sleep() method


// Try-catch block for exceptions
try {
Thread.sleep(1000);
}
catch (Exception e) {
}
}
}
}

// Class 2
// Helper Class 2 Hello
class Miss extends Thread {

// Method 2
// public void show() {
public void show()
{

// Iterating to print more number of times


for (int i = 0; i < 5; i++) {

// Print statement
System.out.println("Miss");

// Making thread to sleep using sleep() method


// Try-catch block for exceptions
try {
Thread.sleep(1000);
}
catch (Exception e) {
}
}
}
}

// Class 3
// Main class
public class GFG {

// Method 3
// Main method
public static void main(String[] args)
{

// Creating objects in the main() method


Shot obj1 = new Shot();
Miss obj2 = new Miss();

// Starting the thread objects


obj1.start();
obj2.start();

// Calling methods of class 1 and class 2


obj1.show();
obj2.show();
}
}
Output:- [Case 2
Shot
Shot
Shot
Shot
Shot
Miss
Miss
Miss
Miss
Miss]

// Example 3
// Java Program to illustrate Output Using sleep() Method
// in Parallel Execution

// Class 1
// Helper Class 1
class Shot extends Thread {

// Method 1
// public void show() {
public void run()
{

// Iterating to print more number of times


for (int i = 0; i < 5; i++) {

// Print statement
System.out.println("Shot");

// Making thread to sleep using sleep() method

// Try catch block for exceptions


try {
Thread.sleep(1000);
}
catch (Exception e) {
}
}
}
}

// Class 2
// Helper Class 2 Hello
class Miss extends Thread {

// Method 2
// public void show() {
public void run()
{

// Iterating to print more number of times


for (int i = 0; i < 5; i++) {

// Print statement
System.out.println("Miss");

// Making thread to sleep using sleep() method

// Try catch block for exceptions


try {
Thread.sleep(1000);
}
catch (Exception e) {
}
}
}
}

// Class 3
// Main class
public class GFG {
// Method 3
// Main method
public static void main(String[] args)
{

// Creating objects in the main() method


Shot obj1 = new Shot();
Miss obj2 = new Miss();

// Starting the thread objects


// using start() method

// start() method calls the run() method


// automatically
obj1.start();
obj2.start();
}
}

[Case 3: output
Shot
Miss
Shot
Miss
Shot
Miss
Shot
Miss
Shot
Miss]

Daemon thread :-
Basically a service provider thread that provides services to the
user thread. The scope for this thread start() or be it
terminate() is completely dependent on the user's thread as it
supports in the backend for user threads being getting run. As
soon as the user thread is terminated daemon thread is also
terminated at the same time as being the service provider
thread.
Hence, the characteristics of the Daemon thread are as follows:
 It is only the service provider thread not responsible for
interpretation in user threads.
 So, it is a low-priority thread.
 It is a dependent thread as it has no existence on its own.
 JVM terminates the thread as soon as user threads are
terminated and come back into play as the user's thread
starts.
 Yes, you guess the most popular example is garbage
collector in java. Some other examples do include
'finalizer'.
Exceptions: IllegalArgumentException as return type while
setting a Daemon thread is boolean so do apply carefully.
Note: To get rid of the exception users thread should only start
after setting it to daemon thread. The other way of starting
prior setting it to daemon will not work as it will pop-out
IllegalArgumentException
As discussed above in the Thread class two most widely used
method is as follows:
Daemon Thread
Methods Action Performed

It checks whether the current thread is a


isDaemon()
daemon thread or not

It set the thread to be marked as daemon


setDaemon()
thread

// Java Program to show Working of Daemon Thread


// with user’s threads

import java.io.*;
// Importing Thread class from java.util package
import java.util.*;

// Class 1
// Helper Class extending Thread class
class CheckingMyDaemonThread extends Thread {

// Method
// run() method which is invoked as soon as
// thread start via start()
public void run()
{

// Checking whether the thread is daemon thread or


// not
if (Thread.currentThread().isDaemon()) {

// Print statement when Daemon thread is called


System.out.println(
"I am daemon thread and I am working");
}

else {

// Print statement whenever users thread is


// called
System.out.println(
"I am user thread and I am working");
}
}
}

// Class 2
// Main Class
class GFG {

// Main driver method


public static void main(String[] args)
{
// Creating threads in the main body
CheckingMyDaemonThread t1
= new CheckingMyDaemonThread();
CheckingMyDaemonThread t2
= new CheckingMyDaemonThread();
CheckingMyDaemonThread t3
= new CheckingMyDaemonThread();

// Setting thread named 't2' as our Daemon thread


t2.setDaemon(true);

// Starting all 3 threads using start() method


t1.start();
t2.start();
t3.start();

// Now start() will automatically


// invoke run() method
}
}

// Java Program to show Working of Daemon Thread


// with users threads where start() is invoked
// prior before setting thread to Daemon

import java.io.*;
// Basically we are importing Thread class
// from java.util package
import java.util.*;

// Class 1
// Helper Class extending Thread class
class CheckingMyDaemonThread extends Thread {

// Method
// run() method which is invoked as soon as
// thread start via start()
public void run()
{

// Checking whether the thread is daemon thread or


// not
if (Thread.currentThread().isDaemon()) {

// Print statement when Daemon thread is called


System.out.println(
"I am daemon thread and I am working");
}

else {

// Print statement whenever users thread is


// called
System.out.println(
"I am user thread and I am working");
}
}
}

// Class 2
// Main Class
class GFG {

// Method
// Main driver method
public static void main(String[] args)
{

// Creating threads objects of above class


// in the main body
CheckingMyDaemonThread t1
= new CheckingMyDaemonThread();
CheckingMyDaemonThread t2
= new CheckingMyDaemonThread();
CheckingMyDaemonThread t3
= new CheckingMyDaemonThread();

// Starting all 3 threads using start() method


t1.start();
t2.start();
t3.start();

// Now start() will automatically invoke run()


// method

// Now at last setting already running thread 't2'


// as our Daemon thread will throw an exception
t2.setDaemon(true);
}
}

Another way to achieve the same is through Thread Group in


which as the name suggests multiple threads are treated as a
single object and later on all the operations are carried on over
this object itself aiding in providing a substitute for the Thread
Pool.

Note:
While implementing ThreadGroup do note that ThreadGroup is
a part of ' java.lang.ThreadGroup' class not a part of Thread
class in java so do peek out constructors and methods of
ThreadGroup class before moving ahead keeping a check over
deprecated methods in his class so as not to face any
ambiguity further.
Here main() method in itself is a thread because of which you
do see Exception in main() while running the program because
of which system.main thread exception is thrown
sometimes while execution of the program.

Synchronization
It is the mechanism that bounds the access of multiple threads
to share a common resource hence is suggested to be useful
where only one thread at a time is granted the access to run
over.
It is implemented in the program by using ' synchronized '
keyword.
Now let's finally discuss some advantages and disadvantages of
synchronization before implementing the same. For more depth
in synchronization, one can also learn object level
lock and class level lock and do notice the differences between
two to get a fair understanding of the same before
implementing the same.
Why Synchronization is Required?
1. Data inconsistency issues are the primary issue where
multiple threads are accessing the common memory
which sometimes results in faults in order to avoid that a
thread is overlooked by another thread if it fails out.
2. Data integrity
3. To work with a common shared resource which is very
essential in the real world such as in banking systems.
Note: Do not go for synchronized keyword unless it is most
needed, remember this as there is no priority setup for threads,
so if the main thread runs before or after other thread the
output of the program would be different.
The biggest advantage of synchronization is the increase in
idiotic resistance as one cannot choose arbitrarily an object to
lock on as a result string literal cannot be locked or be the
content. Hence, these bad practices are not possible to perform
on synchronized method block.
As we have seen humongous advantages and get to know how
important it is but there comes disadvantage with it.
Disadvantage: Performance issues will arise as during the
execution of one thread all the other threads are put to a
blocking state and do note they are not in waiting state . This
causes a performance drop if the time taken for one thread is
too long.
multi-tasking
As perceived from the image in which we are getting that count
variable being shared resource is updating randomly. It is
because of multithreading for which this concept becomes a
necessity.
 Case 1: If ' main thread' executes first then count will be
incremented followed by a ' thread T' in synchronization
 Case 2: If ' thread T ' executes first then count will not
increment followed by ' main thread ' in synchronization

// Java Program to illustrate Output Conflict between


// Execution of Main thread vs Thread created

// count = 1 if main thread executes first


// count = 1 if created thread executes first

// Importing basic required libraries


import java.io.*;
import java.util.*;

// Class 1
// Helper Class extending Thread class
class MyThread extends Thread {

// Declaring and initializing initial count to zero


int count = 0;

// Method 1
// To increment the count above by unity
void increment() { count++; }

// Method 2
// run method for thread invoked after
// created thread has started
public void run()
{

// Call method in this method


increment();

// Print and display the count


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

// Class 2
public class GFG {

// Main driver method


public static void main(String[] args)
{
// Creating the above our Thread class object
// in the main() method
MyThread t1 = new MyThread();
// start() method to start execution of created
// thread that will look for run() method
t1.start();
}
}
Output Explanation:

Here the count is incremented to 1 meaning ' main thread ' has
executed prior to ' created thread '. We have run it many times
and compiled and run once again wherein all cases here main
thread is executing faster than created thread but do
remember output may vary. Our created thread can execute
prior to ' main thread ' leading to 'Count : 0' as an output on
the console.
Now another topic that arises in dealing with synchronization in
threads is Thread safety in java synchronization is the new
concept that arises out in synchronization so let us discuss it
considering
 A real-life scenario followed by
 Pictorial representation as an illustration followed by
 Technically description and implementation

Real-life Scenario
Suppose a person is withdrawing some amount of money from
the bank and at the same time the ATM card registered with
the same account number is carrying on withdrawal operation
by some other user. Now suppose if withdrawing some amount
of money from net banking makes funds in account lesser than
the amount which needed to be withdrawal or the other way.
This makes the bank unsafe as more funds are debited from
the account than was actually present in the account making
the bank very unsafe and is not seen in daily life. So what
banks do is that they only let one transaction at a time. Once it
is over then another is permitted.
Illustration:

Interpreting the same technology as there are two different


processes going on which object in case of parallel execution is
over headed by threads. Now possessing such traits over
threads such that they should look after for before execution or
in simpler words making them synchronized. This mechanism is
referred to as Thread Safe with the use of the keyword
' synchronized ' before the common shared method/function to
be performed parallel.
Technical Description:
As we know Java has a feature, Multithreading , which is a
process of running multiple threads simultaneously. When
multiple threads are working on the same data, and the value
of our data is changing, that scenario is not thread-safe, and we
will get inconsistent results. When a thread is already working
on an object and preventing another thread from working on
the same object, this process is called Thread-Safety. Now there
are several ways to achieve thread-safety in our program
namely as follows:
1. Using Synchronization
2. Using Volatile Keyword
3. Using Atomic Variable
4. Using Final Keyword
Conclusion: Hence, if we are accessing one thread at a time
then we can say thread-safe program and if multiple threads
are getting accessed then the program is said to be thread-
unsafe that is one resource at a time can not be shared by
multiple threads at a time.
Implementation:
 Java Program to illustrate Incomplete Thread Iterations
returning counter value to Zero irrespective of iteration
bound
 Java Program to Illustrate Complete Thread Iterations
illustrating join() Method
 Java Program to Illustrate thread-unsafe or non-
synchronizing programs as of incomplete iterations
 Java Program to Illustrate Thread Safe And synchronized
Programs as of Complete iterations using
' synchronized ' Keyword.

Example 1
// Java Program to illustrate Incomplete Thread Iterations
// Returning Counter Value to Zero
// irrespective of iteration bound

// Importing input output classes


import java.io.*;

// Class 1
// Helper Class
class TickTock {
// Member variable of this class
int count;

// Method of this Class


// It increments counter value whenever called
public void increment()
{
// Increment count by unity
// i.e count = count + 1;
count++;
}
//
}

// Class 2
// Synchronization demo class
// Main Class
class GFG {

// Main driver method


public static void main(String[] args) throws Exception
{

// Creating an object of class TickTock in main()


TickTock tt = new TickTock();
// Now, creating an thread object
// using Runnable interface
Thread t1 = new Thread(new Runnable() {
// Method
// To begin the execution of thread
public void run()
{

// Expression
for (int i = 0; i < 10000; i++) {

// Calling object of above class


// in main() method
tt.increment();
}
}
});

// Making above thread created to start


// via start() method which automatically
// calls run() method in Ticktock class
t1.start();

// Print and display the count


System.out.println("Count : " + tt.count);
}
}
Output:
Case 1
Count: 0

// Example 2
// Java Program to Illustrate Complete Thread Iterations
// illustrating join() Method

// Importing input output classes


import java.io.*;

// Class 1
// Helper Class
class TickTock {

// Member variable of this class


int count;

// Method of this Class


public void increment()
{

// Increment count by unity


// whenever this function is called
count++;
}
}

// Class 2
// Synchronization demo class
// Main Class
class GFG {

// Main driver method


public static void main(String[] args) throws Exception
{

// Creating an object of class TickTock in main()


TickTock tt = new TickTock();

// Now, creating an thread object


// using Runnable interface
Thread t1 = new Thread(new Runnable() {
// Method
// To begin the execution of thread
public void run()
{

// Expression
for (int i = 0; i < 1000; i++) {
// Calling object of above class
// in main() method
tt.increment();
}
}
});

// Making above thread created to start


// via start() method which automatically
// calls run() method
t1.start();

// Now we are making main() thread to wait so


// that thread t1 completes it job
// using join() method
t1.join();

// Print and display the count value


System.out.println("Count : " + tt.count);
}
}
Output: -
Case 2
Count: 10000
// Example 3
// Java Program to Illustrate Thread Unsafe Or
// Non-synchronizing Programs as of Incomplete Iterations
// Without using 'synchronized' program

// Importing input output classes


import java.io.*;

// Class 1
// Helper Class
class TickTock {

// Member variable of this class


int count;

// Method of this Class


public void increment()
{

// Increment count by unity


count++;
}
}

// Class 2
// Synchronization demo class
// Main Class
class GFG {

// Main driver method


public static void main(String[] args) throws Exception
{

// Creating an object of class TickTock in main()


TickTock tt = new TickTock();

// Now, creating an thread object


// using Runnable interface
Thread t1 = new Thread(new Runnable() {
// Method
// To begin the execution of thread
public void run()
{

// Expression
for (int i = 0; i < 100000; i++) {

// Calling object of above class


// in main() method
tt.increment();
}
}
});

// Now creating another thread and lets check


// how they increment count value running parallelly
// Thread 2
Thread t2 = new Thread(new Runnable() {
// Method
// To begin the execution of thread
public void run()
{

// Expression
for (int i = 0; i < 100000; i++) {

// Calling object of above class


// in main() method
tt.increment();
}
}
});

// Making above thread created to start


// via start() method which automatically
// calls run() method
t1.start();
t2.start();
// Now we are making main() thread to wait so
// that thread t1 completes it job
t1.join();
t2.join();

// Print and display the count


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

Output: -
Case 3
Count: 151138

// Example 4
// Java Program to Illustrate Thread Safe And
// Synchronized Programs as of Complete Iterations
// using 'synchronized' Keyword

// Importing input output classes


import java.io.*;

// Class 1
// helper Class
class TickTock {
// Member variable of this class
int count;

// Method of this Class


public synchronized void increment()
{

// Increment count by unity


count++;
}
//
}

// Class 2
// Synchronization demo class
// Main Class
class GFG {

// Main driver method


public static void main(String[] args) throws Exception
{

// Creating an object of class TickTock in main()


TickTock tt = new TickTock();
// Now, creating an thread object
// using Runnable interface
Thread t1 = new Thread(new Runnable() {
// Method
// To begin the execution of thread
public void run()
{

// Expression
for (int i = 0; i < 100000; i++) {

// Calling object of above class


// in main() method
tt.increment();
}
}
});

// Thread 2
Thread t2 = new Thread(new Runnable() {
// Method
// To begin the execution of thread
public void run()
{

// Expression
for (int i = 0; i < 100000; i++) {

// Calling object of above class


// in main() method
tt.increment();
}
}
});

// Making above thread created to start


// via start() method which automatically
// calls run() method
t1.start();
t2.start();

// Now we are making main() thread to wait so


// that thread t1 completes it job
t1.join();
t2.join();

// Print and display the count


System.out.println("Count : " + tt.count);
}
}
Output: - Case 4
Count: 200000
Output Explanation:
In case 1 we can see that count is zero as initialized. Now we
have two threads main thread and the thread t1. So, there are
two threads so now what happens sometimes instance is
shared among both of the threads.
In case 1 both are accessing the count variable where we are
directly trying to access thread via thread t1. count which will
throw out 0 always as we need to call it with the help of object
to perform the execution.
Now we have understood the working of synchronization is a
thread that is nothing but referred to as a term Concurrency
in java which in layman language is executing multiple
tasks. Let us depict concurrency in threads with the help of a
pictorial illustration.

Consider the task of multiplying an array of elements by a


multiplier of 2. Now if we start multiplying every element
randomly wise it will take a serious amount of time as every
time the element will be searched over and computer. By far
we have studied multithreading above in which we have
concluded to a single line that thread is the backbone of
multithreading.
So, incorporating threads in the above situation as the machine
is quad-core we here take 4 threads for every core where we
divide the above computing sample set to (1/4) th resulting out
in 4x faster computing. If in the above scenario it had taken 4
seconds then now it will take 1 second only. This mechanism of
parallel running threads in order to achieve faster and lag-free
computations is known as concurrency.
Note: Go for multithreading always for concurrent execution
and if not using this concept go for sequential execution
despite having bigger chunks of code as safety to our code is
the primary issue.
(Conclusion
Understanding Java Multithreading is important for creating fast
and efficient applications. By using multithreading, you can run
multiple tasks at the same time, making your programs more
responsive and powerful. Key ideas like thread synchronization,
concurrent collections, and the executor framework help
manage multiple threads safely. In the above article we have
described all details about Java multithreading.)

Synchronization in Java:-
In multithreading, synchronization is important to make sure
multiple threads safely work on shared resources. Without
synchronization, data can become inconsistent or corrupted if
multiple threads access and modify shared variables at the
same time. In Java, it is a mechanism that ensures that only
one thread can access a resource at any given time. This
process helps prevent issues such as data inconsistency
and race conditions when multiple threads interact with shared
resources.
Example: Below is a Java Program to demonstrate
synchronization.
// Java Program to demonstrate synchronization in Java
class Counter {
private int c = 0; // Shared variable

// Synchronized method to increment counter


public synchronized void inc() {
c++;
}

// Synchronized method to get counter value


public synchronized int get() {
return c;
}
}

public class Geeks {


public static void main(String[] args) {
Counter cnt = new Counter(); // Shared resource

// Thread 1 to increment counter


Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
cnt.inc();
}
});

// Thread 2 to increment counter


Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
cnt.inc();
}
});

// Start both threads


t1.start();
t2.start();

// Wait for threads to finish


try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}

// Print final counter value


System.out.println("Counter: " + cnt.get());
}
}

Output
Counter: 2000
[Explanation: Two threads, t1 and t2, increment the shared
counter variable concurrently. The inc() and get() methods are
synchronized, meaning only one thread can execute these
methods at a time, preventing race conditions. The program
ensures that the final value of the counter is consistent and
correctly updated by both threads.]

[Need of Synchronization:
When multiple threads share resources, synchronization make
sure that only one thread accesses the resource at a time. This
prevents problem like data getting mixed up or broken because
of multiple threads changing it together.]

Synchronized Blocks in Java


Java provides a way to create threads and synchronise their
tasks using synchronized blocks.
A synchronized block in Java is synchronized on some object.
Synchronized blocks in Java are marked with
the synchronized keyword. All synchronized blocks
synchronize on the same object and can only have one thread
executed inside them at a time. All other threads attempting to
enter the synchronized block are blocked until the thread inside
the synchronized block exits the block. If you want to master
concurrency and understand how to avoid common pitfalls,
General Form of Synchronized Block
synchronized(sync_object)
{
// Access shared variables and other
// shared resources
}
This synchronization is implemented in Java with a concept
called monitors or locks. Only one thread can own a monitor at
a given time. When a thread acquires a lock, it is said to have
entered the monitor. All other threads attempting to enter the
locked monitor will be suspended until the first thread exits the
monitor.
Example: Below is an example of synchronization using
Synchronized Blocks.

// Java Program to demonstrate synchronization block in Java

class Counter {
private int c = 0; // Shared variable

// Method with synchronization block


public void inc() {
synchronized(this) { // Synchronize only this block
c++;
}
}

// Method to get counter value


public int get() {
return c;
}
}

public class Geeks {


public static void main(String[] args) {
Counter cnt = new Counter(); // Shared resource

// Thread 1 to increment counter


Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
cnt.inc();
}
});

// Thread 2 to increment counter


Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
cnt.inc();
}
});

// Start both threads


t1.start();
t2.start();

// Wait for threads to finish


try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}

// Print final counter value


System.out.println("Counter: " + cnt.get());
}
}

Output
Counter: 2000

Types of Synchronization
There are two types of synchronizations in Java which are listed
below:
 Process Synchronization
 Thread Synchronization
1. Process Synchronization in Java
Process Synchronization is a technique used to coordinate the
execution of multiple processes. It ensures that the shared
resources are safe and in order.

Example: Here is a popular example of Process


Synchronization in Java
// Java Program to demonstrate Process Synchronization
class BankAccount {
private int balance
= 1000; // Shared resource (bank balance)

// Synchronized method for deposit operation


public synchronized void deposit(int amount)
{
balance += amount;
System.out.println("Deposited: " + amount
+ ", Balance: " + balance);
}

// Synchronized method for withdrawal operation


public synchronized void withdraw(int amount)
{
if (balance >= amount) {
balance -= amount;
System.out.println("Withdrawn: " + amount
+ ", Balance: " + balance);
}
else {
System.out.println(
"Insufficient balance to withdraw: "
+ amount);
}
}

public int getBalance() { return balance; }


}

// Main class
public class Geeks {
public static void main(String[] args)
{
BankAccount account
= new BankAccount(); // Shared resource

// Thread 1 to deposit money into the account


Thread t1 = new Thread(() -> {
for (int i = 0; i < 3; i++) {
account.deposit(200);
try {
Thread.sleep(50); // Simulate some delay
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
});

// Thread 2 to withdraw money from the account


Thread t2 = new Thread(() -> {
for (int i = 0; i < 3; i++) {
account.withdraw(100);
try {
Thread.sleep(
100); // Simulate some delay
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
});

// Start both threads


t1.start();
t2.start();

// Wait for threads to finish


try {
t1.join();
t2.join();
}
catch (InterruptedException e) {
e.printStackTrace();
}

// Print final balance


System.out.println("Final Balance: "
+ account.getBalance());
}
}

Output
Withdrawn: 100, Balance: 900
Deposited: 200, Balance: 1100
Deposited: 200, Balance: 1300
Withdrawn: 100, Balance: 1200
Deposited: 200, Balance: 1400
Withdrawn: 100, Balance: 1300
Final Balance: 1300

[Explanation: It demonstrates process synchronization using


a bank account with deposit and withdrawal operations. Two
threads, one for depositing and one for withdrawing, perform
operations on the shared account. The
methods deposit() and withdraw() are synchronized to
ensure thread safety, preventing race conditions when both
threads access the balance simultaneously. This ensures
accurate updates to the account balance.]

2. Thread Synchronization in Java


Thread Synchronization is used to coordinate and ordering of
the execution of the threads in a multi-threaded program.
There are two types of thread synchronization are mentioned
below:
Mutual Exclusive
Cooperation (Inter-thread communication in Java)
Example: Java Program to demonstrate thread
synchronization for Ticket Booking System.
// Java Program to demonstrate thread synchronization for
Ticket Booking System
class TicketBooking {
private int availableTickets = 10; // Shared resource
(available tickets)

// Synchronized method for booking tickets


public synchronized void bookTicket(int tickets) {
if (availableTickets >= tickets) {
availableTickets -= tickets;
System.out.println("Booked " + tickets + " tickets,
Remaining tickets: " + availableTickets);
} else {
System.out.println("Not enough tickets available to
book " + tickets);
}
}

public int getAvailableTickets() {


return availableTickets;
}
}

public class Geeks {


public static void main(String[] args) {
TicketBooking booking = new TicketBooking(); // Shared
resource

// Thread 1 to book tickets


Thread t1 = new Thread(() -> {
for (int i = 0; i < 2; i++) {
booking.bookTicket(2); // Trying to book 2 tickets each
time
try {
Thread.sleep(50); // Simulate delay
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});

// Thread 2 to book tickets


Thread t2 = new Thread(() -> {
for (int i = 0; i < 2; i++) {
booking.bookTicket(3); // Trying to book 3 tickets each
time
try {
Thread.sleep(40); // Simulate delay
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});

// Start both threads


t1.start();
t2.start();

// Wait for threads to finish


try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}

// Print final remaining tickets


System.out.println("Final Available Tickets: " +
booking.getAvailableTickets());
}
}
Output
Booked 2 tickets, Remaining tickets: 8
Booked 3 tickets, Remaining tickets: 5
Booked 3 tickets, Remaining tickets: 2
Booked 2 tickets, Remaining tickets: 0
Final Available Tickets: 0
[Explanation: Here, the TicketBooking class contains
a synchronized method bookTicket(), which ensures that
only one thread can book tickets at a time, preventing race
conditions and overbooking. Each thread attempts to book a
set number of tickets in a loop, with thread synchronization
ensuring that the availableTickets variable is safely accessed
and updated. Finally, the program prints the remaining tickets.]

Mutual Exclusion:-
Mutual Exclusion helps keep threads from interfering with one
another while sharing data. There are three types of Mutual
Exclusive mentioned below:
 Synchronized method.
 Synchronized block.
 Static synchronization.
Example: Below is the implementation of the Java
Synchronization.
// A Java program to demonstrate working of synchronized.
import java.io.*;
// A Class used to send a message
class Sender {
public void send(String msg)
{
System.out.println("Sending " + msg); // Changed to print
without new line
try {
Thread.sleep(100);
}
catch (Exception e) {
System.out.println("Thread interrupted.");
}
System.out.println(msg + "Sent"); // Improved output
format
}
}

// Class for sending a message using Threads


class ThreadedSend extends Thread {
private String msg;
Sender sender;

// Receives a message object and a string message to be


sent
ThreadedSend(String m, Sender obj)
{
msg = m;
sender = obj;
}

public void run()


{
// Only one thread can send a message at a time.
synchronized (sender)
{
// Synchronizing the send object
sender.send(msg);
}
}
}

// Driver class
class Geeks {
public static void main(String args[])
{
Sender send = new Sender();
ThreadedSend S1 = new ThreadedSend("Hi ", send);
ThreadedSend S2 = new ThreadedSend("Bye ", send);

// Start two threads of ThreadedSend type


S1.start();
S2.start();

// Wait for threads to end


try {
S1.join();
S2.join();
}
catch (Exception e) {
System.out.println("Interrupted");
}
}
}
Output
Sending Hi
Hi Sent
Sending Bye
Bye Sent

[Explanation: In the above example, we choose to


synchronize the Sender object inside the run() method of the
ThreadedSend class. Alternately, we could define the whole
send() block as synchronized, producing the same result.
Then we don't have to synchronize the Message object inside
the run() method in the ThreadedSend class.]

We do not always have to synchronize a whole method.


Sometimes it is preferable to synchronize only part of a
method. Java synchronized blocks inside methods make this
possible.
Example: Below is the Java program shows the synchronized
method using an anonymous class
// Java Pogram to synchronized method by
// using an anonymous class
import java.io.*;

class Test {
synchronized void test_func(int n)
{
// synchronized method
for (int i = 1; i <= 3; i++) {
System.out.println(n + i);
try {
Thread.sleep(100);
}
catch (Exception e) {
System.out.println(e);
}
}
}
}

// Driver Class
public class Geeks {
// Main function
public static void main(String args[])
{
// only one object
final Test O = new Test();

Thread a = new Thread() {


public void run() { O.test_func(15); }
};

Thread b = new Thread() {


public void run() { O.test_func(30); }
};

a.start();
b.start();
}
}

Output
16
17
18
31
32
33
[Explanation: Here the Test class has a synchronized method
test_func() that prints a sequence of numbers with a slight
delay, ensuring thread safety when accessed by multiple
threads. Two threads are created using anonymous classes,
each calling the test_func() method with different values. The
synchronized keyword ensures that only one thread can
execute the method at a time.]

File Handling in Java


In Java, with the help of File Class, we can work with files. This
File Class is inside the java.io package. The File class can be
used to create an object of the class and then specifying the
name of the file.
Why File Handling is Required?
 File Handling is an integral part of any programming
language as file handling enables us to store the output of
any particular program in a file and allows us to perform
certain operations on it.
 In simple words, file handling means reading and writing
data to a file.
Example
// Importing File Class
import java.io.File;

class Geeks
{
public static void main(String[] args)
{
// File name specified
File obj = new File("myfile.txt");
System.out.println("File Created!");
}
}
Output:
File Created!

In Java, the concept Stream is used in order to perform I/O


operations on a file. So, at first, let us get acquainted with a
concept known as Stream in Java.

Streams in Java
In Java, a sequence of data is known as a stream. This concept
is used to perform I/O operations on a file. Below are the types
of Streams:
1. Input Stream
The Java InputStream class is the superclass of all input
streams. The input stream is used to read data from numerous
input devices like the keyboard, network, etc. InputStream is an
abstract class, and because of this, it is not useful by itself.
However, its subclasses are used to read data.
There are several subclasses of the InputStream class, which
are as follows:

1. AudioInputStream
2. ByteArrayInputStream
3. FileInputStream
4. FilterInputStream
5. StringBufferInputStream
6. ObjectInputStream
Creating an InputStream:
// Creating an InputStream
InputStream obj = new FileInputStream();
Here, an input stream is created using FileInputStream.
Note: We can create an input stream from other subclasses as
well as InputStream.
Common Methods of InputStream:

Method Description

Reads one byte of data from the input


read()
stream.

read(byte[] Reads byte from the stream and stores that


array)() byte in the specified array.

It marks the position in the input stream


mark()
until the data has been read.

Returns the number of bytes available in the


available()
input stream.

markSupporte It checks if the mark() method and the


d() reset() method is supported in the stream.

Returns the control to the point where the


reset()
mark was set inside the stream.
Method Description

Skips and removes a particular number of


skips()
bytes from the input stream.

close() Closes the input stream.

2. Output Stream
The output stream is used to write data to numerous output
devices like the monitor, file, etc. Output Stream is an abstract
superclass that represents an output stream. Output Stream is
an abstract class and because of this, it is not useful by itself.
However, its subclasses are used to write data.
There are several subclasses of the Output Stream class which
are as follows:
1. ByteArrayOutputStream
2. FileOutputStream
3. StringBufferOutputStream
4. ObjectOutputStream
5. DataOutputStream
6. PrintStream
Creating an OutputStream:
// Creating an OutputStream
OutputStream obj = new FileOutputStream();
Here, an output stream is created using FileOutputStream.
Note: We can create an output stream from other subclasses
as well as Output Stream.
Common Methods of Output Stream:
Method Description

Writes the specified byte to the output


write()
stream.

write(byte[] Writes the bytes which are inside a specific


array) array to the output stream.

close() Closes the output stream.

Forces to write all the data present in an


flush()
output stream to the destination.

Based on the data type, there are two types of streams:

1. Byte Stream
This stream is used to read or write byte data. The byte stream
is again subdivided into two types which are as follows:
 Byte Input Stream: Used to read byte data from
different devices.
 Byte Output Stream: Used to write byte data to
different devices.

2. Character Stream
This stream is used to read or write character data. Character
stream is again subdivided into 2 types which are as follows:
 Character Input Stream: Used to read character data
from different devices.
 Character Output Stream: Used to write character data
to different devices.
Owing to the fact that you know what a stream is, let's polish
up File Handling in Java by further understanding the various
methods that are useful for performing operations on the files
like creating, reading, and writing files.
Java File Class Methods
The following table depicts several File Class methods:

Return
Method Name Description Type

It tests whether the file is


canRead() Boolean
readable or not.

It tests whether the file is


canWrite() Boolean
writable or not.

createNewFile
It creates an empty file. Boolean
()

delete() It deletes a file. Boolean

It tests whether the file exists


exists() Boolean
or not.

Returns the size of the file in


length() Long
bytes.

getName() Returns the name of the file. String

Returns an array of the files in


list() String[]
the directory.

mkdir() Creates a new directory. Boolean


Return
Method Name Description Type

getAbsoluteP Returns the absolute pathname


String
ath() of the file.

Let us now get acquainted with the various file operations in


Java.

File Operations
The following are the several operations that can be performed
on a file in Java:
 Create a File
 Read from a File
 Write to a File
 Delete a File

1. Create a File
 In order to create a file in Java, you can use the
createNewFile() method.
 If the file is successfully created, it will return a Boolean
value true and false if the file already exists.
// Creating File using Java Program
// Import the File class
import java.io.File;
import java.io.IOException;

public class CreateFile


{
public static void main(String[] args)
{
// Creating the File also
// Handling Exception
try {
File Obj = new File("myfile.txt");

// Creating File
if (Obj.createNewFile()) {
System.out.println("File created: " + Obj.getName());
}
else {
System.out.println("File already exists.");
}
}

// Exception Thrown
catch (IOException e) {
System.out.println("An error has occurred.");
e.printStackTrace();
}
}
}
Output:
2. Write to a File
We use the FileWriter class along with its write() method in
order to write some text to the file.
Example:
// Writing Files using Java Program

// Import the FileWriter class


import java.io.FileWriter;
import java.io.IOException;

public class WriteFile


{
public static void main(String[] args)
{
// Writing Text File also
// Exception Handling
try {

FileWriter Writer = new FileWriter("myfile.txt");

// Writing File
Writer.write("Files in Java are seriously good!!");
Writer.close();
System.out.println("Successfully written.");
}

// Exception Thrown
catch (IOException e) {
System.out.println("An error has occurred.");
e.printStackTrace();
}
}
}

Output

3. Read from a File


We will use the Scanner class in order to read contents from a
file.
Example:
// Reading File using Java Program

// Import the File class


import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class ReadFile
{
public static void main(String[] args)
{
// Reading File also
// Handling Exception
try {
File Obj = new File("myfile.txt");
Scanner Reader = new Scanner(Obj);

// Traversing File Data


while (Reader.hasNextLine()) {
String data = Reader.nextLine();
System.out.println(data);
}

Reader.close();
}

// Exception Cases
catch (FileNotFoundException e) {
System.out.println("An error has occurred.");
e.printStackTrace();
}
}
}
Output
4. Delete a File
We use the delete() method in order to delete a file.
Example:
// Deleting File using Java Program
import java.io.File;

public class DeleteFile


{
public static void main(String[] args)
{
File Obj = new File("myfile.txt");

// Deleting File
if (Obj.delete()) {
System.out.println("The deleted file is : " +
Obj.getName());
}
else {
System.out.println(
"Failed in deleting the file.");
}
}
}
Output

Java Method References


In Java, a method is a collection of statements that perform
some specific task and return the result to the caller. A method
reference is the shorthand syntax for a lambda
expression that contains just one method call. In general, one
does not have to pass arguments to method references.
Why Use Method References?
Method references are used for the following reasons, which are
listed below:
 Method references enhance readability, which makes the
code easier to understand.
 It supports a functional programming style that works well
with streams and collections.
 Reusability increases because we can directly use the
existing methods.
Note: Functional Interfaces in Java and Lambda Function are
prerequisites required in order to grasp a grip over Method
References in Java.

Example:
// Using Method Reference
import java.util.Arrays;
public class Geeks
{
// Method
public static void print(String s) {
System.out.println(s);
}

public static void main(String[] args)


{
String[] names = {"Geek1", "Geek2", "Geek3"};

// Using method reference to print each name


Arrays.stream(names).forEach(Geeks::print);
}
}
Output
Geek1
Geek2
Geek3

Explanation: In the above example, we are using method


reference to print items. The print method is a static method
which is used to print the names. In the main method we
created an array of names and printing each one by calling the
print method directly.

Key Benefits of Method References

The key benefits of method references are listed below:


 Improved Readability: Method references simplify the
code by removing boilerplate syntax.
 Reusability: Existing methods can be directly reused,
enhancing modularity.
 Functional Programming Support: They work
seamlessly with functional interfaces and lambdas.
Function as a Variable
In Java 8 we can use the method as if they were objects or
primitive values, and we can treat them as a variable.
// This square function is a variable getSquare.
Function<Integer, Integer> getSquare = i -> i * i ;
// Pass function as an argument to another function easily
SomeFunction(a, b, getSquare) ;
Sometimes, a lambda expression only calls an existing method.
In those cases, it looks clear to refer to the existing method by
name. The method references can do this, they are compact,
easy-to-read as compared to lambda expressions.
Generic Syntax for Method References

Aspect Syntax

Refer to a method in an
Object :: methodName
object

list.forEach(s ->
Print all elements in a list
System.out.println(s));

Shorthand to print all


list.forEach(System.out::println);
elements in a list

Types of Method References


There are four type method references that are as follows:
1. Static Method Reference
2. Instance Method Reference of a particular object
3. Instance Method Reference of an arbitrary object of a
particular type
4. Constructor Reference
To look into all these types we will consider a common example
of sorting with a comparator which is as follows:

1. Reference to a Static Method


A static method lets us use a method from a class without
writing extra code. It is a shorter way to write a lambda that
just calls that static method.
Syntax:
// Lambda expression
(args) -> Class.staticMethod(args);
// Method reference
Class::staticMethod;

Example: - Reference to a Static Method

// Reference to a static method


import java.io.*;
import java.util.*;

class Person
{
private String name;
private Integer age;

// Constructor
public Person(String name, int age)
{
// This keyword refers to current instance itself
this.name = name;
this.age = age;
}

// Getter-setters
public Integer getAge() { return age; }
public String getName() { return name; }
}

// Driver class
public class Geeks
{
// Static method to compare with name
public static int compareByName(Person a, Person b) {
return a.getName().compareTo(b.getName());
}

// Static method to compare with age


public static int compareByAge(Person a, Person b) {
return a.getAge().compareTo(b.getAge());
}

// Main driver method


public static void main(String[] args) {

// Creating an empty ArrayList of user-defined type


// List of person
List<Person> personList = new ArrayList<>();

// Adding elements to above List


// using add() method
personList.add(new Person("Vicky", 24));
personList.add(new Person("Poonam", 25));
personList.add(new Person("Sachin", 19));

// Using static method reference to


// sort array by name
Collections.sort(personList, Geeks::compareByName);

// Display message only


System.out.println("Sort by Name :");

// Using streams over above object of Person type


personList.stream()
.map(x -> x.getName())
.forEach(System.out::println);
System.out.println();

// Now using static method reference


// to sort array by age
Collections.sort(personList, Geeks::compareByAge);

// Display message only


System.out.println("Sort by Age :");

// Using streams over above object of Person type


personList.stream()
.map(x -> x.getName())
.forEach(System.out::println);
}
}
Output
Sort by Name :
Poonam
Sachin
Vicky

Sort by Age :
Sachin
Vicky
Poonam
Explanation: This example shows how to use static method
references to sort items. We have a person class with attributes
like name and age and there are two methods to compare
people by name and by age. In the main method we created a
list of people and sorting them by name and then sort them by
age and then printing the name again.
2. Reference to an Instance Method of a Particular
Object
This type of method means using a method from a certain
object which we already have. We do not need to write another
function to call that particular method we can just simply refer
to it directly.
Syntax:
// Lambda expression
(args) -> obj.instanceMethod(args);
// Method reference
obj::instanceMethod;

Example: - Reference to an Instance Method of a


Particular Object
// Reference to an Instance Method of
// a Particular Object
import java.io.*;
import java.util.*;

class Person {

// Attributes of a person
private String name;
private Integer age;
// Constructor
public Person(String name, int age)
{
// This keyword refers to current object itself
this.name = name;
this.age = age;
}

// Getter-setter methods
public Integer getAge() { return age; }
public String getName() { return name; }
}

// Helper class
// Comparator class
class ComparisonProvider
{
// To compare with name
public int compareByName(Person a, Person b) {
return a.getName().compareTo(b.getName());
}

// To compare with age


public int compareByAge(Person a, Person b) {
return a.getAge().compareTo(b.getAge());
}
}

// Main class
public class Geeks
{
public static void main(String[] args)
{
// Creating an empty ArrayList of user-defined type
// List of person
List<Person> personList = new ArrayList<>();

// Adding elements to above object


// using add() method
personList.add(new Person("Vicky", 24));
personList.add(new Person("Poonam", 25));
personList.add(new Person("Sachin", 19));

// A comparator class with multiple


// comparator methods
ComparisonProvider comparator
= new ComparisonProvider();

// Now using instance method reference


// to sort array by name
Collections.sort(personList,
comparator::compareByName);

// Display message only


System.out.println("Sort by Name :");

// Using streams
personList.stream()
.map(x -> x.getName())
.forEach(System.out::println);

System.out.println();

// Using instance method reference


// to sort array by age
Collections.sort(personList, comparator::compareByAge);

// Display message only


System.out.println("Sort by Age :");

personList.stream()
.map(x -> x.getName())
.forEach(System.out::println);
}
}
Output
Sort by Name :
Poonam
Sachin
Vicky

Sort by Age :
Sachin
Vicky
Poonam
Explanation: This example show how to use an instance
method reference to sort a list of people. We have created a
Person class with name and age and we also created a
ComparisonProvider class, it has methods to compare people
by name or age. In the main method we created a list of people
and we are using the ComparisonProvider instance to sort and
print the names first by name, then by age.
3. Reference to an Instance Method of an Arbitrary
Object of a Particular Type
It means calling a method on any object that belongs to a
certain group or class, not just one specific object. It helps us
write less code when we want to do the same thing for many
objects.
Syntax:
// Lambda expression
(obj, args) -> obj.instanceMethod(args);
// Method reference
ObjectType::instanceMethod;
Example: - Reference to an Instance Method of an
Arbitrary Object of a Particular Type
// Reference to an Instance Method of an
// Arbitrary Object of a Particular Type
import java.io.*;
import java.util.*;

public class Geeks


{
public static void main(String[] args)
{
// Creating an empty ArrayList of user defined type
// List of person
List<String> personList = new ArrayList<>();

// Adding elements to above object of List


// using add() method
personList.add("Vicky");
personList.add("Poonam");
personList.add("Sachin");

// Method reference to String type


Collections.sort(personList, String::compareToIgnoreCase);

// Printing the elements(names) on console


personList.forEach(System.out::println);
}
}

Output
Poonam
Sachin
Vicky
Explanation: This example show how to use a method
reference to sort a list of names. We created a list of names and
then sorting them ignoring uppercase or lowercase with the
help of compareToIgnoreCase method of the String class and
then we are printing the sorted names
4. Constructor Method Reference
It lets us quickly create a new object without writing extra code.
It is a shortcut to call the class new method.
Syntax:
// Lambda expression
(args) -> new ClassName(args);
// Method reference
ClassName::new;
Example: - Constructor Method Reference

// Java Program to Illustrate How We can Use


// constructor method reference
// Importing required classes
import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
import java.util.function.*;
// Object need to be sorted
class Person {
private String name;
private Integer age;

// Constructor
public Person()
{
Random ran = new Random();

// Assigning a random value


// to name
this.name
= ran
.ints(97, 122 + 1)
.limit(7)
.collect(StringBuilder::new,
StringBuilder::appendCodePoint,
StringBuilder::append)
.toString();
}

public Integer getAge()


{
return age;
}
public String getName()
{
return name;
}
}

public class Geeks {

// Get List of objects of given


// length and Supplier
public static <T> List<T>
getObjectList(int length,
Supplier<T> objectSupply)
{
List<T> list = new ArrayList<>();

for (int i = 0; i < length; i++)


list.add(objectSupply.get());
return list;
}

public static void main(String[] args)


{

// Get 10 person by supplying


// person supplier, Supplier is
// created by person constructor
// reference
List<Person> personList
= getObjectList(5, Person::new);

// Print names of personList


personList.stream()
.map(x -> x.getName())
.forEach(System.out::println);
}
}

Output
ilvxzcv
vdixqbs
lmcfzpj
dxnyqej
zeqejcn
Explanation: This example show how to use a constructor
method reference to create objects. We have created a Person
class and it gives each person a random name. The
getObjectList method creates a list of objects by using a
supplier, which means it uses the Person constructor to make
new Person objects. In the main method we created a list of
people and then we are printing their random names.
Common Use Cases
There are some common cases where we use Method
References in Java as mentioned below:
 Iterating over collections: Simplifying operations like
printing or processing elements.
 Stream API operations: Enhancing readability in
filtering, mapping, and reducing operations.
 Custom utilities: Using predefined methods for
frequently used tasks like sorting and comparisons.

You might also like