Java Back End Dev
Java Back End Dev
Install JDK (Java Development Kit) → for compiling & running code,
and JRE (Java Runtime Environment) → for running only
(Most developers install the JDK, which already includes the JRE.)
Check if Java is already installed or not:
java -version
openjdk version "21.0.8" 2025-07-15
OpenJDK Runtime Environment (build 21.0.8+9-Ubuntu-0ubuntu124.04.1)
OpenJDK 64-Bit Server VM (build 21.0.8+9-Ubuntu-0ubuntu124.04.1, mixed mode, sharing)
javac -version
javac 21.0.8
Install OpenJDK (Ubuntu provides OpenJDK via apt)
Java 21 (latest LTS-Long Term Support)
sudo apt update
sudo apt install openjdk-21-jdk -y
sudo update-alternatives --config java
/usr/lib/jvm/java-21-openjdk-amd64/bin/java
nano [Link] # file creation for small java program
javac [Link] # compiles to [Link]
java HelloWorld # runs the compiled class
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Brain of the computer - performs all calculations, executes instructions, and controls all other parts of the system.
Main Components of CPU
ALU (Arithmetic Logic Unit) Performs arithmetic operations and logical operations (like
comparison).
CU (Control Unit) Controls and coordinates the activities of all parts of the
computer - fetches, decodes, and executes instructions.
Registers Small, high-speed storage locations inside the CPU used to
store temporary data and instructions.
Functions:
◦ Fetches instructions from memory.
◦ Decodes and executes them.
◦ Sends output to the appropriate device.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Operating System
System software - acts as an interface between the user and the computer hardware. It manages all hardware and software
resources and allows other programs to run.
Functions:
◦ Process Management: Manages running programs (processes), their execution, and CPU scheduling.
◦ Memory Management: Allocates and tracks computer memory (RAM) used by programs.
◦ File Management: Manages files - how data us stored, named, and retrieved from storage devices.
◦ Device Management: Controls and coordinates I/O devices.
◦ User Interface (UI): Provides a way for users to interact with the computer. Eg., Command line (CLI) or GUI.
◦ Security & Access Control: Protects system data from unauthorized access or misuse.
◦ Error Detection & Handling: Detects and handles errors in software or hardware during operations.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Programming Language
Set of instructions and rules - allows humans to communicate with a computer to create software, websites, and applications.
It acts as a bridge between humans and machines, translating human logic into machine-understandable form (binary code or
sequence 0101).
Low-Level Language A language that is close to machine language Machine Language, Assembly
(binary code) and provides little to no Language
abstraction from hardware(giving more over
hardware but harder to write).
Directly understandable by the computer with
minimal translation.
Mid-Level Language A language that bridges the gap between C (You can use it for system
machine and high-level languages. Supports both programming - like kernel
low-level (hardware access) and high-level development, and can also
(programming logic) features. make applications)
High Level Language Human-readable and are portable across C++, Java, Python, Javascript,
computers. And also provides abstraction from PHP, C#
hardware. They need a compiler or interpreter to
run.
Programming Language Methodology
Structured approach used to design, organize, and develop programs using a particular language.
It’s how you plan, structure, and write your code to solve problems efficiently and maintainably.
Cross-Cutting Concerns
Java is NOT a Pure Object-Oriented Programming
◦ A pure object-oriented programming language is one in which everything is an object – every value, variable, operation, and
data type must belong to some class or object.
◦ In pure OOP language:
▪ NO primitive data types (everything is an object)
▪ Everything is accessed via methods
▪ There are no standalone functions or variables
▪ All code resides inside classes or objects
▪ Smalltalk – example of a nearly pure OOP language – even numbers, booleans, and operators is an object.
◦ Java is object-oriented, but NOT purely object-oriented. It follows most OOP principles but still allows some non-object-
oriented features.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Compiler
◦ Translates the entire program written in a high-level language (HLL) (like C, C++, Java) into machine code (object code or
executable file) before execution.
◦ High-Level Language → Compiler → Machine Code / Object Code
Interpreter
◦ An interpreter translates and executes the program line-by-line (or statement-by-statement) at runtime.
◦ High-Level Language → Interpreter → Execution (immediate)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
C++ vs Java
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Platform
It includes both:
◦ Hardware: the physical machine (CPU, memory)
◦ Software: the operating system and runtime that manage and execute programs.
When we talk about Java, the term “platform” means something very specific:
Java Platform = Java Runtime Environment (JRE) + Java Development Kit (JDK) + JVM + Java API
In short, Java provides its own platform, which runs on top of other platforms (like Windows, Linux, macOS). That’s what makes Java
platform-independent.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Microcontroller vs Microprocessor
Both microcontroller and microprocessor are integrated circuits (ICs) that act as the brain of a computing system. (Hardware)
Microprocessor (MPU)
◦ Brain Only
◦ A CPU chip used in general-purpose computers or systems where high computing power is needed.
◦ It only contains ALU (Arithmetic Logic Unit), Control Unit, and Registers
◦ It needs external chips for memory (RAM/ROM), Input/Output ports, Timers, ADCs, etc.
◦ Eg., Intel Core i7, AMD Ryzen, Intel 8085, Pentium.
◦ Used in desktop PCs, laptops, servers, high-performance systems.
Microcontroller (MCU)
◦ Brain + Body
◦ A self-contained mini-computer on a single chip.
◦ It includes CPU, RAM, ROM/Flash memory, Input/Output ports, Timers, Counters, ADC (Analog-to-Digital Converter), and
Communication interfaces (UART, SPI, I2C).
◦ Eg., Intel 8051, PIC, Atmega328 (Arduino), ARM Cortex-M series, ESP32.
◦ Used in Embedded systems, Home appliances, Cars (engine control, sensors), Robotics, IoT devices, Washing machines, and
Microwave ovens.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Features of Java
Simple Java is easy to learn and use because it has clear syntax and easy to understand. It does NOT have
complex features like pointers, operator overloading, multiple inheritance, and explicit memory
allocation. There is NO need to remove unreferenced objects because there is an automatic memory
management (Garbage Collection).
Object-Oriented Everything in Java is treated as object - making code modular, reusable, and easier to maintain.
Java is a very object-oriented language. Object-oriented means we organise our software as a
combination of different types of objects that incorporate both data and behavior (Whatever the logic
we want to write for our application, it will be written in the form of objects and class).
It follows concepts like Inheritance, Polymorphism, Abstraction, and Encapsulation.
Platform Independent and Compiler converts source code to bytecode and then the JVM executes the bytecode generated by the
Portable compiler. This bytecode can run on any platform be it Windows, Linux, or macOS, which means if we
compile a program on Windows, then we can run it on Linux and vice versa. Each operating system has
a different JVM, but the output produced by all the OS is the same after the execution of bytecode. It is
a software-based platform that runs on top of other hardware-based platforms which has two
components: Runtime Environment & Application Programming Interface.
Secured Java provides strong security features like bytecode verification, no explicit pointers, and a security
manager that defines access rules for classes.
[In Java, we do NOT have pointers, so we can NOT access out-of-bound arrays. That’s why several
security flaws like stack corruption or buffer overflow are impossible to exploit in Java. Also, Java
programs run in an environment (JVM) that is independent of the OS (Operating System) environment
which makes java programs more secure. Class Loader adds security by separating the package for the
classes of the local file system from those that are imported from network sources. Bytecode Verifier
checks the code fragments for illegal code that can violate access rights to objects. Security Manager
determines what resources a class can access such as reading and writing to the local disk.]
Robust Java language is robust which means reliable. It is developed in such a way that it puts a lot of effort
into checking errors as early as possible, that’s why the Java compiler can detect even those errors that
are not easy to detect any other programming language. Java has strong error-handling, exception
management, and memory management, reducing the chance of system crashes.
High Performance Java uses Just-In-Time (JIT) compiler (part of JVM).
When you run Java program, the Java compiler first converts .java into bytecode .class. JVM loads
bytecode and JIT compiler translates frequently executed parts (called hot code) into native machine
code at runtime. Once converted, this native code runs directly on the CPU, which makes execution
much faster.
Dynamic The Class Loader in Java loads new classes dynamically into memory while the program is running.
The Reflection API allows inspection and modification of classes, methods, and objects during runtime.
[In a web application, when a new plugin or module is added, Java doesn’t need to recompile the entire
program - it can load the new class dynamically while the application is running.]
Power of compilation and Most languages are designed with purpose either they are compiled language or they are interpreted
interpretation language. But Java integrates arising enormous power as Java compiler compiles the source code to
bytecode and JVM executes this bytecode to machine OS-dependent executable code.
Snadbox Execution Java programs run in a separate space that allows user to execute their applications without affecting
the underlying system with the help of a bytecode verifier.
Distributed Java enables programs to run and communicate across multiple networked systems. e.g., RMI (Remote
Method Invocation), EJB (Enterprise Java Beans), network-based apps.
[Java provides built-in networking APIs ([Link] package) to handle data exchange over the internet or
LAN.
Technologies like RMI and EJB allow objects or components to communicate and work together even if
they are on different machines.
Suppose you have a banking system where the front-end runs on one computer and the database on
another. Java can connect both using RMI, allowing one system to call methods or functions stores on
the other system - as if they were local.]
Multithreaded We can write Java programs that deal with many tasks at once by defining multiple threads for
maximum utilization of the CPU. The main advantage of multi-threading is that it doesn’t occupy
memory for each thread. It shares a common memory area. Threads are important for multi-media,
web applications, etc.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
JRE: (Java Runtime Environment) 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.
It contains JVM, Core libraries (like [Link], [Link], [Link]), Runtime files.
The entry point of your Java program is the main method. The JVM always looks for main method to start execution.
◦ Another method inside your class will not get displayed. JVM does not call it automatically because the method is the only one
executed by default.
◦ To execute it, you must call it manually inside main().
public class Basic {
public static void main(String[] args) {
[Link]("Hello World!");
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Source code ([Link]) written in C or C++. It is human-readable, but hardware cannot understand this directly.
The Compiler (gcc or g++) translates source code into machine code. The compiler checks syntax errors, type errors, and linking of
functions. If everything is correct, it generates an executable file.
Output: [Link] (on Windows) or [Link] (on Linux). This file contains machine instructions the CPU can execute.
When you run [Link], it is loaded into memory by the OS. The OS communicates with the hardware to execute instructions. The
output is then displayed on the screen.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
JShell
An interactive tool introduced in Java 9 - allows developers to execute Java code snippets instantly, without creating a full Java class
or writing boilerplate code like
public static void main(String[] args)
Also known as Java REPL (Read Eval Print Loop) - a command line tool for testing, learning, and exploring Java interactively.
How to start JShell?
◦ Open command prompt or terminal
◦ Type jshell
◦ Type syntax to check
◦ Exit JShell using /exit.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Java is used in all kinds of applications like mobile applications (Android is Java-based), desktop applications, web applications, client-
server applications, enterprise applications, gaming applications, cloud-based applications, and many more in all domain.
◦ Eg., Spotify, Twitter, Opera Mini Browser, Acrobat Reader, Netflix, Uber, MatLab, Simcards, Fintech Domain (Barclays, Hsbc, Citi
group, Goldman Sachs), Eclipse, Hadoop, Google Docs, etc.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Comments
Comments are non-executable statements used to explain code. They help developers understand, maintain, and document
programs — they are ignored by the compiler.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
[Link]()
This is not a single function. It is a chain of Class, Static field, Object, and Method call.
Break it into components:
◦ System
▪ System is a class in [Link]. So internally it is [Link]. It is automatically imported because [Link].* is
imported by default.
◦ out
▪ Inside System class, there is a field public static final PrintStream out;
▪ Meaning
public → accessible everywhere
static → belongs to class, not object
final → reference cannot change
Type = PrintStream
▪ So [Link] means access static variable out inside class System.
▪ out is an object of class [Link]. So, [Link] returns a PrintStream object.
◦ println()
▪ println() is a method inside PrintStream. So, [Link]() means call println() method on the PrintStream object
stored in [Link].
◦ println() is the method of the PrintStream class; out is an object of PrintStream which is present inside the System class.
Technical Structure:
Variable Naming
Valid variable names: ab, AB, aBc, a1, a12, _a, a_b, ab$, $ab
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Data Types
1 byte of space would store 8 bits of data. Out of which one is used for sign
int num = 5;
// int num2 = 5.5; // incompatible types: possible lossy conversion from double to int
// int num3 = 9999999999999999; // integer number too large
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Format Specifiers
% is used as a placeholder that tells the compiler: the value to be printed is of this specific data type. These placeholders are called
format specifiers.
◦ A convenience method to write a formatted string to this output stream using the specified format string and arguments.
An invocation of this method of the form [Link](format, args).
public class PrimitiveDataType1 {
public static void main(String[] args) {
int num = 5;
[Link]("Number = %d", num);
float pi = 3.14159f;
[Link]("Pi = %.2f", pi);
char ch = 'A';
[Link]("Character = %c", ch);
}
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Non-Primitive demonstration
class Point {
int x;
int y;
}
class Test {
public static void main(String[] args) {
[Link](p1.x); // 30
[Link](p2.x); // 30
}
}
Output:
00
Explanation:
A Point object is created. And instance variables x and y are not explicitly initialized.
Output:
Compile-time error: variable ‘x’ might not have been finalized.
Output:
20 20
class SwapTwoNumbers {
static void main() {
int a = 10;
int b = 20;
int temp = a;
a = b;
b = temp;
[Link](a+" "+b);
}
}
Output:
20 10
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Output:
10 10 10
Explanation:
x1 is a primitive int
Autoboxing: primtive int -> wrapper class Integer
Uncoxing: Integer -> primitive int
Output:
Not same
class Test {
static void main() {
Integer x1 = 40;
int x2 = 40;
if(x1 == x2)
[Link]("Same");
else
[Link]("Not Same");
}
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Variables
Variable in Java is a data container that saves the data values during Java program execution. Every variable is assigned a data type
that designates the type and quantity of value it can hold. A variable is a memory location name for the data.
The value stored in a variable can be changed during program execution.
In Java, all variables must be declared before use.
Local Variables
◦ Defined within a block or method or constructor.
◦ The scope of these variables exists only within the block in which the variables are declared.
◦ Stored in stack memory.
◦ Must be initialized.
◦ Can NOT use access modifiers (public, private, etc.)
◦ Exists only during method execution.
class Test {
void display() {
int a = 10; // Local variable
[Link](a);
}
}
Instance Variables
◦ Non-static variables and are declared in a class outside of any method, constructor, or block.
◦ They are declared in a class, these variables are created when an object of the class is created and destroyed when the object
is destroyed.
◦ Initialization is not mandatory(default 0).
◦ Can be accessed only by creating Objects.
◦ The scope of these variables available to all methods of that class.
◦ Stored in heap memory.
◦ Java assigns default value. [eg., int → 0, boolean → false, object → null]
class Student {
int age; // Instance variable
void show() {
[Link](age);
}
}
◦ Each object has its own copy.
◦ Changes in one object do NOT affect another.
Student s1 = new Student();
Student s2 = new Student();
[Link] = 20;
[Link] = 25;
Static Variables
◦ Declared using the static keyword within a class outside of any method, constructor or block.
◦ Static variables are created at the start of program execution and destroyed automatically when execution ends.
◦ We can only have one copy of a static variable per class. Can be accessed without Object Creation. [When i declare the variable
static,there is a single copy of that variable created across class. So, even if I create 100 objects, 100 instance variables will be
created, but only a single static variable will be created.]
class Counter {
Counter() {
count++;
}
void display() {
[Link]("Count = " + count);
}
[Link]();
[Link]();
[Link]();
Explanation
static int count → Only one copy exists in the class.
Every time an object is created, the constructor increases count.
All objects share the same variable, so each prints the same value.
[Link] shows that static variables can be accessed without object creation.
◦ Shared among all objects.
◦ Stored in method area.
class Student {
static String college = "ABC College"; // Static variable
int id;
void display() {
[Link](id + " " + college);
}
}
[Link] = 1;
[Link] = 2;
[Link]();
[Link]();
Output
1 ABC College
1 ABC College
Practical implementation:
◦ Local Variable
▪ Use a local variable when the data is temporary and only required during the execution of a method.
▪ Eg., Suppose we want to calculate the total price of items in a cart.
class Order {
void calculateTotal() {
int total = 0; // local variable
◦ Instance Variable
▪ Use instance variables when the data belongs to an object and every object should have its own value.
▪ Eg., student name, Bank account balance, Employee salary, Product price
class Student {
void display() {
[Link](name + " scored " + marks);
}
[Link]();
[Link]();
}
}
◦ Static Variable
▪ Use static variables when the value is shared by all objects of the class.
▪ Eg., College name for students, Company name for employees, Bank name for all accounts, Global counter.
class Employee {
int id;
String name;
static String company = "TCS"; // static variable
void display() {
[Link](id + " " + name + " " + company);
}
[Link]();
[Link]();
}
}
Operators
An operator is a symbol that performs an operation on one or more operands (variables or values) and produces a result.
int a = 10;
int b = 5;
int c = a + b; // '+' is an operator
Here,
a and b → operands
+ → operator
Result → 15
Arithmetic Operators
◦ Used to perform mathematical calculations.
int a = 10;
int b = 3;
[Link](a + b); // 13
[Link](a - b); // 7
[Link](a * b); // 30
[Link](a / b); // 3
[Link](a % b); // 1
int a = 10;
int b = 5;
[Link](a > b); // true
[Link](a == b); // false
[Link](a != b); // true
Logical Operators
◦ Used with boolean expressions.
Assignment Operators
◦ Used to assign values to variables.
int a = 10;
a += 5;
[Link](a); // 15
Unary Operators
◦ Operate on only one operand.
int a = 5;
a++; // increment
[Link](a); // 6
a--; // decrement
[Link](a); // 5
Bitwise Operators
◦ Used to perform bit-level operations.(Biwise operators work at the binary or bit level)
◦ Every integer in Java is stored as bits (0 and 1), and these operators manipulate those bits directly.
int a = 5; // 0101
int b = 3; // 0011
0101
&0011
-----
0001
int a = 5; // 0101
int b = 3; // 0011
int result = a | b;
[Link](result); // 7
0101
|0011
-----
0111
int a = 5; // 0101
int b = 3; // 0011
int result = a ^ b;
[Link](result); // 6
0101
^0011
-----
0110
int a = 5; // 0101
int result = ~a;
[Link](result); // -6
~x = -(x + 1)
~5 = -(5 + 1) = -6
iint a = 5;
int result = a << 1;
[Link](result); // 10
5 = 0101
0101 << 1
1010
int a = 8;
int result = a >> 1;
[Link](result); // 4
8 = 1000
1000 >> 1
0100
int a = -8;
int result = a >>> 1;
[Link](result);
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Access Modifiers
Access modifiers in Java helps to restrict the scope of a class, constructor variable, method, or data member. There are four types
of access modifiers available in Java (i.e., public, protected, default, private).
Accessibility: Private < Default < Protected < Public (most restricted → most open)
Default
◦ In Java, default access modifier means no modifier is written. If you do not specify public, protected, or private, Java
automatically assigns default (package-private) access.
◦ Members with default access are accessible only within the same package.
class Student {
class Main {
[Link]([Link]); // allowed
[Link](); // allowed
}
}
Output:
age and display() have default access
Since both classes are in the same package, access is allowed.
____________________________________________________________________________________________________________
____________________________________________________________________________________________________________
ACCESSING DEFAULT IN DIFFERENT PACKAGE [ERROR]
package package1;
class Student {
import [Link];
Explanation:
default access = same package only.
Private
◦ A private variable, method, or constructor can only be accessed inside the same class. It can NOT be accessed from another
class, even if the class is in the same package.
class Student {
Explanation:
age and display() are private
They are accessible inside the same class
____________________________________________________________________________________________________________
____________________________________________________________________________________________________________
ACCESSING PRIVATE OUTSIDE THE CLASS [ERROR]
class Student {
Output:
Compilation Error: age has private access in Student.
Explanation:
private members cannot be accessed outside the class.
____________________________________________________________________________________________________________
____________________________________________________________________________________________________________
ACCESSING PRIVATE USING GETTER AND SETTER (ENCAPSULATION) -> Used in real application
class Student {
[Link](25);
[Link]([Link]());
}
}
Output:
25
Explanation:
age is private.
Access is controlled through public methods.
____________________________________________________________________________________________________________
____________________________________________________________________________________________________________
PRIVATE CONSTRUCTOR EXAMPE (Sometimes constructors are also private) -> used in Singleton design pattern
class Test {
private Test() {
[Link]("Object created");
}
Protected
◦ A protected member can be accessed in another package only through inheritance (subclass). However, you can NOT access it
using a parent class object. You must access it through the subclass object.
package package1;
import [Link];
public class Child extends Parent {
[Link](c.x); // allowed
[Link](); // allowed
}
}
____________________________________________________________________________________________________________
package package2;
import [Link];
Public
◦ public is the least restrictive access modifier in Java.
◦ A public class, variable, method, or constructor can be accessed from anywhere:
▪ Same class
▪ Same package
▪ Subclass
▪ Different package
class Student {
[Link]([Link]); // allowed
[Link](); // allowed
}
}
Output:
Age: 20
Explanation:
age and display() are public
They can be accessed from any class
____________________________________________________________________________________________________________
____________________________________________________________________________________________________________
PUBLIC CLASS CAN BE USED FROM ANY PACKAGE
package pakage1;
import [Link];
Explanation:
Access works because the class and method are public.
____________________________________________________________________________________________________________
____________________________________________________________________________________________________________
PUBLIC CONSTRUCTOR
class Car {
public Car() {
[Link]("Car object created");
}
}
Output:
Car object created.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Methods
Block of code grouped together to perform a certain task or operation.
Achieve re-usability.
Write once, use many times.
Easy modification and readability.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Constructor
Special Type of method, Called when instance of the class is created and memory of object is allocated.
Java Compiler provides default constructor, if there is no constructor available.
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).
Name Same as class.
No Return Type.
void Test() { } // This becomes a method, not constructor
Can NOT be abstract, Static, Final and Synchronized.
class Student {
int age;
Student() {
age = 20;
}
Output:
20
Explanation:
Memory for the object is allocated in heap.
The constructor is automatically called.
Variables are initialized
Java provides a Constructor class which can be used to get the internal information of a constructor in the class. It is found in the
[Link] package.
Constructor chaining is the process of calling one constructor from another constructor with respect to current object. Within same
class: It can be done using this() keyword. From base class: by using super() keyword.
Destructor: There is no concept of destructor in Java. In place of the destructor, Java provides the garbage collector that works the
same as the destructor. It automatically deletes the unused objects (objects that are no longer used) and free-up the memory. The
programmer has no need to manage memory manually. It can be error-prone, vulnerable, and may lead to a memory leak.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Java Source File Structure
Java program can contain any number of classes.
A Java source file may have any name, but if it contains a public class, then the source file name must be exactly the same as the
public class name.
}
class B {
}
class C {
}
class A {
}
class B {
}
class C {
A Java source file can contain at most one public class. If more than one public class is declared in the same source file, the compiler
throws an error.
// File name: [Link]
public class P01ClassNaming {
}
public class A {
}
class B {
}
class C {
}
Output: java: class A is public, should be declared in a file named [Link]
The Java file name is decided ONLY by the public class. The class containing main() can be public or non-public. If no public class
exists, the file can be named anything.
class Runner {
public static void main(String[] args) {
[Link]("Running");
}
}
javac [Link]
java Runner
}
We can use any name for the file.
◦ After compilation javac [Link] -> Generated files: [Link], [Link], [Link], [Link]
◦ Each class generates a separate .class file, and the JVM executes the main() method of the class specified in the java command.
In above program, 4 classes will get generated.
▪ When I execute java A -> JVM finds main() in class A
▪ java B -> JVM finds main() in class B
▪ java C -> JVM finds main() in class C
▪ java D -> Class D does not contain main() -> JVM throws runtime error -> Error: Main method not found in class D
▪ java P01ClassNaming -> Runtime error will be thrown -> Error: Could not find or load main class P01ClassNaming
Caused by: [Link]: P01ClassNaming
import statement
package OopsConcept;
/*
OUTPUT
java: cannot find symbol
symbol: class ArrayList
location: class OopsConcept.P02ImportStatement
*/
Solution: import ArrayList Class (import [Link]),
else use fully qualified name: [Link] list = new [Link]();
class Test {
public static void main(String[] args) {
Scanner sc = new Scanner([Link]);
}
}
Recommended; No ambiguity; Faster readability
▪ Explicit Pakage import -> imports all classes of a package (not sub-packages)
import [Link].*;
class Test {
ArrayList list = new ArrayList();
}
All compiled classes (.class files) available in the current working directory are automatically available to a Java program, without
using an import statement.
◦ The current working directory (.) is part of the default classpath. JVM and compiler search the classpath to locate classes.
Hence, casses in the same directory (default package) are found automatically. (Java works with compiled bytecode, not source
files.)
src
├── Arrays
├── Concurrency_and_Multithreading
├── OopsConcept
│ ├── [Link]
│ ├── [Link]
│ └── [Link]
├── Pattern_Printing
│ ├── [Link]
│ ├── [Link]
│ └── [Link]
└── [Link] -> move [Link] into a package, then only you can access
src
└── Generics
└── [Link]
package OopsConcept;
import [Link];
import java.*; // ❌ INVALID – Pattern is available inside ‘regex’ package
s
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
OOPs (Object-Oriented Programming System)
OOPS is a programming paradigm that models real-world entities using objects and classes.
OOPS organizes code around objects rather than functions and uses Encapsulation, Abstraction, Inheritance, and Polymorphism to
create secure and reusable software systems.
Its primary goal is to bind data and the functions that operate on that data together, restricting direct access from outside the class.
Core components of OOPS: class, object, encapsulation, abstraction, inheritance, and polymorphism.
4 Pillars of OOPS:
Encapsulation
Wrapping data and methods into a single unit and restricting direct access.
Achieved by:
Making variables private.
Providing getter and setter methods.
Benefits:
Data hiding
Controlled access
Security
Abstraction
Showing only essential details and hiding implementation.
Achieved by:
Abstract classes
Interfaces
Eg., When you drive a car, you use steering and pedals - you do NOT see engine complexity.
Inheritance
One class acquires properties and methods of another class.
Benefits:
Code re-usability
Logical hierarchy
Polymorphism
“Many forms” A single action behaves differently in different situations.
Types:
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Class
Group of objects which have common properties. It is a template or blueprint from which objects are created. It is a logical entity. It
can NOT be physical.
Defines properties (variables) and behaviors (methods).
Class contains fields, methods, constructors, blocks, nested class, and interface.
Modifiers: Private < Default < Protected < Public
Constructor
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. Same name, no return type, Called only once.
Types: Default and Parameterised contructors
A class can have multiple constructors, a concept known as constructor overloading. Each constructor must have a unique
signature (different number and/or types of parameters) to allow the compiler to distinguish between them. A specific constructor
is invoked explicitly using the new keyword when an object is instantiated, matching the arguments provided at that time.
Constructors are used to initialize the instance variables of a newly created object.
Blocks
A block is a group of zero or more statements enclosed within curly braces { }.
Types of blocks:
Instance Block (or Non-static Block)
A block that runs every time an object is created, before the constructor executes.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Object
Physical as well as a logical entity, that has state (represented by attributes of an object; also reflects the properties of an object. Eg.,
breed, age, color), behavior (represented by methods of an object; also reflects the response of an object with other objects,
functionalities. Eg., bark, sleep, eat) and identity (gives a unique name to an object and enables one object to interact with other objects.
Eg., name of the dog).
Instance (an example or case of something) of a class.
Represents real-world entity.
int x = 10;
[Link](t1.x);
[Link](t2.x);
}
}
Using Deserialization:
Used when reading an object from a file.
import [Link].*;
[Link](obj.a);
}
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Inheritance
Inheritance is an Object-Oriented Programming (OOP) concept where one class acquires the properties and behaviors (fields and
methods) of another class.
It helps in code reuse and establishing relationships between classes.
class Parent {
// properties and methods
}
class Animal {
void eat() {
[Link]("Animal is eating");
}
}
void bark() {
[Link]("Dog is barking");
}
Output:
Animal is eating
Dog is barking
Explanation:
Dog inherits eat() from Animal
Dog also has its own method bark()
Types of Inheritance
Single Inheritance
One child class inherits from one parent class.
A→B
class A {
void show() {
[Link]("Class A");
}
}
class B extends A {
void display() {
[Link]("Class B");
}
}
____________________________________________________________________________________________________________________
____________________________________________________________________________________________________________________
class Vehicle {
String name;
int NoOfWheels;
void start() {
[Link](name+" has started.");
}
}
class Car extends Vehicle {
void accelerate() {
[Link]("Car has been accelerated.");
}
static void main() {
Car car = new Car();
[Link] = "Ferrari";
[Link] = 4;
[Link]();
[Link]();
}
}
Multilevel Inheritance
Inheritance chain of multiple levels.
A→B→C
class A {
void show() {
[Link]("Class A");
}
}
class B extends A {
void display() {
[Link]("Class B");
}
}
class C extends B {
void print() {
[Link]("Class C");
}
}
Hierarchical Inheritance
Multiple child classes inherit from one parent class.
A
/ \
B C
class A {
void show() {
[Link]("Class A");
}
}
class B extends A { }
class C extends A { }
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Encapsulation
Encapsulation is the process of wrapping data (variables) and code (methods) together into a single unit.
In Java, this unit is a class.
Explanation:
id can NOT be accessed directly
It can only be accessed through methods
Better security
External classes can NOT directly modify the data. Only controlled methods can change it.
Better maintainability
If you change internal logic, other code does NOT break.
Eg., You modify salary calculation internally but the interface (getter/setter) remains the same.
Helps in unit testing
Encapsulation allows controlled testing of methods and data.
Real life analogy: Think about an ATM machine. You interact only with withdraw, deposit, check balance. But the internal banking system
is hidden. This is Encapsulation.
User → ATM Interface → Bank System (Hidden)
this keyword is used to represent the current object.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Abstraction
Abstraction is the process of hiding implementation details and showing only essential functionality to the user.
The user knows what the system does, but NOT how it does it.
Eg., When you drive a car, you use the steering wheel, accelerator, and brakes — but you don’t need to know how the engine works
internally → That’s abstraction.
Or When you send a message on WhatsApp, you just type and press send — you don’t see how the data is transmitted, encrypted, or
stored. All that complexity is hidden from you. That’s abstraction in action
How Abstraction is implemented in Java?
Abstraction can be achieved by:
Abstract class: Using the abstract keyword; can contain both abstract and concrete methods.
Interface: 100% (or nearly complete) abstraction; defines only method signatures (no implementation, except default or
static methods).
// Class 'Dog' must either be declared abstract or implement abstract method 'makeSound()' in 'Animal'
class Dog extends Animal {
void makeSound() {
[Link]("Bark");
}
}
Output:
Bark
Sleeping...
Meow
Explanation:
Animal defines what all animals do (makeSound, sleep)
Each subclass (Dog, Cat) defines how that sound is made.
The internal sound logic is hidden → Abstraction.
interface Appliance {
void turnOn(); // abstract method
void turnOff(); // abstract method
}
// Class 'Fan' must either be declared abstract or implement abstract method 'turnOn()' in 'Appliance'
// Class 'Fan' must either be declared abstract or implement abstract method 'turnOff()' in 'Appliance'
class Fan implements Appliance {
public void turnOn() {
[Link]("Fan starts spinning at medium speed");
}
Output:
Fan starts spinning at medium speed.
Fan stops spinning.
Washing machine starts washing clothes.
Washing machine stops and drains water.
Explanation:
The interface Appliance defines what every appliance can do — turnOn() and turnOff().
The classes Fan and WashingMachine implement how those actions actually happen.
In main(), we only use the interface reference (Appliance) to call methods — this hides the internal implementation → Abstraction.
Explanation:
Here,Appliance is the interface type (abstract).
Fan is the concrete implementation class.
You’re telling Java: “I don’t care which appliance it is — just give me one that can be turned on or off.”
Explanation:
Here, both the reference type and object type are the same (Fan).
You’re explicitly depending on the concrete class.
You now know and rely on the exact implementation — which breaks abstraction.
You can access everything the Fan class provides — even those not part of the interface.
Interface reference = new ImplementingClass(); → ✅ Abstraction
ConcreteClass reference = new ConcreteClass(); → ❌ No abstraction
// Class 'CreditCardPayment' must either be declared abstract or implement abstract method 'pay(double)' in 'Payment'
class CreditCardPayment extends Payment {
void pay(double amount) {
[Link]("Paid "+amount+"using Credit Card");
}
}
Output:
Transaction in process...
Paid ₹1500.0 using UPI.
Transaction in process...
Paid ₹2000.0 using Credit Card.
Explanation:
Payment defines a general structure for all payment types.
Concrete subclasses (CreditCardPayment, UpiPayment) decide how the payment is processed.
The main program only uses the Payment reference — not caring about the internal process.
This is real-world abstraction — the user knows only what to do (pay()), not how it’s done internally.
Bank b;
b = new SBI();
[Link]([Link]());
b = new HDFC();
[Link]([Link]());
}
}
Output:
7
8
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Abstract Class
A class that cannot be instantiated (you can’t create an object of it directly). It is declared using the abstract keyword.
abstract class Animal {
abstract void makeSound(); // abstract method (no body)
An abstract class can have both the regular (or concrete) and abstract methods. A method that doesn’t have its body is known as
abstract method.
◦ Abstract methods → must be implemented by the subclass (unless the subclass is also abstract).
◦ Concrete methods → are already implemented in the abstract class, and subclasses can either use them directly or override
them if needed.
Though abstract classes cannot be instantiated, we can create subclasses from it. We can then access members of the abstract class
using the object of the subclass.
If the abstract class includes any abstract method, then all the child classes inherited from the abstract superclass must provide the
implementation of the abstract method.
// Class 'Car' created in order to override abstract methods. '@Override' to check methods, if the Override method(s) exists in parent class or
not.
// Class 'Car' must either be declared abstract or implement abstract methods 'accelerator' and 'brakes(int)' in 'Vehicle'
class Car extends Vehicle {
// Must implement abstract method
@Override
void accelerator() {
[Link]("Car is accelerating");
}
@Override
void brakes(int wheels) {
[Link]("Car "+wheels+" wheels brakes are pushed");
}
void honks() {
[Link]("Car is honking");
}
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Interface
An interface is a fully abstract class. It includes a group of abstract methods (methods without a body). We use the interface
keyword to create an interface in Java.
To use an interface, other classes must implement it. We use the implements keyword to implement an interface.
interface Animal {
// public void abstract sound(); // modifier ‘abstract’ is redundant for interface methods; and modifier ‘public’ is also redundant
(not necessary) for interface members
void sound(); // abstract method (no body)
}
Multiple Inheritance means - A class can inherit features (methods/properties) from more than one parent.
◦ Java does not support multiple inheritance using classes because of the Diamond Problem.
class A {
void show() {
[Link]("show() method of class A");
}
}
class B {
void show() {
[Link]("show() method of class B");
}
}
interface Showable {
void show();
}
Output:
Car starts with a key.
Interfaces can have default methods with implementation (Java 8+)
interface SmartDevice {
void turnOn();
Output:
Smart TV is ON
Connecting to WiFi...
◦ Before Java 8, interfaces were very restrictive. We could only define abstract methods and public static final variables
(constants). And Interfaces could NOT contain method implementations. Every method was implicitly abstract. Implementing
classes must override all methods.
◦ Solution introduced in Java 8 as Default methods. A default method allows an interface to provide a method implementation.
Static methods belong to the interface itself, NOT to objects (Java 8+).
interface Gadget {
static void info() {
[Link]("Gadgets are electronic devices.");
}
}
When a class wants to extend something from another class, we use extends keyword. When a class wants to inherit
something from interface, we use implements keyword. When a interface wants to inherit something from another
interface, we use extends keyword.
There is a parent class, and interface wants to inherit from that. Is it possible? No, this is NOT possible in Java. An
interface cannot inherit from a class. Interfaces can only extend other interfaces. In Java, a class and an interface have
different purposes: Class → contains state (variables) + behavior (methods with implementation). Interface → defines
contracts (method declarations). Because of this design, an interface cannot inherit implementation from a class.
An interface which has no member is known as a marker/tagged interface. Eg., Serializable, Cloneable, Remote, etc. They
are used to provide some essential information to the JVM, so that JVM may perform some useful operation.
Cloneable Interface
In Java, the Cloneable interface is a marker interface present in the Java [Link] package.
A marker interface means it does NOT contain any methods, but it signals the JVM that a class allows cloning of its
objects.
Cloning means creating an exact copy of an existing object in memory.
If a class does NOT implement Cloneable and we try to clone it using clone(), Java throws: CloneNotSupportedException.
Java's [Link] method creates a copy of an object, but it only works when the class implements Cloneable. Without
Cloneable : Exception in thread "main" [Link]. So, Cloneable tells JVM: “This class allows
object cloning”.
To clone an object in Java: Implement Cloneable interface, override clone() method, call [Link](), and handle
CloneNotSupportedException.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Polymorphism
Polymorphism means “many forms.”
In OOPs, one task can be performed in different ways using the same interface or method name.
Example idea: A person can be a father, employee, or husband depending on the situation.
Polymorphism helps to:
Write flexible code.
Improve code readability.
Support method reuse.
Enable dynamic behavior in programs.
There are two types of Polymorphism:
Compile-Time Polymorphism (Static binding)
The method to execute is decided during compilation.
Achieved through Method Overloading (same method name but different parameters).
class MathUtil {
int add(int a, int b) {
return a + b;
}
Explanation:
Here add() behaves differently depending on parameters
class Test {
public static void main(String[] args) {
Animal a = new Dog();
[Link]();
}
}
Output:
Dog barks
Explanation:
Here the method is decided at runtime.
Method Overloading
When there are multiple functions with the same name but different parameters then these functions are said to be overloaded.
Increases readability.
Functions can be overloaded by change in the number of arguments or/and change in the tye of arguments. Can NOT overload by
return type. Type Conversion but to higher type, if exact prototype does NOT match. We can overload static methods. We can
overload main method too.
If no method exactly matches the argument type, Java will automatically promote the argument to the next higher compatible
data type to find a matching [Link] happens during compile-time polymorphism (method overloading resolution).
Primitive promotion order in Java: byte → short → int → long → float → double
Java will move upward in this hierarchy to find the closest matching method. [int can NOT accept double values.]
Method Overriding
Overriding is a feature that allows a subclass or child class to provide a specific implementation of a method that is already
provided by one of its super-classes or parent classes.
When a method in a subclass has the same name, same parameters or signature, and same return type (or sub-type) as a
method in its super-class. Also called Dynamic Method Dispatch. [case of Method Overriding]
Function call to the overriden method is resolved at runtime.
It is the type of the object being referred to (not the type of the reference variable) that determines which version of a overriden
method will be executed.
We can NOT override static methods, its data hiding.
If we comment out the child body, then it will print the Parent class value.
Parent Reference (parentChildObj) is referring to child object, and this scenario is known as Dynamic Method Dispatch. And
in such case, which method to call is decided at runtime, based on which object is referred to. So, the parent reference is
referring to the child object, so at the runtime it decides that I am referrring to child object, so I am going to call the method
which is present in child class. But for the case of variable, it looks for the variable on the basis of reference it has. If the
reference is of parent class, it is going to read first that variable from parent class, or may be I can say from the class of which
reference is being used for. This phenomena is also known as Upcasting. When a reference of a parent class is referring to the
object of a child class.
[When a parent class reference refers to a child class object, it is called Upcasting. In this situation, overridden methods
follow Dynamic Method Dispatch, meaning the method is resolved at runtime based on the object type, whereas variables
are accessed based on the reference type.]
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Static
The static keyword in Java is used for class-level members.
It means the member belongs to the class rather than to individual objects.
Static members are shared by all objects.
They are loaded into memory once when the class is loaded.
Static variable (class variable)
A static variable is shared among all objects of the class.
Only one copy exists in memory.
Stored in the class area (method area).
Created when the class is loaded.
All objects share the same value.
[Link] does NOT work in some contexts because this refers to the current object (instance) of the class, while a static
variable belongs to the class, not to an object.
How can I load the collegeName before the main() method gets executed? Using static block.
Static class
Class can be made static only if it is nested class. We can NOT declare a top-level classes with a static modifier, but can declare
nested class as static. Nested class does NOT need a reference of Outer class.
In this case, a static class can NOT access non-static members of the of the Outer class.
Static method
A static method belongs to the class, NOT to objects.
Can be called without creating an object.
Can access only static variables and static methods directly.
Can NOT directly access non-static members.
Static block
A static block is used to initialize static variables.
Executes only once.
Runs when the class is loaded.
Executes before the main method.
The static keyword in Java is used to create class-level variables, methods, blocks, or nested classes that belong to the class rather than
to objects and are shared across all instances.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// To create an object:
Outer outer = new Outer();
[Link] inner = [Link] Inner();
class Inner {
void display() {
// Can access private members of Outer
[Link](message);
}
}
}
Output:
Hello from Outer class
class Engine {
void start() {
[Link](model + " engine started!");
}
}
void run() {
Engine e = new Engine();
[Link]();
}
}
Output:
Tesla Model 3 engine started!
s
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Final
The final keyword in Java is used to restrict changes. [Used to restrict the user.]
It can applied to variables, methods, and classes.
Final variable
A final variable can NOT be modified once it is assigned a value. It behaves like a constant.
A blank final variable is declared without initialization but must be initialized once. [If we are creating a blank final variable, you
will be allowed to assign the value to that variable at least once, or at most once. In simple words, you can only assign the value for
a single time.]
If the final variable is a reference, this means that the variable can NOT be re-bound to reference another object.
The uppercase rule applies mainly to static final constants, because they represent global constants for the class.
Final method
A final method can NOT be overridden by a subclass.
Prevent modification of critical behavior.
Final class
A final class can NOT be inherited.
Eg., all Wrapper classes like Integer, String, Float, Double. To create an immutable class like the predefined String class. One can
NOT make a class immutable without making it final.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Factory Design Pattern
The Factory Design Pattern is a creational design pattern used in Object-Oriented Programming and widely used in Java.
Its main idea is: Move the object creation logic from the client code to a separate factory class.
Instead of the client directly creating objects using new, it asks a factory class to create the object.
This improves loose coupling (change at one place, does NOT affect the other part), code flexibility, and maintainability.
Interface (Shape)
The top layer contains an interface called Shape.
It defines a contract.
Any shape must implement the draw() method.
Concrete Classes
Three classes implement the interfaces: Circle, Square, Rectangle.
They provide their own implementation of draw().
All these classes implement the same interface but behave differently.
Factory Class (ShapeFactory)
The factory class is responsible for object creation.
The factory decides which object to create.
The client does NOT know the actual class being instantiated.
Client Code (FactoryDesignPattern)
The client asks the factory for objects.
Flow of execution
Client calls ShapeFactory.
Client requests an object (getShape()).
Factory decides which class to instantiate.
Factory returns the object.
Client uses the object through the Shape interface.
interface Shape {
void draw();
}
class Circle implements Shape {
public void draw() {
[Link]("Drawing Circle");
}
}
class Square implements Shape {
public void draw() {
[Link]("Drawing Square");
}
}
class Rectangle implements Shape {
public void draw() {
[Link]("Drawing Rectangle");
}
}
class ShapeFactory {
public Shape getShape(String shapeType) {
if(shapeType == null) return null;
if([Link]("CIRCLE"))
return new Circle();
if([Link]("SQUARE"))
return new Square();
if([Link]("RECTANGLE"))
return new Rectangle();
return null;
}
}
The Factory Method Pattern is a creational design pattern that provides an interface for creating objects, but lets subclasses decide
which class to instantiate.
In simple terms: Instead of creating objects using new, the factory method creates the object for you.
Why it is needed?
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Design Patterns
Design Patterns are reusable solutions to common software design problems that occur during application development.
They are NOT ready-made code, but templates or blueprints that help developers design flexible and maintainable systems using
Object-Oriented Programming principles.
In simple terms: A Design Pattern is a standard way to solve a commonly occurring problem in software design.
Problems that mainly occur:
Creating objects efficiently.
Structuring classes properly.
Managing communication between objects.
Why Design Patterns are important?
Reusable solutions
Design patterns provide general solutions to recurring problems in software design.
Instead of reinventing the solution every time, developers can apply an already proven approach.
Eg., Instead of writing complex object creation logic repeatedly, developers can use the Singleton Pattern.
Established best practices
Design patterns are considered best practices discovered by experienced developers over many years.
They were popularized by the famous book: “Design Patterns: Elements of Reusable Object-Oriented Software”. The authors
are often called the Gang of Four (GoF).
Improved Communication
Design patterns provide a common vocabularly for developers.
Eg., Instead of explaining a long design structure, a developer can say: “We should use the Factory Pattern here”.
Immediately everyone understands the approach.
Better maintainability and scalability
Design pattern promote loose coupling, high cohension, and better modular structure. This leads to easier maintenance,
easier testing, and better scalability.
Design patterns are broadly divided into three categories:
Creational Patterns
Structural Patterns
Behavioral Patterns
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
s
Exception Handling
Exception
An unexpected event that occurs during program execution. It affects the flow of the program instructions which can cause the
program to terminate abnormally.
An exception can occur for many reasons. Some of them are:
◦ Invalid user input ([Link])
◦ Device failure ([Link])
◦ Loss of network connection ([Link], [Link], [Link])
◦ Physical limitations (out of disk memory or out of RAM) ([Link] → Heap space exhausted (RAM),
[Link]: No space left on device → Disk storage is full)
◦ Code errors ([Link], [Link], [Link])
◦ Opening an unavailable file ([Link])
Types of Exception:
◦ Runtime Exceptions (Unchecked Exceptions)
▪ java Main -> run-time
▪ These are programming errors that happen during execution (runtime) – not checked at compile-time.
▪ They are the subclasses of [Link].
▪ If it is runtime exception, its your fault.
▪ Compiler does not force you to handle them (no need for try-catch or throws)
▪ Examples:
Exception Handling
[Link]
└── [Link]
├── [Link]
│ ├── Checked Exceptions
│ └── Unchecked Exceptions (RuntimeException)
└── [Link]
When a Java program runs, the JVM creates a runtime stack (also called the call stack) to store method call information. Each time a
method is called:
◦ A new stack frame is created for that method.
◦ The frame holds local variables, return address, and intermediate results.
◦ When the method finishes, its frame is popped (removed) from the stack.
◦ After completing execution, JVM destroys the empty stack after completing all methods call and terminates the program
normally.
public class Test {
public static void main(String[] args) {
doStuff();
}
class Test {
public static void main(String[] args) {
doStuff();
}
This stack trace represents the sequence of method calls that led to the exception — from where it occurred (doMoreStuff) up to where it was
initiated (main).
class Test {
public static void main(String[] args) {
doStuff();
}
class Test {
public static void main(String[] args) {
doStuff();
[Link](10 / 0);
}
Atleast one method terminates abnormally, the total program termination is abnormal termination. If methods completed
normally, then only termination is normal termination.
Exception Hierarchy
[Link]
└── [Link]
├── [Link]
│ ├── RuntimeException
│ | ├── ArithmeticException
│ │ ├── IndexOutOfBoundsException
│ │ | ├──ArrayIndexOutOfBoundsException
│ │ | ├── StringIndexOutOfBoundsException
│ │ ├── ArrayStoreException
│ │ ├── ClassCastException
│ │ ├── IllegalArgumentException
│ │ ├── NumberFormatException
│ │ ├── IllegalStateException
│ │ ├── IndexOutOfBoundsException
│ │ │ ├──StringIndexOutOfBoundsException
│ │ ├── NullPointerException
│ │ ├── NegativeArraySizeException
│ │ ├── UnsupportedOperationException
│ │ ├── SecurityException
│ │ └── ConcurrentModificationException
│ │
│ ├── CloneNotSupportedException
│ ├── InterruptedException
│ ├── IOException
│ │ ├── EOFException
│ │ ├── FileNotFoundException
│ │ ├── SocketException
│ │ └── UnknownHostException
│ ├── SQLException
│ │ └── SQLSyntaxErrorException
│ ├── ClassNotFoundException
│ ├── InstantiationException
│ ├── NoSuchMethodException
│ ├── InvocationTargetException
│ ├── ParseException
│ ├── TimeoutException
│ ├── ExecutionException
│ ├── ReflectiveOperationException
│ └── GeneralSecurityException
│ ├── InvalidKeyException
│ ├── SignatureException
│ └── NoSuchAlgorithmException
│
└── [Link]
├── AssertionError
├── OutOfMemoryError
├── StackOverflowError
├── VirtualMachineError
│ ├── InternalError
│ └── UnknownError
├── LinkageError
| ├── ClassCircularityError
│ ├── ClassFormatError
│ ├── NoClassDefFoundError
│ ├── UnsatisfiedLinkError
│ └── VerifyError
└── ServiceConfigurationError
Used to handle exceptions and prevents the abnormal termination of the program.
Basic syntax:
When an exception occurs, Java automatically creates an exception object — like ArithmeticException, IOException, etc. That object holds
information about the error message, where it occurred, and stack trace. When you catch it: catch (Exception e) , here e is that exception
object.
getStackTrace() → It returns the stack trace elements as an array of StackTraceElement objects. Each element
represents one step (method call) in the call stack.
getMessage() → It returns a short description or message about what went wrong. It’s usually set by Java internally
(like “/ by zero”) or by you if you throw a custom exception.
printStackTrace() → It prints the entire call stack — showing: exception type, error message, and line numbers in the
file where it occurred.
There should be only one object - that should be globallly accessible.
[Link]
[Link]();
Design patterns are well proved solution of commonly occurring problems in software design.
Prototype Pattern
It is used when we have to make copy/clone from existing object.
Objects are different.
Checking instance 2 times.
Handled the multi-threaded environments.
Wrapper Class
A Wrapper Class in Java is a class that wraps (converts) a primitive data type into an object.
Java is object-oriented, but primitive types (int, char, etc.) are not objects. Wrapper classes allow primitives to be treated as objects.
Primitive -> Wrapper mapping
Primitive Type Wrapper Class
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean
Classes in the [Link] package (like Collection, List, Map, etc.) work only with objects, not with primitive data types. Wrapper
classes help by converting primitives into objects so they can be used with these classes.
// PRIMITIVES ARE NOT OBJECTS
int x = 10; // primitive
Synchronization in multithreading requires an object (or class) lock. Hence, an object is needed to support synchronization.
◦ In Java, every object has an intrinsic lock (monitor). Synchronization works by acquiring a lock. This lock is associated with an
object (instance-level synchronization) and a class (class-level synchronization).
The automatic conversion of primitive types to the object of their corresponding wrapper class is known as autoboxing. Eg.,
conversion of int to Integer, long to Long, double to Double, etc.
Integer obj = new Integer(12); // Constructors are Deprecated since Java 9 → Always creates a new object, which leads to
unnecessary memory usage.
Integer obj2 = new [Link](12); // uses Integer Cache. Default cache range: -128 to 127. Avoids unnecessary object creation
◦ valueOf() is available in all Java wrapper classes and is used to convert primitive values or strings into their corresponding
wrapper objects.
byte myByte = [Link](“10”);
float myFloat = [Link](“10.5”);
char myChar = [Link](‘A’);
Boolean myBoolean = [Link](false);
Unboxing is just the reverse process of autoboxing. Automatically converting an object of a wrapper class to its corresponding
primitive type is known as unboxing. Eg., conversion of Integer to int, Long to long, Double to double, etc.
int num = obj; // unboxing
Generics
Generics means parameterized types. Using Generics, it is possible to create classes that work with different data types. An entity
such as class, interface, or method that operates on a parameterized tye is a generic entity.
// create a generic class
class GenericsClass<T> {
// variable of T type
private T data;
[Link]([Link]()); // 121
[Link]([Link]()); // 12118
}
}
class Student<E> {
E id;
Student(E id) {
[Link] = id;
}
E getId() {
return id;
}
}
[Link]([Link]()); // 121
[Link]([Link]()); // Rajeev Shukla
}
}
E getId() {
return id;
}
V getName() {
return name;
}
}
The Object is the superclass of all other classes, and Object reference can refer to any object. These features lack type safety.
Generics add that type of safety feature. Generics provide strong compile-time checking. (Generics in Java are similar to templates in
C++)
Type Safety: Generics make errors to appear compile-time than at run-time. Suppose you want to create an ArrayList that store
name of students, and if by mistake the programmer adds an integer object instead of a string, the compiler allows it. But, when we
retrieve this data from ArrayList, it causes problems at runtime.
Type Safety prevents insertion of wrong data types. And errors are detected at compile time, not runtime. This avoids ClassCastException.
// WITHOUT GENERICS
ArrayList list = new ArrayList();
[Link]("Rahul");
[Link](10); // allowed
// Problem: Runtime error when retrieving data
// WITH GENERICS
ArrayList<String> list = new ArrayList<>();
[Link]("Rahul");
[Link](10); // compile-time error
Code Reuse: We can write a method/class/interface once and use it for any type we want.
Similar to the generic class, we can also create a method that can be used with any type of data. Such a class is known as Generics
Method.
public <T> void genericMethod(T data) {...}
// <E> is an unbounded type parameter. E can be any reference type. This includes String, Integer, Long, User-defined classes
like CustomClass
<E> void printDoubleData(E data) {
[Link](data);
}
}
class CustomClass {
In general, the type parameter can accept any data types (except primitve types). However, if we want to use generics for some
specific types (such as accept data of number types) only, then we use bounded types.
Here, GenericClass is created with bounded type. This means GenericsClass can only work with data types that are children of
Number (Byte, Short, Integer, Long, Float, Double).
class GenericClass <T extends Number> {
public void display() {
[Link]("This is a bounded type generic class.");
}
}
class CustomClass {
E can be only Nuber or its subclasses. <E extends Number> restricts the generic type to numeric wrapper classes, ensuring only
Number or its subclasses can be passed.
<E extends Object> would work same as <E> means any object.
Syntax of Anonymous class
@FunctionalInterface is an optional
Only one abstract method
Objects can’t call static methods. Static methods are associated with class.
Streams
Introduced in Java 8 (in [Link] package), that allows us to process collections (like List, Set, etc) and other data sources in a
functional and declarative way.
The stream is a pipeline of operations: you create a stream from a source, apply zero or more intermediate operations (which produce
another stream), then apply a terminal operation to produce a result.
A Stream is not data; it has a source (collection, array, I/O), and a chain of operations represented as a pipeline object. The work
happens when the terminal operation is invoked.
Stream dont change the original data structure, they only provide the result as per the pipelined methods. Each intermediate operation
is lazily executed and returns a stream as a result, hence various intermediate operations can be pipelined. Terminal operations mark
the end of the stream and return the result. There should be exactly one terminal operation in the stream, without a terminal operation
the stream wouldn’t flow.
Stream is lazy and evaluates code only when required. The elements of a stream are only visited once during the life of a stream. Like
an Iterator, a new stream must be generated to revisit the same elements of the source.
Streams are single-pass. The pipeline processes each element once when terminal op runs. If you need to process the same source
multiple times, create a new stream each time.
Iterator also consumes elements one-by-one; streams are conceptually similar but offer many functional operations and can be
parallelized.
Don’t store a stream for long-term reuse.
For repeated processing, either create the stream anew or collect to a container first: List<T> copy = [Link]().collect(toList()).
Streams are wrappers around a data source, allowing us to operate with that data source and making bulk processing convenient and
fast.
A stream wraps the source and provides a fluent API to transform and aggregate data in bulk (i.e., operate on many elements with
concise expressions).
Stream acts like a view or pipeline connected to the original data. It doesn’t copy or modify the data. It just provides a wrapper that lets you
perform transformations on the elements inside the source.
You can chain methods together in a readable, natural way — like a sentence.
Each method returns a stream again (until the last one). This allows “fluent chaining” — no intermediate variables or nested loops. Improves
readability and reduces boilerplate code.
map/filter/reduce allow expressing "bulk" computation (apply to every element) without explicit loops.
Java Stream methods:
[Link](T… values) To create a stream from given values. Stream<Integer> stream = [Link](1, 2,
3);
[Link](int start, int Creates a stream of integers from start IntStream Stream = [Link](1,
end) (inclusive) to end (exclusive). 10); // 1 to 9
[Link](array) Converts an array to IntStream, int[] arr = {1, 2, 3};
LongStream, DoubleStream, or generic IntStream stream = [Link](arr);
Stream.
mapToInt() Converts objects to int values (returns Stream<String> s = [Link](“10”, “20”,
IntStream) “30”);
IntStream nums =
[Link](Integer::parseInt);
average() Returns OptionalDouble. Used only on double avg = [Link](new int[] {10,
IntStream, DoubleStream, etc. 20, 30}).average().getAsDouble(); // 20.0
max() Returns OptionalInt/OptionalDouble. int max = [Link](new int[]{5, 10,
2}).max().getAsInt(); // 10
boxed() Convert primitive stream to wrapper IntStream intStream = [Link](1,
(IntStream → Stream) 5);
Stream<Integer> boxed =
[Link]();
Example 1
int[] arr = {5, 10, 15};
double avg = [Link](arr)
.mapToInt(i -> i*2) // double every number
.average()
.getAsDouble();
int maxValue = [Link](arr)
.boxed()
.mapToInt(Integer::intValue)
.max()
.getAsInt();
[Link](avg); // 20.0
[Link](maxValue); // 15
Example 2
List<String> names = [Link]("John", "Jane", "Jack", "Dee", "Dane");
[Link]().filter(name -> [Link]('J') && [Link]()>=4)
.map(String::toUpperCase)
.sorted()
.forEach([Link]::println);
Terminal Operations
collect: The collect method is used to return the result of the intermediate operations performed on the stream.
List numbers = [Link](2, 3, 4, 5, 3);
Set square = [Link]().map(x->x*x).collect([Link]());
forEach: The forEach method is used to iterate through every element of the stream.
List numbers = [Link](2, 3, 4, 5, 3);
[Link]().map(x -> x * x).forEach(y->[Link](y));
reduce: The reduce method is used to reduce the elements of a stream to a single [Link] reduce method takes a Identity (initial
value), Accumulator/Binary Operator (logic of aggregation) & combiner (in parallel mode) as a parameter.
List numbers = [Link](2, 3, 4, 5, 3);
int even = [Link]().filter(x -> x%2==0).reduce(0,(ans,i)-> ans+i);
Primitive Streams
To work with the three most used primitive types — int, long and double — the standard library includes three primitive-
specialized implementations: IntStream (sequence of primitive int-valued elements), LOngStream , and DoubleStream.
◦ Java provides IntStream, LongStream, and DoubleStream as primitive-specialized stream types that hold primitive values (not
wrapper objects).
◦ These let you perform streaming operations (map/filter/reduce/etc.) on numbers without creating Integer/Long/Double objects
for each element.
◦ Less allocation and less GC overhead for large numeric pipelines
▪ When processing huge amounts of numeric data (like millions of integers, doubles, sensor data, logs, analytics, etc.), the
performance depends heavily on how many objects get allocated and how much work the Garbage Collector (GC) has to
do.
▪ A numeric pipeline usually means operations like: processing millions of numbers (sum, max, filter, map); data
transformations in streams; data analytics; machine-learning preprocessing; and reading numeric files or network data.
▪ Less allocation = fewer temporary objects created in memory.
▪ Less GC overhead = Garbage Collector runs less frequently and does less work.
▪ Integer, Double, Long (object wrappers) → cause:
per-item object allocation
pointer indirection
GC pressure
Stream<Integer> stream = [Link]()
.map(x -> x*2)
.reduce(0, Integer::sum);
// This creates millions of Integer objects.
▪ Why ‘Less GC overhead’ matters? GC is expensive beacause it pauses the application, scans memory regions, frees objects,
and moves or compacts memory. When processing Gbs of numeric data per second, GC pauses slow down throughput
severly.
▪ How to reduce allocation & GC overhead? Use primitive arrays (int[], double[], long[]) and primitive streams (IntStream,
DoubleStream, LongStream). Avoid: Integer[], Double[], Stream<Integer>
int Integer
Type Primitive data type Wrapper (Object) class
Not in any package [Link]
Memory Stores the actual numeric value in Stores a reference to an object that
memory. contains the value.
int a = 10;S Integer b = 10;
Here, a goes on stack with value. Here, b refers to an Integer object in heap
(or Integer cache).
Default values 0 null
Null handling Cannot be null Can be null
int y = null; Integer x = null;
Usage in Collections You cannot use: Collections like ArrayList, HashMap, etc
List<int> list; // invalid require objects, not primitives:
List<Integer> list = new ArrayList<>();
Autoboxing and Unboxing Java automatically converts between int int a = 5;
and Integer: Integer b = a; // autoboxing
int a = 5;
Integer b = a; // autoboxing
int c = b; // unboxing
Peformance int is faster and memory efficient. Integer is slower because: it requires
object creation; more memory usage; and
possible null checks.
Comparison int x = 1000; Integer a = 1000;
int y = 1000; Integer b = 1000;
[Link](x == y); // true [Link](a == b); // false
[Link]([Link](b)); // true
Why?
== compares object reference.
equals() compares value.
Boxing and unboxing does take some time, but it’s not a lots. A lot of temporary boxed objects also triggers Garbage Collection a
lot more often, and that’s a performance drain too. It all adds up, so if the stream processes a lot of integer values in a tight 'loop',
the difference can be relevant.
◦ Boxing = converting a primitive int → Integer (e.g., [Link](5) or autoboxing).
◦ Unboxing = converting Integer → int (e.g., int x = integerObj;).
◦ Boxing creates objects on the heap. In a tight numeric loop or large stream, repeatedly creating wrapper objects increases
memory churn and garbage collection overhead.
List<Integer> boxed = [Link](0, 1_000_000).boxed().collect([Link]());
// creates 1_000_000 Integer objects -> memory + GC cost
// Large arrays, tight loops, or high-throughput streaming pipelines: prefer primitive streams to avoid many temporary wrapper
objects.
The overhead of Integer is quite large. An int is 4 bytes for the value, while an Integer is 4 bytes for the reference plus 16 bytes for
the object, so Integer uses ~20 bytes per value, i.e. 5 times the memory.
◦ A primitive int stores the 32-bit value directly (4 bytes).
◦ An Integer is an object: you pay for the object header, the field(s), alignment/padding, and the reference you store in containers
— so memory per Integer is much larger than 4 bytes.
◦ Storing millions of numbers as Integer consumes much more memory than int[] or IntStream operations.
◦ Use primitive arrays or primitive streams when memory is a concern.
s
Thread
We heard about the processes, when we buy the system, we think that, if we have the multiple processors, the more the number of
processors, the system will not hang. The system will work seamlessy.
The static code is known as program, but when that program starts execution, it becomes a process. Process is a program in
execution (means a program that is currently running in the computer and using resources like memory and CPU), and the thread is
a small task or worker inside a process that helps do work faster by doing things at the same time (basic unit of execution within a
Java program that allows multiple tasks to run concurrently within a single process).
Eg., In mobile phone, whatsApp app installed (program) → WhatsApp opened and running (process) → One thread receives
messages, one thread send messages, and one thread downloads images (all happen at the same time i.e., thread).
The threads help developers build responsive and efficient applications by:
◦ executing tasks in parallel
▪ Threads enable multiple tasks (such as input handling, computation, and I/O operations) to run simultaneously instead of
sequentially.
◦ effectively utilizing multi-core processors
▪ Eg., On a 4-core processor, up to 4 threads can run concurrently, maximizing CPU utilization
◦ improved application performance and responsiveness
▪ Long-running tasks can be executed in background threads, allowing the main thread to remain responsive (e.g., UI
remains smooth while data is loading).
Process vs Thread
Process Thread
A process is an independent program in execution. Each A thread is a lightweight unit of execution that exists
process has its own address space, system resources, and within a process. Multiple threads can exist inside a single
execution context. process and execute concurrently.
Every process has its separate memory space, which include Threads within the same process share the same memory
its own heap, stack, and data segment. One process cannot space (heap and data) but have separate stacks for
directly access another process’s memory. method calls and local variables.
A process is allocated resources such as memory, files, and Threads do NOT own resources independently; they share
I/O devices independently by the operating system. the resources allocated to the parent process, making
execution more efficient.
Creating a process involves significant overhead because the Thread creation is lightweight since threads reuse the
OS must allocate seperate memory and resources. existing process resources, resulting in faster startup and
lower system cost.
Process communicate using Inter-Process Communication Threads communicate easily through shared variables and
(IPC) mechanisms such as pipes, sockets, shared memory, or objects, enabling faster and simpler communication.
message queues, which are complex and slower.
Processes are highly isolated from one another. A failure in Threads have low isolation. An error in one thread (eg.,
one process usually does not affect other processes. modifying shared data incorrectly) can impact other
threads in the same process.
Switching between processes requires saving and loading Thread context switching is faster and cheaper because
complete execution contexts, making context switching threads share the same memory and resources.
expensive.
Process provide better stability but are less efficient for Threads offer higher performance and are ideal for tasks
concurrent tasks. requiring parallel execution, such as servers and real-time
applications.
Eg., Running Chrome and MS Word simultaneously. Eg., Multiple tabs in Chrome, each handled by threads.
Creating Threads
◦ Extending the Thread Class
▪ The Thread class provides the foundation for creating and managing threads in Java. By extending this class, you can
override the run() method to define the code that will be executed in a separate thread.
class ThreadExample{
public static void main(String[] args) {
MyThread thread1 = new MyThread(); // create thread instance
MyThread thread2 = new MyThread(); // create another thread instance
try {
[Link](500); // Pause execution for 500 milliseconds
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
/*
Thread-1 is running: 0
Thread-0 is running: 0
Thread-1 is running: 1
Thread-0 is running: 1
Thread-1 is running: 2
Thread-0 is running: 2
Thread-1 is running: 3
Thread-0 is running: 3
Thread-1 is running: 4
Thread-0 is running: 4
*/
/*
Thread-0 is running: 0
Thread-1 is running: 0
Thread-1 is running: 1
Thread-0 is running: 1
Thread-1 is running: 2
Thread-0 is running: 2
Thread-1 is running: 3
Thread-0 is running: 3
Thread-1 is running: 4
Thread-0 is running: 4
*/
▪ Advantages: Better object-oriented design and allows class to extend other classes.
Same Runnable instance can be shared across multiple threads.
▪ Disadvantages: Slightly more code to write and indirect access to Thread methods.
Collection: If we want to represent a group of individual objects (called elements) as a single entity, then we should go for
Collection.
◦ It is a complete architecture to store and manipulate collections of objects.
◦ Located in [Link] package.
◦ It is used to storem retrieve, manipulate, and delete data easily.
◦ Eg., List, Set, Queue.
◦ Features:
▪ Can grow or shrink dynamically.
▪ Can store heterogeneous (different) tyoes of objects.
▪ Built-in methods for adding, removing, searching, sorting, etc.
◦ Collection framework: It defines several classes (implementations) and interfaces (rules) which can be used a group of objects
as single entity.
◦ A standardized set of interfaces and classes in Java to store and manage collections efficiently.
◦ Located in [Link] package.
◦ Provides interfaces, classes, and algorithms.
Java C++
Collection Container
Collection framework STL (Standard Template Library)
Part
Collection (I):
If we want to represent a group of individual objects as a single entity then we should go for Collection.
▪ A Collection allows us to store multiple objects together in one container (like a bag or list).
▪ For example, instead of creating separate variables:
int a = 10;
int b = 20;
int c = 30;
▪ We can put them into one collection:
List<Integer> list = [Link](10, 20, 30);
Collection interface defines the most common methods which are applicable for any Collection object.
▪ The Collection interface is the base (parent) interface for most data structures in Java.
▪ It defines common methods that every collection should have, such as:
add()
remove()
size()
clear()
contains()
Since different collection types (like List, Set, Queue) implement this interface, they all provide these basic
operations.
In general collection framework is considered as root interface of Collection Framework. (There is no concrete class which
implements collection interface directly.)
▪ The Java Collection Framework has a hierarchy. At the top sits the Collection interface (like the family head). But no class
directly implements Collection.
Instead List, Set, and Queue extend the Collection interface. Classes like ArrayList, HashSet, and LinkedList implement those interfaces, not
Collection directly.
Collection (interface)
|
|--- List (interface)
| |--- ArrayList (class)
| |--- LinkedList (class)
|
|--- Set (interface)
| |--- HashSet (class)
| |--- TreeSet (class)
List (I):
If we want to represent a group of individual objects as a single entity where duplicates are allowed and insertion order
preserved then we should go for List.
Implementation: ArrayList (1.2v), LinkedList (1.2v), Vector, Stack. (Vector and Stack came in 1.0v, that’s why they are known as
Legacy classes – classes that existed before Java 1.2, before the modern Collection Framework. Vector and Stack classes are re-
engineer in 1.2v to implement list interface).
Set (I):
A Queue is a data structure that follows the FIFO – First In First Out. It means element inserted first is removed first.
Eg.,
▪ People standing in a line at a ticket counter: The person who comes first gets served first. The last person waits until all
before them are served. This directly represents FIFO.
▪ Print Queue: When multiple documents are sent to printer, they are printed in the order they were submitted. The first
document entered is printed first.
▪ Task Scheduling in OS: Processes waiting for CPU time are stored in a queue. CPU picks the next process from the front.
Used heavily in OS scheduling algorithms.
[Link](10);
[Link](20);
[Link](30);
[Link]([Link]()); // 10
[Link]([Link]()); // 10 (removed)
[Link](q); // [20, 30]
}
}
Priority Queue
import [Link].*;
Blocking Queue
▪ waits (blocks) if queue is full (on insert) or empty (on removal).
import [Link].*;
Map (I)
Map(I) 1.2v
|
|
______|_____________________________________________ Dictionary (AC) 1.0v
| | | | |
| | | | |
HashMap 1.2v WeakHashMap 1.2v IdentityHashMap 1.4v Hashtable 1.0v
| |
| |
LinkedHashMap Properties 1.0v
If we want to represent a group of individual objects as key-value pairs, then should go for Map.
Both key and value are objects. Keys are unique, but values can be duplicate.
Common Implementations:
▪ HashMap
most commonly used
no order guaranteed
fast performance (O(1) average)
▪ LinkedHashMap
maintains insertion order
▪ TreeMap
stores keys in sorted order (based on natural ordering or Comparator)
▪ Hashtable
synchronized (thread-safe)
slower and legacy
Common methods:
▪ put(k, v) → insert or replace a value
▪ get(k) → retrieve value
▪ remove(k) → delete mapping
▪ containsKey(k) → check if key exists
▪ containsValue(v) → check if value exists
▪ keySet() → returns all keys
▪ values() → returns all values
▪ entrySet() → returns key-value pairs
SortedMap (I)
Map 1.2v
|
|
SortedMap 1.2v
If we want to represent a group of key value pairs according to some sorting order of keys then we should go for SortedMap.
NavigableMap (I)
Map(I) 1.2v
|
|
SortedMap(I) 1.2v
|
|
NavigableMap(I) 1.6v
|
|
TreeMap 1.2v
It is the child interface (or sub-interface) of SortedMap, it defines several utility methods for navigation urpose.
Common methods:
▪ firstKey() → returns lower key
▪ lastKey() → returns highest key-value
▪ headMap(k) → keys strictly before given key-value
▪ tailMap() → keys greater or equal to given key
▪ subMap(fromKey, toKey) → keys greater or equal to given key
▪ reversed() → returns a reverse order view of this map
Collection Framework
Collection(I) 1.2v
|
|
____________________________|________________________
| | |
| | |
List(I) Set(I) Queue(I) 1.5v
| | |
| | |
________|_________ ____|____ ____|____
| | | | | | |
AL LL Vector 1.0v | | | |
| HashSet SortedSet(I) PQ 1.5v BQ 1.5v
| | | |
Stack 1.0v | | |
LHS NavigableSet(I) 1.6v ____|____
1.4v | | |
| | |
TreeSet PBQ LBQ
1.5v 1.5v
Map(I) 1.2v
|
| 1.0v
______|_________________________________________________ Dictionary(AC)
| | | | | |
| | | | | |
HashMap WeakedHashMap IdentityHashMap SortedMap(I) Hashtable 1.0v
| 1.4v | |
| | |
LinkedHashMap 1.4v 1.6v NavigableMap(I) Properties 1.0v
|
|
TreeMap
Sorting:
Comparable Comparator
Purpose Used for default (natural) sorting of Used for custom/multiple sorting logics.
objects.
Package Available in [Link] package. Available in [Link] package
Method public int compareTo(T o) public int compare(T o1, T o2)
How many sort orders? Only one sorting logic (inside the class Multiple sorting logics possible (outside
itself). the class).
Where is the logic written? Inside the same class of the object being In a separate class OR via lambda.
sorted.
Used when When class has a natural order (eg., When you want different ways to sort
Integer, String). same objects (eg., by name, by age).
Real-life analogy Your Aadhaar card number: fixed, single Filters on Amazon: sort by price,
identity. popularity, rating – multiple options.
import [Link];
import [Link];
import [Link];
class Student implements Comparable<Student>{ // Class 'Student' must either be declared abstract or implement abstract method
'compareTo(T)' in 'Comparable'
int age;
String name;
Student(int age, String name) {
[Link] = age;
[Link] = name;
}
@Override
public int compareTo(Student student) {
return [Link] - [Link];
}
@Override
public String toString() {
return age+ " " +name;
}
}
[Link](list); // uses compareTo(), or it will show error. First add 'implements Comparable for Student' to Student class
then add 'compareTo()'
// Java is saying: “Student does NOT implement Comparable in a way that matches what [Link]() needs.”
[Link]("Sorted by Age, using Comparable: "+list); // OUTPUT: Sorted by Age, using Comparable: [2 Aaakash
Ghorpade, 30 Shashank Ojha, 33 Satyam Singh]
}
}
import [Link];
import [Link];
import [Link];
import [Link];
class Student{
int age;
String name;
Student(int age, String name) {
[Link] = age;
[Link] = name;
}
@Override
public String toString() {
return age+ " " +name;
}
}
[Link]("Sorted by Name:");
[Link](list, sortByName);
[Link](list);
[Link]("Sorted by Age:");
[Link](list, sortByAge);
[Link](list);
/*
OUTPUT:
Sorted by Name:
[2 Aaakash Ghorpade, 33 Satyam Singh, 30 Shashank Ojha]
Sorted by Age:
[2 Aaakash Ghorpade, 30 Shashank Ojha, 33 Satyam Singh]
*/
}
}
Cursors
Enumeration Iterator ListIterator
Used for Legacy classes only (Vector, All Collection classes (List, Set, List implementations only
Hashtable) Queue, etc) (ArrayList, LinkedList)
Methods HasMoreElements(), HasNext(), next(), remove() HasNext(), next(),
nextElement() hasPrevious(), previous(), add(),
set(), remove()
Features Read-only (cannot remove Read + remove; one-directional Read + modify (add, remove,
elements); very old (from JDK → forward only; Failfast → set); bidirectional traversal
1.0); throws (forward + backward)
ConcurrentModificationExcepti
on
Utility class:
A utility class that provides static methods to operate on Collection objects (List, Set)
s
Collection Interface
Collection(I) → If we want to represent a group of individual objects as a single entity then we should go for Collection.
Collection interface defines the most common methods which are applicable for any collection object.
Methods Descrption
boolean add(Object o) Add an object or element
boolean addAll(Collection c) Add group of objects
boolean remove(Object o) Remove a particular pbject
boolean removeAll(Collection c) Remove group of objects
void clear() Remove all objects
boolean retainAll(Collection c) To remove all objects except those present in c
boolean isEmpty() Checks whether my Collection is empty or not
int size() No. of elements
boolean contains(Object o) Particular object is available or not
boolean containsAll(Collection c) Group of objects available or not
Object[] a = [Link](); Convert Collection to array
Iterator iterator() To get objects one by one
Collection interface does not contain any method to retrieve objects, there is no concrete class which implements collection class
directly.
List Interface
If we want to represent a group of individual objects as a single entity where duplicates are allowed and insertion order must be
preserved then we should go for List.
Methods Description
void add(int index, Object o) Insert object at specified index
boolean addAll(int index, Collection c) Add group od objects from the specified index
Object get(int index) Get element of a particular index
Object remove(int index) Remove element from the given index
Object set(int index, Object new) To replace the element present at specified index with provided
Object and returns old object
int indexOf(Object o) Returns index of first occurrence of ‘o’
int lastIndexOf(Object o) Returns index of last occurrence of ‘o’
ListIterator listIterator(); Bidirectional traversal
ArrayList
Heterogeneous objects are allowed, except TreeSet & TreeMap everywhere heterogeneous objects are allowed.
Constructors:
◦ ArrayList<Object> list = new ArrayList<>(); → No-arg Constructor
▪ creates an empty ArrayList object with default initial capacity = 10. Once ArrayList reaches its max capacity a new
ArrayList will be created with newCapacity = (oldCapacity * 3/2)+1. This is why ArrayList provides dynamic resizing.
ArrayList<Student> list2 = new ArrayList<>(list); // Contents of collection 'list2' are updated, but never queried
class ArrayListDemo {
public static void main(String[] args) {
ArrayList list = new ArrayList();
[Link]("A");
[Link](10);
[Link]("A");
[Link](null);
[Link](list);
[Link](2);
[Link](list);
[Link](2, "M");
[Link]("N");
[Link](list);
}
}
/*
OUTPUT
[A, 10, A, null]
[A, 10, null]
[A, 10, M, null, N]
*/
RandomAccess Interface
◦ RandomAccess is a marker interface in [Link] package. It contains no methods.
public interface RandomAccess { }
◦ It is used to mark a List implementation as supporting fast (generally O(1)) random access, i.e., fast indexing: [Link](i)
◦ ArrayList, Vector, and Stack classes implements RandomAccess interface, so that we can access any random element with the
same speed.
◦ Hence if our frequent operation is retrieval operation then ArrayList is the best choice.
class ArrayListDemo {
public static void main(String[] args) {
ArrayList list1 = new ArrayList();
LinkedList list2 = new LinkedList();
[Link](list1 instanceof Serializable);
[Link](list2 instanceof Serializable);
[Link](list1 instanceof Cloneable); // [Link] package
[Link](list2 instanceof Cloneable);
[Link](list1 instanceof RandomAccess);
[Link](list2 instanceof RandomAccess);
}
}
/*
OUTPUT
true
true
true
true
true
false
*/
ArrayList is best choice if our frequent operation is retrieval operation, because ArrayList implements RandomAccess interfaces.
ArrayList is the worst choice if our frequent operation is insertion oe deletion in the middle, because several shift operation are
require.
ArrayList Vector
Synchronization Not synchronized. Every method is synchronized.
(Every method present in ArrayList is non- (Most of the methods present in the
synchronized.) Vector are synchronized methods.)
Thread-safe At a time multiple threads are allowed to At a time only one thread is allowed to
operate on ArrayList object and hence operate on Vector object is thread-safe.
ArrayList is not thread safe.
Performance All threads allowed to operate Threads are required to wait to operate on
simultaneously → threads are not Vector object and hence relatively
required to wait to operate on ArrayList, performance is low.
hence relatively performaance is high
Growth Mechanism (Capacity Increment) When full → newCapacity = (oldCapacity When full,
* 3/2)+1 If no increment provided → newCapacity
= oldCapacity * 2 (double)
If increment provided → added as per
increment value
Legacy or Modern Class Introduced in Java 1.2 as a part of modern Introduced in Java 1.0 → considered
Collections framework. legacy → retrofitted to Collection
Framework in Java 1.2
Iterator type Uses Iterator Supports Ierator, as well as Enumeration
(old, legacy)
Multi-threading Uses in single-threaded programs, and Orginally designed for multi-threading. But
Multi-threading with external today, CopyOnWriteArrayList and
synchronization. ConcurrentHashMap are preferred.
Even though list is synchronized, when iterating you must manually synchronize:
synchronized(list) {
Iterator it = [Link]();
while([Link]()) {
[Link]([Link]());
}
}
Because iterator() is not thread-safe.
LinkedList
LinkedList is a linear data structure in Java that stores elements as nodes, where each node contains:
◦ Data
◦ Pointer/reference to the next node
◦ (In doubly linked list) Pointer to the previous node
Java’s LinkedList is a Doubly Linked List implementation and is part of [Link] package. → The underlying data structure is Doubly Linked List.
Features:
◦ Uses nodes instead of contiguous memory → no memory wastage
◦ Good for insertion/deletion at beginning or middle.
◦ Insertion order is preserved.
◦ Poor performance for searching (linear time).
◦ Duplicates are allowed.
◦ Heterogeneous Objects are allowed.
◦ Null insertion is possible.
◦ LinkedList implements Serializable and Clonable interfaces but not RandomAccess interface.
import [Link];
// Accessing elements
[Link]([Link](0)); // a
[Link]([Link]()); // a
[Link]([Link]()); // null
// Removing elements
[Link]();
[Link](list); // [A, Rahul, A, 1234, null]
[Link]();
[Link](list); // [Rahul, A, 1234, null]
[Link]();
[Link](list); // [Rahul, A, 1234]
[Link](2);
[Link](list); // [Rahul, A]
[Link]("A");
[Link](list); // [Rahul]
// Queue operations
[Link]("X"); // add element
[Link](list); // [Rahul, X]
[Link](); // removes head
[Link](list); // [X]
[Link]([Link]()); // view head i.e, X
//Deque operations
[Link]("Y");
[Link](list); // [Y, X]
[Link]("Z");
[Link](list); // [Y, X, Z]
[Link]([Link]()); // Y
[Link]([Link]()); // Z
}
}
Usually we can use LinkedList to implement Stack and Queue to provide support for this requirement LinkedList class defines
following specific methods.
void addFirst();
void addLast();
Object getFirst();
Object getLast();
Object removeFirst();
Object removeLast();
Constructors
LinkedList l = new LinkedList();
▪ creates an empty LinkedList Object
LinkedList l1 = new LinkedList(Collection c);
▪ creates an equivalent LinkedList Object for the given Collection
ArrayList LinkedList
Internal Data Structure Internally uses a dynamic array. Internally uses a doubly linked list.
Elements stored in contiguous memory. Elements stored as nodes, each node has
data, pointer to previous, and pointer to
next.
Accessing elements (Random Access) Very fast because it uses index-based Slow because traversal starts from
access. beginning or end. O(n)
Directly jumps to memory location. O(1)
Insertion/Deletion Slow for insertion/deletion in middle or Fast for insertion/deletion at beginning or
beginning. middle.
Because elements must be shifted. Just update node pointers.
Eg., Inserting at index 0 → shift everthing O(1) → for beginning/end
right O(n) O(n) → for middle (because traversal
needed)
Cache Performance Better cache locality (contiguous memory) Poor cache locality (scattered nodes)
→ Faster iteration
Vector
Introduced in JDK 1.0. Since Java 2 (JDK 1.2), Vector was updated to implement the List interface.
Similar to an array: elements are accessed using an integer index. Vector implements RandomAccess interface.
Unlike arrays, the size of Vector can dynamically grow or shrink based on additions or removals.
Vector is synchronized by default (most of the methods present in Vector are synchronized) → thread-safe. However, due to
synchronization overhead:
◦ If thread-safety is not required, prefer ArrayList over Vector.
Methods
import [Link];
[Link]([Link]()); // 10
[Link]("A");
[Link]([Link]()); // 20
[Link](v); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, A]
}
}
import [Link];
[Link]
↳ [Link]<E>
↳ [Link]<E>
↳ [Link]<E>
↳ [Link]<E>
Stack inherits Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, and RandomAccess interfaces.
Extends Vector,meaning:
◦ It is synchronized.
◦ Allows null, duplicates, insertion order, and heterogeneous objects (although generics are recommended).
Constructor:
◦ Stack s = new Stack();
▪ Creates an empty Stack.
▪ Internally uses Vector’s default constructor.
▪ Initial capacity of the underlying Vector = 10 (inherited from Vector).
▪ The stack grows dynamically as elements are pushed.
▪ After creation, the stack contains no items.
Methods
import [Link];
In Java, a cursor is an object used to traverse (iterate) through the elements of a collection.
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
int previousIndex()
import [Link];
import [Link];
import [Link].*;
class CursorDemo {
public static void main(String[] args) {
Enumeration e = [Link]();
Iterator itr = [Link]();
ListIterator ltr = [Link]();
[Link]([Link]().getName()); // [Link]$1
[Link]([Link]().getName()); // [Link]$Itr
[Link]([Link]().getName()); // [Link]$ListItr
}
}
Java uses inner classes to implement cursor logic:
◦ Vector$1 → Anonymous inner class implementing Enumeration
◦ Vector$Itr → Inner class implementing Iterator
◦ Vector$ListItr → Inner class implementing ListIterator
Set (I)
Collection(I) 1.2v
|
|
____________________________|________________________
| | |
| | |
List(I) Set(I) 1.2v Queue(I) 1.5v
|
|
_______|______
| |
| |
HashSet 1.2v SortedSet(I) 1.2v
| |
| |
LinkedHashSet NavigableSet(I) 1.6v
1.4v |
|
TreeSet 1.2v
Set interface doesn’t contain any new methods. SO we have to use only Collection interface methods.
HashSet
HashSet is a class in Java that implements the Set interface and stores elements using a hash table.
Duplicates are not allowed. If we are trying to insert duplicates, we won’t get any compile-time or run-time errors. add() method
simply returns false.
Insertion order is not preserved and all objects will be inserted based on hash-code of objects. (No guaranteed order - elements
appear in random order)
Constructors (constructors are common for HashSet, LinkedHashSet, HashMap, IdentityHashMap, WeakHashMap, Hashtable)
◦ HashSet h = new HashSet();
▪ Creates an empty HashSet. Internally creates a HashMap with default initial capacity = 16, and default load factor = 0.75.
▪ What does load factor mean?
HashSet grows when it becomes 75% full. Means 16*0.75=12 → When 12 elements are inserted, HashSet resizes to
maintain performance.
import [Link];
LinkedHashSet is a Set implementation that does not allow duplicates, maintains insertion order, uses hashtable + doubly linked
list, and allows one null value. (It is a child class of HashSet, but it adds a linked list to preserve order)
Internally it uses HashMap for fast search, and doubly linked list for remembering insertion order.
import [Link];
import [Link];
LinkedHashSet is the best choice to develop cache based applications, where duplicates are not allowed and insertion order must be
preserved.
SortedSet
If we want to represent a group of individual objects according to some sorting order and duplicates are not allowed then we should
go for SortedSet.
Methods of SortedSet:
first() Returns smallest element
last() Returns largest element
handSet(toElement) elements < toElement
tailSet(fromElement) elements >= fromElement
subSet(from, to) fromElement <= elements < toElement
comparator() Returns custom comparator (or null if natural ordering)
Default natural sorting order for numbers – ascending order and for String – alphabetical order.
We can apply the above methods only on SortedSet implemented class objects. That is on the TreeSet object.
TreeSet
TreeSet is implemented using a TreeMap, and TreeMap uses a Red-Black Tree, which is a type of self-balancing BST. So TreeSet
always keeps elements sorted.
Elements are stored according to sorting order, not according to the order of insertion (Insertion order NOT preserved). Order is
natural ordering (ie., integers ascending) or custom ordering (Comparator).
TreeSet does NOT allow null. Before Java 7, TreeSet allowed a single null when using a custom Comparator. Java 7+, natural ordering
does NOT allow null. Adding null throws NullPointerException.
TreeSet<String> set = new TreeSet<>();
[Link](null); // ❌ NullPointerException
s
Even numbers elements get filtered and its square is stored
Map ----> stream, map, collect
Filter -----> stream, filter, collect
Sorted ----> stream, sorted, collect
Filter names that start with 'J', have at least length 4, convert to uppercase, and sort alphabetically using Java Streams.
import [Link].*;
import [Link].*;
[Link](result);
}
}
[JACK, JANE, JOHN]
The Strategy Pattern lets you define a family of algorithms, put each one in a separate class, and make them interchangeable at
runtime. It helps achieve loose coupling by depending on abstraction (interface) instead of concrete implementations.
Loose and Tight Coupling
Loose coupling and tight coupling describe how strongly different parts (modules, classes, services, systems) depend on each other
—commonly used in software engineering, systems design, and even organizations.
Tight Coupling
◦ Components are highly dependent on each other.
◦ Characteristics
▪ One component knows a lot about another
▪ Changes in one part often force changes in others
▪ Harder to test, modify, or reuse
▪ Faster to build initially, but harder to maintain
◦ Eg.,
class Car {
Engine engine = new Engine(); // directly creates Engine
}
▪ Here, Car is tightly coupled to Engine. If Engine changes, Car may break.
Loose Coupling
◦ Components are minimally dependent on each other.
◦ Characteristics
▪ Components interact through interfaces or contracts
▪ Changes in one component don’t affect others much
▪ Easier to test, replace, scale, and maintain
▪ Preferred in large systems and microservices
◦ Eg.,
class Car {
Engine engine;
Car(Engine engine) {
[Link] = engine;
}
}
▪ Now Car depends on an interface or abstraction, not a specific Engine.
/*
OUTPUT
Notification: Hello Satyam, your action was successful
*/
// loose
NotificationService notificationService = new EmailNotificationService();
[Link] userService1 = new [Link](notificationService);
[Link]();
}
}
/*
OUTPUT
Email notification: Hello Satyam, your action was successful
*/
s
Spring Initializr
[Link] (boiler plate code) -> Online tool (and API) provided by the Spring team to help developers bootstrap a new Spring Boot
project quickly — without manual setup.
Boilerplate (or boilerplate code) refers to standard, reusable code or text that can be copied and used with minimal modification in
multiple places.
It generates a ready-to-run Spring Boot project skeleton with the structure, dependencies, and configuration files already in place.
Structure description:
◦ Project
▪ Maven
uses [Link]
dependency management via XML
▪ Gradle – Groovy
uses [Link] (Groovy DSL)
faster builds than Maven
▪ Gradle – Kotlin
uses [Link]
type-safe build scripts
◦ Language
▪ Java (standard choice)
▪ Kotlin
▪ Groovy (mostly used in testing)
◦ Spring Boot version
▪ 3.5.7 (✅ Stable – Recommended)
▪ SNAPSHOT → Development version
▪ RC → Release Candidate (testing phase)
▪ ❌ Never use SNAPSHOT in production
▪ ✅ Always choose latest stable
◦ Project Metadata
▪ Group
Acts like company / organization name
Base for package structure
eg.,[Link] , [Link]
▪ Artifact
Jar/War file name
Also project folder name
eg., [Link]
▪ Name
Application display name
Usually same as artifact
▪ Description
Only for documentation
No runtime impact
▪ Package name
Base Java package
All classes go under this
eg.,[Link] , [Link]
▪ Packaging (JAR by default)
JAR (Java ARchive): Packages a standalone Java application (desktop, CLI, or microservice). Use case:
Microservice/REST API, Cloud-native deployment (Docker, Kubernetes)
WAR (Web Application ARchive): Packages a web application (servlets, JSP, [Link]).
▪ Configuration
Properties ([Link]=8081)
YAML (server: port: 8081)
▪ Java (choose version)
◦ Select dependencies (like Web, JPA, Security, etc.)
▪ In a Java/Spring Boot project, these dependencies provide pre-built features — like web servers, databases, logging, or
security — so you don’t have to code everything from scratch.
Want to build REST APIs? ➜ spring-boot-starter-web
Need to connect to a database? ➜ spring-boot-starter-data-jpa
Download a ZIP file with pre-configured project structure
PROJECT STRUCTURE
test → testing is considered very important in Spring Boot. The test folder is a mirror image of your main code, containing all your
unit and integration tests, resources, and configurations for testing.
[Link] → configuration
Bean
A Bean is simply an object that is managed by the Spring IoC (Inversion of Control) container. You don’t manually create it using new;
Spring does it for you.
Any Java object that Spring creates, configures, and manages for you is called a Spring Bean.
Dependency Injection
giving an object its dependencies (objects it needs) from outside — instead of creating them itself.
▪ Now, we put @Component above class. @Component tells Spring to make this class a Bean.
RazorpayPaymentService → a service class that handles payments.
InternalWorkingOfSpringBootApplication → our main class that depends on RazorpayPayment
Example Code
2. Field
Injection
Inject dedpendency directly to a field.
You let Spring directly inject the dependency into a field (variable) of your class using the @Autowired annotation — without using
a constructor or setter.
How it works?
◦ Spring Boot starts → creates the ApplicationContext.
◦ It finds @Component classes: RazorpayPaymentService
◦ Creates Bean of RazorpayPaymentService.
◦ Finds @Autowired field inside InternalWorkingOfSpringBootApplication.
◦ Injects the already-created RazorpayPaymentService Bean into that field.
Example
StripePaymentService implements a PaymentService interface, and it’s doing business logic (processing a payment) — so the correct
stereotype here would be using @Service here (instead of @Controller)
Example
Here, we are facing ambiguity issue, because Spring sees two Beans that implement PaymentService: RazorpayPaymentService and
StripePaymentService.
Solution: If we remove @Component (or @Service) from one class, only the remaining annotated class will be treated as a Bean..
@ConditionalOnProperty
Is a Spring Boot conditional annotation used to enable or disable a bean based on a property value in the application configuration
file (like [Link] or [Link]).
When you set debug=true, Spring Boot prints auto-configuration reports and condition evaluation logs to the console while starting the
application.
It helps you see:
Which beans are being created ✅
Which beans are not created ❌ (and why)
Which @ConditionalOnProperty conditions matched or did not match
While running the app with the following code, you’ll see logs.
1. In POJO Classes, these are your Java classes that perform specific logic – StripePaymentService and RazorpayPaymentService. Each class is
typically annotated with @Component, @ConditionalOnProperty(…), so they can become beans depending on configuration.
2. Component Scanning: Spring Boot automatically scans packages (using @ComponentScan or the default package of your main class). It
detects classes annotated with @Component, @Service, @Repository, @Controller, etc.
When detected, they become eligible beans for the IoC container (Inversion of Control Container).
3. IoC Container is the core of Spring. It is responsible for: creating beans, managing their lifecycles, and injecting dependencies (via
constructor, setter, or field injection.)
IOC Container
Application Context
Component Scanning
AutoConfiguration
pointcut: A pointcut is an expression that matches one or more join points. Defines which methods or classes your advice should apply to.
A join point is a specific point in your program’s execution — for example: A method being called, An exception being thrown, Or a
constructor being executed.
16 November
execute
executeUpdate
executeQuery
ResultSSet
Spring Annotations
- ResponseHandler