Java Notes Professional
Java Notes Professional
JAVA
Complete Study Notes
From Basics to Advanced — A Structured Reference Guide
By Jatin Sir
Professionally Formatted Edition
Page 1 of 214
Java Complete Study Notes | By Jatin Sir
Page 2 of 214
Java Complete Study Notes | By Jatin Sir
01_Java’s_Story :
Java's Story :
"Write Once, Run Anywhere” (WORA) - The promise that made Java a legend!
Java's official slogan - introduced by Sun Microsystems when Java was launched in the
mid-1990s.
What it means?
We can write Java code on one platform (say, Windows), compile it to bytecode, and then run it
anywhere — Linux, macOS, embedded systems — as long as a
Java Virtual Machine (JVM) exists there.
We don’t need to rewrite our code for each OS.
Facts:
Created by James Gosling at Sun Microsystems in 1995.
Originally called Oak, renamed to Java (inspired by coffee!)
Platform-independent thanks to the JVM (Java Virtual Machine)
Runs billions of devices — from Android apps to ATMs!
Major Java Version Highlights!
| Version
| Year
| Major Highlights
| Java 10–14. | 2018–2020 | var keyword, Switch Expressions, Text Blocks
JAVA'S main components !
| Component | Role
JVM - Java Virtual Machine
• Runs the compiled .class bytecode.
• Converts bytecode to machine code line by line.
• Part of JRE.
JRE - Java Runtime Environment
• JRE + development tools (javac, debugger, etc).
• Used to write and compile Java programs.
Java Execution Flow !
[Link]("Hi, I'm Java!");
What is a Data Type?
A data type defines HOW MUCH MEMORY is required to store data and what kind of data a
variable can hold — such as numbers, characters, or logical values.
Page 3 of 214
Java Complete Study Notes | By Jatin Sir
Page 4 of 214
Java Complete Study Notes | By Jatin Sir
Page 5 of 214
Java Complete Study Notes | By Jatin Sir
Page 6 of 214
Java Complete Study Notes | By Jatin Sir
Memory (4 bytes)
int tells Java to reserve 4 bytes in RAM.
The value 25 will be stored in that 4-byte memory block.
The variable name age acts as a pointer to the reserved memory where the data (like 25) is kept.
04_Literals
What is a Literal?
A literal is a constant value written directly in the code.
It represents a fixed value assigned to a variable.
Notes
Literals are used with primitive data types like int, float, char, boolean.
null is used with non-primitive types (like objects or Strings).
Each literal tells the compiler what kind of value is being assigned.
Example in Code:
int score = 88;
// Integer Literal
float pi = 3.14f;
// Character Literal
// String Literal
// Null Literal
Page 7 of 214
Java Complete Study Notes | By Jatin Sir
└── javaprograms
└── [Link]
• A PACKAGE in Java is like a FOLDER on our computer.
Just like a folder can contain many files, a package can contain many Java classes (files).
• Helps in:
• Organizing code
• Avoiding name conflicts
• Controlling access
Syntax to declare a package - package javaprograms
package is a RESERVED KEYWORD in Java.
Package names should be written in LOWER CASE by convention.
Java Program Structure
A basic Java class looks like this:
public class Example {
public static void main(String[] args) {
// logic here
}
}
Understanding the Block Structure
Class starts here
public class HelloWorld {
public static void main(String[] args) {
[Link]("Hello, Java!");
} // Main method ends here
} // Class ends here
Can We Create a private Class?
In Java:
Page 8 of 214
Java Complete Study Notes | By Jatin Sir
public class A {
}
public class B {
}
Summary
Place .java files under src/package_name/.
Always start the file with a package declaration (unless in the default package).
Only one public top-level class is allowed per .java file.
Class name must match the filename when using public.
Q: Why does Java allow only one public class per .java file? What happens if we try to define
two public classes in the same file?
Java enforces a one-to-one relationship between the public class and the file name for clarity,
maintainability, and proper class loading by the JVM.
• If two public classes are defined in the same .java file, the compiler will throw an error: The
public type X must be defined in its own file.
• Only one class can be public, and its name must match the file name.
Understanding Code Execution
Step-by-Step Breakdown of Java Code
Line 1: package example1;
// package name
Line 3:
public static void main(String[] args) { // main method opening brace
Line 4:
method
int a = 20;
Line 5:
long g = 2000;
Page 9 of 214
Java Complete Study Notes | By Jatin Sir
Line 6:
char gender = 'm';
Line 7:
float percentage = 33.23f;
Line 8:
boolean isPercentage = false;
Line 9:
[Link](a);
Line 10:
Line 11: }
}
Line-by-Line Execution:
Line 1: package Example1;
Declares that this class belongs to the example1 package, which helps organize related Java
files together in a structured way (like folders).
Page 10 of 214
Java Complete Study Notes | By Jatin Sir
| age = 25
+-----------+
Memory (4 bytes)
int tells Java to reserve 4 bytes in RAM.
The value 25 will be stored in that 4-byte memory block.
The variable name age acts as a pointer to the reserved memory where the data (like 25) is kept.
04_Literals
What is a Literal?
A literal is a constant value written directly in the code.
It represents a fixed value assigned to a variable.
Notes
Literals are used with primitive data types like int, float, char, boolean.
null is used with non-primitive types (like objects or Strings).
Each literal tells the compiler what kind of value is being assigned.
Example in Code:
int score = 88;
// Integer Literal
float pi = 3.14f;
// Character Literal
// String Literal
// Null Literal
Page 11 of 214
Java Complete Study Notes | By Jatin Sir
// logic here
}
}
Understanding the Block Structure
Class starts here
public class HelloWorld {
public static void main(String[] args) {
[Link]("Hello, Java!");
} // Main method ends here
} // Class ends here
Can We Create a private Class?
Page 12 of 214
Java Complete Study Notes | By Jatin Sir
In Java:
public class A {
}
public class B {
}
Summary
Place .java files under src/package_name/.
Always start the file with a package declaration (unless in the default package).
Only one public top-level class is allowed per .java file.
Class name must match the filename when using public.
Q: Why does Java allow only one public class per .java file? What happens if we try to define
two public classes in the same file?
Java enforces a one-to-one relationship between the public class and the file name for clarity,
maintainability, and proper class loading by the JVM.
• If two public classes are defined in the same .java file, the compiler will throw an error: The
public type X must be defined in its own file.
• Only one class can be public, and its name must match the file name.
Understanding Code Execution
Step-by-Step Breakdown of Java Code
Line 1: package example1;
// package name
Line 3:
public static void main(String[] args) { // main method opening brace
Page 13 of 214
Java Complete Study Notes | By Jatin Sir
Line 4:
method
int a = 20;
Line 5:
long g = 2000;
Line 6:
char gender = 'm';
Line 7:
float percentage = 33.23f;
Line 8:
boolean isPercentage = false;
Line 9:
[Link](a);
Line 10:
Line 11: }
}
Line-by-Line Execution:
Line 1: package Example1;
Declares that this class belongs to the example1 package, which helps organize related Java
files together in a structured way (like folders).
Package names should always be in lowercase — this is a Java naming convention to avoid
conflicts and keep things consistent.
Line 2: public class Example1 {
Defines a Java class named Example1.
The keyword CLASS tells the compiler this is a class definition.
Line 3: public static void main(String[] args) {
This is the starting point of the program.
Java execution begins with the main() method.
• main() must be PUBLIC STATIC or JVM won't start execution.
•
Page 14 of 214
Java Complete Study Notes | By Jatin Sir
Stack Memory
Page 15 of 214
Java Complete Study Notes | By Jatin Sir
+---------------------------+
|
main()
Top <----------------------+
| +---------------------+ |
| | isPercentage: false | |
| | percentage: 33.23f | |
| | gender: 'm'
| |
| | g: 2000L
| |
| | a: 20
| |
| +---------------------+ |
Page 16 of 214
Java Complete Study Notes | By Jatin Sir
+---------------------------+
The **'top of the stack'** specifically points to the **current method* being executed.
Once the main() method finishes execution, all local variables that were pushed onto the
stack
are popped out — clearing the stack memory and making room for the next method call.
Line 8: boolean isPercentage = false;
A boolean variable stores either true or false.
Stored in the stack like the others.
Line 9: [Link](a);
Prints the value of variable a to the console.
When the method ends, all the local variables created inside the stack are removed and
stack is
cleared, and he program ends.
Line 11: } // class closing brace
Page 17 of 214
Java Complete Study Notes | By Jatin Sir
In Java, local variables (declared inside methods) are not automatically initialized.
Because the compiler can't guarantee what value a hold — it could be garbage or random —
and
Java wants to avoid unsafe behavior.
Page 18 of 214
Java Complete Study Notes | By Jatin Sir
// Syntax
if (condition) {
block of code to execute if condition is true
}
How does it works ?
If the condition is true, Java executes the immediate next statement or code block.
If the condition is false, Java skips the next statement/block.
Example 1: Single Statement
int c = 100;
Page 19 of 214
Java Complete Study Notes | By Jatin Sir
if (c > 50)
[Link]("C is greater than 50");
Example 2: Code Block with Multiple Lines
if (c > 5) {
[Link]("Hello");
[Link]("How are you");
[Link]("Are you learning java");
}
Example 3: False condition without braces
int c = 10 + 5;
if (c > 10) // False
[Link]("C is lesser than 10"); // skipped
[Link]("C is greater than 10"); // This is outside the if - always executed
Java's Rule for if Without Braces {}
When we write an if statement without {}, only the immediate next line (the first one) is treated
as part of the if.
Page 20 of 214
Java Complete Study Notes | By Jatin Sir
↓
Execute next statement(s)
if (c > 10)
False
↓
Skip the next statement(s)
2. if else
The if-else statement lets you choose between two blocks of code based on whether the
condition is true or false**
Syntax :
if (condition) {
} else {
}
Example:
int a = 10;
int b = 20;
if (a > b) {
[Link]("a is greater");
} else {
[Link]("b is greater");
}
Output:
Since a > b is false, the output will be: b is greater
Key Concept:
The if block runs if the condition is true
The else block runs if the condition is false
Only one of the two blocks will execute
Tip
Used when you have exactly two outcomes.
Avoid using else without if — they always work as a pair
3. if-else-if Ladder
The if-else-if ladder is used when you need to check multiple conditions (sequential checks),
one after another.
Page 21 of 214
Java Complete Study Notes | By Jatin Sir
It allows you to choose one block out of many based on which condition is true first.
Syntax:
if (condition1) {
} else if (condition2) {
} else if (condition3) {
} else {
}
Example
int percentage = 75;
if (percentage > 100 || percentage < 0) {
grade = 'A';
} else if (percentage >= 80 && percentage < 90) {
grade = 'B';
} else if (percentage >= 70 && percentage < 80) {
grade = 'C';
} else if (percentage < 70) {
grade = 'F'; // Handles rest of values <70
} else {
Page 22 of 214
Java Complete Study Notes | By Jatin Sir
// code block
break;
case value2:
// code block
break;
default:
}
Key Points
switch
Starts the block that checks different values
case
Each possible value to match
break
Exits the switch after a match is found
default
(Optional) Runs if no case matches
Example: Switch Based on Grade
char grade = 'B';
switch (grade) {
case 'A':
Page 23 of 214
Java Complete Study Notes | By Jatin Sir
Chapter 5: Loops
for, while, do-while, Enhanced For Loop
[Link]("Excellent!");
break;
case 'B':
[Link]("Very Good!");
break;
case 'C':
[Link]("Good");
break;
case 'D':
[Link]("You passed");
break;
case 'F':
[Link]("Better luck next time");
break;
default:
[Link]("Invalid grade");
}
How It Works?
If grade = 'B'?
Java checks:
(1) Is it 'A'? No
(2) Is it 'B'? Yes → prints "Very Good!" and exits
Note
Works with byte, short, int, char, String, and enums (not float, double).
Without break, all below cases will execute ("fall-through").
Loops
What is a Loop?
A loop allows us to execute a block of code MULTIPLE TIMES based on a condition.
Instead of writing the same lines again and again, loops make code shorter, efficient, and
less error-prone.
Why Do We Need Loops?
Imagine we want to print a name 100 times, without a LOOP, we have to write -
[Link]("Athira");
[Link]("Athira");
[Link]("Athira");
Page 24 of 214
Java Complete Study Notes | By Jatin Sir
}
How to define a for loop?
for (int i = 1; i <= 2; i++) {
Page 25 of 214
Java Complete Study Notes | By Jatin Sir
[Link]("Java");
}
OUTPUT:
i <= 2. > so output is printed twice!
java
Java
Breakdown of Syntax:
int i = 1; → Initialization (loop starts from 1)
i <= 5; → Condition (loop runs while this is true)
i++ → Updation (i is incremented by 1 after every loop)
What is i++?
i++ means increment i by 1 after each loop cycle.
It’s the same as writing: i = i + 1;
Example: i++ gives 1, 2, 3, 4, 5
Example: i-- gives 5, 4, 3, 2, 1
Looping Variable
"i" is called the LOOPING VARIABLE or CONTROL VARIABLE.
We can name it anything (e.g., j, count, etc.)
Scope Reminder:
The VARIABLE i is accessible only inside the loop.
If we try to use i outside the loop, it results in a COMPILE TIME ERROR.
ERROR: cannot resolve symbol 'i'.
Post vs Pre Increment & Decrement
| Expression | Step 1
| Step 2
Final x Final i
Summary
Post-Increment (i++): Use the value first, then increment.
Pre-Increment (++i): Increment first, then use the updated value.
Post-Decrement (i--): Use the value first, then decrement.
Pre-Decrement (--i): Decrement first, then use the updated value.
Reverse For Loop
When we want to loop backwards, such as printing values from 5 to 1, use reverse for loop.
for (int i = 5; i >= 1; i--) {
// Here, i-- decreases the value of i in each iteration until the condition i >=
Page 26 of 214
Java Complete Study Notes | By Jatin Sir
1 becomes false.
[Link](i);
}
For Loop Without Condition
Gives an INFINITE LOOP as an EXIT CRITERIA is not mentioned.
If we don’t use break, this loop will run forever!
for (int i = 1; ; i++)
For Loop With Multiple Variables
We can declare and update multiple variables in a for loop.
for (int i = 1, j = 5; i <= 5; i++, j--) {
[Link]("i = " + i + ", j = " + j);
}
i = 1, j = 5
i = 2, j = 4
i = 3, j = 3
i = 4, j = 2
i = 5, j = 1
Enhanced For Loop (For-Each)
Used when we just want to read elements of an array/collections one by one, without using an
index.
int[] numbers = {1, 2, 3, 4, 5}; // numbers is an array which holds 5 integer values: 1, 2, 3, 4, 5.
for (int num : numbers) {. // num is a loop variable or Element variable — it temporarily holds
each element of the array numbers during every iteration.
[Link](num);
}
When to Use For Loop?
When NUM OF ITERATIONS is KNOWN.
Great for counting, printing patterns, or accessing array indexes.
Concept Check:
for (int i = 1; i <= 5; i++) {
[Link]("i = " + i);
}
Total Executions - 5
Condition checked - 6 times (n + 1)
In a for loop, if it runs n times, the condition is checked n + 1 times.
Key Points:
Before the loop starts:
Page 27 of 214
Java Complete Study Notes | By Jatin Sir
Java first checks the condition to decide if the loop should even begin.
During the loop:
After each iteration finishes, Java checks the condition again to see if another round should run.
When the loop ends:
Finally, Java checks the condition one more time, sees it’s false, and then stops the loop.
So, for n loop runs, the condition is checked n + 1 times.
While
The while loop is used to execute a block of code repeatedly as long as the condition is true.
Syntax:
while (condition) {
}
Flow
Condition is checked first
If true → executes the body
If false → exits the loop immediately
Example 1: Print numbers from 1 to 5
int number = 1;
while (number <= 5) {
[Link](number);
number = number + 1;
}
While Excecution:
| Step | number Value | Condition number <= 5 | Action
📌 Note: Note:
The loop runs as long as the condition is true.
If the condition is false at the start, the loop block won’t run even once.
Infinite loop
int number = 1;
while (number <= 5) {
[Link](number);
}
What Happens?
number stays 1.
Page 28 of 214
Java Complete Study Notes | By Jatin Sir
❌
for (int i = 1; i <= 5; i++) {
int x = 10;
[Link](x + i);
}
Always define the variable outside the loop:
✅
int x = 10;
// Defined once,
Page 29 of 214
Java Complete Study Notes | By Jatin Sir
do {
// Code to execute
} while (condition);
Key Points
The loop executes the block first, then checks the condition.
So, even if the condition is false the first time, the block still executes once.
Often used when you want to take input from the user at least once.
Example
int i = 1;
do {
[Link]("i = " + i);
i++;
}
while (i <= 5);
Output
i=1
i=2
i=3
i=4
i=5
Difference Between while and do-while
When While Condition is Initially False
!
int i = 10;
while (i < 5) {
[Link](i); //
✅ This will print once
i++;
}
Excecution:
The do block runs once no matter what.
i starts at 10.
After printing, i++ makes it 11.
Then while(i < 5) is false → so it exits.
Q1. What is a Loop?
A loop is used to execute a block of code repeatedly until a specified condition is met.
Page 30 of 214
Java Complete Study Notes | By Jatin Sir
// break example
Page 31 of 214
Java Complete Study Notes | By Jatin Sir
Page 32 of 214
Java Complete Study Notes | By Jatin Sir
}
Q19. Why is it not advisable to declare variables inside a
loop?
A new variable is created in every iteration, wasting memory.
for (int i = 1; i <= 5; i++) {
Page 33 of 214
Java Complete Study Notes | By Jatin Sir
Page 34 of 214
Java Complete Study Notes | By Jatin Sir
Page 35 of 214
Java Complete Study Notes | By Jatin Sir
Chapter 6: Methods
Method Definition, Parameters, Overloading, Recursion
while (true) {
if (i == 4)
break;
[Link]("Value is: " + i);
i++;
}
Q25. What will be printed?
int i = 0;
while (i < 3) {
i++;
if (i == 2)
continue; // Skips printing when i = 2
[Link]("i = " + i);
}
Mistakes to avoid in Loops
| Mistake
| Example
| Fix
the condition
loop body
{} for clarity
When To Use Which Loop
| Loop
| When to Use
| Common Use Case
Methods
What is a Method?
A method is a code block designed to perform a specific task. It is like an instruction that tells
the program to do something specific, like an action.
Think of it like giving a command to do
something:
print the bill
calculate total
Login with credentials
How to define a method structure ?
Page 36 of 214
Java Complete Study Notes | By Jatin Sir
}
What is a Method Signature?
Method Signature is - Method Name + Return Type + Parameter List (argument1, argument2)**
int addNumbers(int a, int b)
(argument1, argument2)
↑↑
↑
↑
Return | Argument1 Argument2
Type | (local var) (local var)
Method Name
Page 37 of 214
Java Complete Study Notes | By Jatin Sir
void.
void is a reserved keyword in java.
public void printWelcome() {.
Page 38 of 214
Java Complete Study Notes | By Jatin Sir
Taking screenshots
Connecting to a database
Method Excecution in
java
Let's understand the method excecution with the help of a program - Calculator App!
What Do We Need for a Calculator
App?
4. Declare variables with the appropriate type (like `int` or `double`) to store the numbers we
want to calculate.
2. Define a variable to store the result.
5. We'll define these variables **inside the `main()` method, so they become local variables —
stored in **stack memory.
4. Next, we need to create methods like add() and subtract() to perform our calculations.
6. We’ll pass the LOCAL VARIABLES (holding the numbers) as arguments to these methods
so
they can use them.
CalculatorApp
1 public class CalculatorApp {
2
public static void main(String[] args) { // Execution starts here
3
4
double num1 = 100; // Initialization → num1 is a local variable stored in stack
double num2 = 10; // Initialization → num2 is also stored in stack
5
double result;
6
double sum = getCalculatedSum(num1, num2);
// Java searches the method "getCalculatedSum" inside the method first and then inside
Page 39 of 214
Java Complete Study Notes | By Jatin Sir
9
10
11
12
13
14
15
16
17
18
19 }
[Link](diff); // Prints: 90.0
}
public static double getCalculatedSum(double num1, double num2) {
// Control from line 6 reaches here → this method is pushed to the stack
// The result (110) is returned to result is returned back to the caller // line 6 and
assigned to variable 'sum'
// Control from line 8 reaches here → this method is now pushed to stack
Page 40 of 214
Java Complete Study Notes | By Jatin Sir
return result;
Page 41 of 214
Java Complete Study Notes | By Jatin Sir
+----------------------------------+
top in stack means - This is the method currently
Calling getCalculatedDiff(num1, num2)
• Line 8
At line 15, java calls the method getCalculatedDiff() - Method call
Java search for the method inside the method first and then inside the class
Control goes to Line 15 and the getCalculatedDiff () is pushed to top of the stack
Values of num1 and num2 from the main are passed into this method.
Result is calculated and returned to the caller method - Line 9
Once the method completes, it is popped off (removed) from the stack and control goes to Line
8.
+----------------------------------+
| getCalculatedDiff()
| ← top** top in stack means - This is the method currently
executing
| num1 = 100
+----------------------------------+
| main()
+----------------------------------+
Final Stack (Just Before Program
Ends)
+------------------------------+
| main()
| ← top
| num1 = 100
In final stack, sum and diff store actual results,but "result" in main() still holds garbage bcs
nothing was assigned to it.
| sum = 110
+------------------------------+
All lines in main() executed
No more methods to call
At this point:
• All local variables (num1, num2, sum, diff, result) are cleared from memory.
• The main() method is popped off the stack.
• Stack becomes empty.
• The Java program ends gracefully.
Wrapper Class in
Java
What is a Wrapper Class? Why Do We
Page 42 of 214
Java Complete Study Notes | By Jatin Sir
Need It?
Think of this: You're on an international trip to Amsterdam .
You carry local currency, but to shop or dine abroad, you need Euros (€) — the local currency
there.
So, you convert your local currency to Euros to be able to function smoothly during your trip.
In Java, it's similar - Java is an object-oriented language, and while primitive types like int, char,
and boolean are simple and fast (like local currency), they cannot be used in places where
objects (Euros) are required.
Since Java Collections (like ArrayList, HashMap) and many utility classes and APIs are
designed to work with objects, primitives like int, char, and boolean cannot be used directly
in those contexts.
That’s where Wrapper Classes come in — they act like a currency converter , WRAPPING
THE PRIMITIVE into an OBJECT form so it can be used where only objects are allowed.
Analogy Summary:
| Concept
| Example | Analogy
How Can We Define a Wrapper Class?
A Wrapper Class is an object representation of a primitive type.
Primitive vs Wrapper Types
Wrapper Class Features
int num = 10;
// Primitive
Page 43 of 214
Java Complete Study Notes | By Jatin Sir
Yes
❌
❌
❌
❌
✅
✅
✅
✅
Visual Comparison
| primitive | int num1 = 10;
Wrapper | Integer num2 = 10;
// List
// Set
// Map
Page 44 of 214
Java Complete Study Notes | By Jatin Sir
// Queue
// Stack
// PriorityQueue
// Deque
Page 45 of 214
Java Complete Study Notes | By Jatin Sir
// int
int subjects = 6;
// int
Page 46 of 214
Java Complete Study Notes | By Jatin Sir
when is it needed?
Explicit casting is when we manually convert one data type to another using (type) syntax —
especially when automatic conversion doesn't happen or we want more control.
We tell Java exactly what type to convert to by writing the type in parentheses.
When we explicitly cast totalMarks from int to double, this forces Java to treat totalMarks as a
double (455.0) before doing the division.
int totalMarks = 455;
int subjects = 6;
// Implicit Conversion
double average1 = totalMarks / subjects; // Here, Java performs integer division: 455 / 6 → 75
(decimal .833 is lost)
// Explicit Casting
double average2 = (double) totalMarks / subjects;// Explicit Casting - Here, we are telling java,
please treat totalMarks as a double before doing the math.
[Link]("Implicit: " + average1); // Output: 75.0
[Link]("Explicit: " + average2); // Output: 75.833333...
What happens Internally ?
double average2 = (double) totalMarks / subjects
Here, (double) totalMarks is a double operand. One operand is double, so Java promotes the
other (subjects = 6) to double automatically.
So the final division becomes: 455.0 / 6.0 → 75.833333...
The result is stored in average2, which is a double.
Method Overloading
Method Overloading means creating multiple methods within the same class with
the same name, but with different parameters.
How Java Decides Which Method to
Call in method overloading?
Java looks at:
(1) The number of parameters
(2) The data types of parameters
(3) The order of data types in parameters**
This decision happens at compile time
Calculator - Using Method Overloading
01 public class CalculatorApp {
02 public static void main(String[] args) { / Excecution starts here
Page 47 of 214
Java Complete Study Notes | By Jatin Sir
03
int a = 10;
04
int b = 20;
05
int c = 30;
06
double d = 5.5;
07
// Method calls
08
09
10
11
12
addNumbers(a, b);
addNumbers(a, b, c);
addNumbers(d, b);
// Call 1
// Call 2
// Call 3
13
14
15
16
Page 48 of 214
Java Complete Study Notes | By Jatin Sir
17
18
19
private static void addNumbers(int x, int y) {
[Link]("Sum (int + int): " + (x + y));
}
Page 49 of 214
Java Complete Study Notes | By Jatin Sir
Page 50 of 214
Java Complete Study Notes | By Jatin Sir
Chapter 7: Arrays
Single & Multi-Dimensional Arrays, Traversal
│
├── Line 9: addNumbers(10, 20, 30)
│ └── Java looks for method: addNumbers(int, int, int)
│ └── Matches Line 17
│ └── Values pushed to stack:
│
┌────────────────┐
│
│ x = 10
│ ← from a
│
│ y = 20
│ ← from b
│
│ z = 30
│ ← from c
│
└────────────────┘
│ └── Executes Line 18 → Output: Sum (int + int + int): 60
│ └── Method excecution finishes - Values popped out of Stack - Control returns to the caller
(main method)
│
At Line 9, a, b, and c are passed by value into x, y, and z
Local variables from main() are copied into method arguments - Java's pass-by-value behavior.
Method Call on Line 10: addNumbers(d, b) → Control goes
to Line 21 as it matches the method on Line 21
main() continues
│
├── Line 10: addNumbers(5.5, 20)
│ └── Java looks for method: addNumbers(double, int)
│ └── Matches Line 21
│ └── Values pushed to stack:
│
┌────────────┐
│
Page 51 of 214
Java Complete Study Notes | By Jatin Sir
│ x = 5.5 │ ← from d
│
│ y = 20 │ ← from b
│
└────────────┘
│ └── Executes Line 22 → Output: Sum (double + int): 25.5
│ └── Method excecution finishes - Values popped out of Stack - Control returns to the caller
(main method)
│
At Line 10, d and b are passed by value into x and y.
Local variables from main() are copied into method arguments - Java's pass-by-value
behavior.
Key Points:
Method overloading happens inside the same class.
The return type does not affect method overloading.
Java does not care about private, static, void, or modifiers when overloading.
Only the method signature (name + parameter list) matters.
Why Return Type Doesn’t Matter in
Overloading?
In Java, we do not use return type to differentiate overloaded methods.
If overloading were allowed based on return type alone, it would lead to ambiguity — the
compiler would have no way to decide which method to execute during a method call.
int show() {
return 10; // return type is int
}
double show() {
return 10.5; // return type is double
}
Even though both methods return different types (int and double), they have the same method
name and have no parameters.
When we call show() >> The compiler cannot tell which version of show() we're referring to.
Java will throw an error because it only uses the method name and parameter list to resolve
calls — not the return type.
Arrays
What is an Array?
Page 52 of 214
Java Complete Study Notes | By Jatin Sir
An array is a LINEAR DATA STRUCTURE in Java that stores multiple values of the same data
type in contiguous memory locations.
Arrays allow you to access data efficiently using an index.
📌 Note: Note : Data Structure is a way of organizing and storing data in memory so that it
can be used
efficiently.
Key Characteristics
| Concept
| Explanation
elements.|
| COMMOM ERROR.
| Accessing an invalid index causes
ArrayIndexOutOfBoundsException.
How to Define an Array ?
Single Dimensional Array
int[] arr = new int[5];
int[] marks = {90, 80, 70, 60}; // Array definition without size - array literal syntax (automatically
set based on the number of values)
9. int [ ] → Declares an array that will store integers.
10. arr → Is the reference variable, stored in the stack.
11. new int[5] → Allocates 5 contiguous memory locations in the heap.
4. Each element is automatically initialized to 0 (default value for int)
How Array is stored in memory?
Stack Memory
Heap Memory
arr ────────────────►
[ 0 ][ 0 ][ 0 ][ 0 ][ 0 ]
Index: 0 1 2 3 4
How to Add Elements in an Array ?
We add elements to an array by assigning values to specific indexes.
int[] arr = new int[5]; // Creates an array of size 5
arr[0] = 10;
arr[1] = 20;
arr[2] = 30;
arr[3] = 40;
arr[4] = 50;
Page 53 of 214
Java Complete Study Notes | By Jatin Sir
Page 54 of 214
Java Complete Study Notes | By Jatin Sir
[Link] = 5
Array Traversing Using For Loop
Array traversing means moving through an array from the first index (0) to the last index (length
• 1) to access each element.
int[] nums = {10, 20, 30, 40};
for(int i = 0; i < [Link]; i++) {
instead of hardcoding i < 3
Page 55 of 214
Java Complete Study Notes | By Jatin Sir
Chapter 8: Strings
String Class, Intern Pool, Immutability, String Methods
Page 56 of 214
Java Complete Study Notes | By Jatin Sir
rows
Page 57 of 214
Java Complete Study Notes | By Jatin Sir
Page 58 of 214
Java Complete Study Notes | By Jatin Sir
+----------+
Program Execution Starts from main() >> The main method is loaded into the stack memory
(Top of the Stack).
>> Line 2: String Name1 = "Alexa";
Java checks if "Alexa" is already present in the String Intern Pool - NO, it is not present >> so
"Alexa" is added to the String Intern Pool.
A reference is created for Name1 in the stack (777), pointing to the memory address of "Alexa"
(777) in the String Intern Pool.
>> Line 3: String Name2 = "Alexa";
Java checks if "Alexa" is already present in the String Intern Pool. >> YES, it is already there.
Java assigns the same reference (memory address - 777) to Name2, pointing to the same
Page 59 of 214
Java Complete Study Notes | By Jatin Sir
+---------+
| "Alexa " | 777 String Intern Pool
+---------+
Line 2: String Name1 = "Alexa";
The string "Alexa" is stored in the String Intern Pool. The reference Name1 now points to the
memory address of "Alexa" in the intern pool.
Line 3: String Name2 = "Alexa";
Since "Alexa" is already in the String Intern Pool, Name2 points to the same memory address as
Name1.[ shown in image 1]
Line 4: Name1 = Name1 + Name2; //Alexa Alexa
Page 60 of 214
Java Complete Study Notes | By Jatin Sir
This line performs String concatenation, adding Name1 ("Alexa") and Name2 ("Alexa") - The
result is "Alexa Alexa".
Java checks if "Alexa Alexa" already exists in the String Intern Pool. Since it doesn't, Java adds
"Alexa Alexa" to the pool.
>> Important: Now, Name1 points to a new memory reference 888 (where "Alexa Alexa" is
stored) and the previous reference (777) pointing to "Alexa" is removed.
How Does Java Compare Objects?
Java compares objects in two ways:
(1) Using the reference comparison operator ==
(2) Using the .equals() method
How Reference Comparison Operator
( == ) Compare Primitives ?
>For primitive types (like int, char, boolean, etc.), == compares the actual values stored in the
variables, not memory addresses.
int a = 10; // a is primitive type
int b = 10; // b is primitive type
[Link](a == b); // == compares values for primitives // true because both values are
same. [ 10 == 10]
For Primitive types (like int, char, boolean)
Reference Comparison Operator ( == )compares actual values stored in the variable.
How Reference Comparison Operator
( == ) Compares Non - Primitives ?
For non-primitive types, == compares the memory address (i.e., whether two reference
variables point to the same object in memory)
String s1 = new String("Java"); // s1 - reference variable
String s2 = new String("Java"); // s2 - reference variable
[Link](s1 == s2); // false → different memory addresses
s1 and s2 are reference variables in the stack, each pointing to separate objects (i.e., different
memory addresses) in the heap, both containing the same content: "Java".
String s3 = "Java"; // s3 is stored in the String Intern Pool
String s4 = "Java"; // s4 points to the same object as s3, since the content is identical
[Link](s3 == s4); // true → both refer to the same object in the String pool
.equals() method
Used to compare contents of objects**
The .equals() method is case-sensitive.**
String s1 = new String("Java");
String s2 = new String("Java");
String s3 = new String("JAVA");
Page 61 of 214
Java Complete Study Notes | By Jatin Sir
[Link]([Link]());
// e.g., 63365123
[Link]([Link]());
// same as s1 → 63365123
Even though s1 and s2 are different objects in memory, their hashCodes match because their
CONTENTS ARE EQUAL.
Why is String Non-Primitive?
Unlike primitive types (int, char, etc.), String is a class. So we can create object from a class.
Even when declared like a primitive (String name = "java";). It internally creates a String object.
We can use methods on String (e.g., .length(), .toUpperCase()) — which primitive types don’t
support.
Memory Allocation: String Literal vs
String Object
String Literal
String name = "Athira"; // String Literal
Stored in the String Intern Pool.
> If the same literal already exists, Java reuses the reference → memory efficient.
String Object using new
String name = new String("Athira"); //String Object
A new object is created in Heap memory, even if the same literal exists in the pool > Not
Page 62 of 214
Java Complete Study Notes | By Jatin Sir
memory efficient.
How It Looks Internally In Memory?
Heap Memory
------------| "Athira" | ← created by new
------------String Intern Pool
---------------------| "Athira" | ← already created if not present
---------------------Stack Memory
----------------------------| name ─────────┐
▼
| Reference to Heap
-----------------------------
Why do we prefer String Literals over
String Objects?
String Literals - We prefer String literals because they are stored in the String Pool,which helps
Java reuse existing strings instead of creating new objects.
This improves memory efficiency and performance.
String Object - Using new String() creates unnecessary objects in Heap memory,so it is
generally avoided unless we explicitly need a new instance.
String Summary (Quick Revision)
(1) String is immutable → Once created, it cannot be changed.
(2) String literals are stored in the String Intern Pool.
(3) Using new String("abc") creates a new object in Heap memory.
(4) .equals() compares content.
(5) == behaves differently:
> For primitives: compares actual values
10 == 10 → true
> For objects: compares memory/reference
"abc" == new String("abc") → false
(6) .hashCode() returns an int based on content (overridden in String class)
(7) .hashCode() checks the content
(8) Different memory → == returns false
(9) Strings are thread-safe by nature due to immutability
Brain Teasers !
public class StringCheck {
public static void main(String[] args) {
String a = "Be Consistent";
String b = new String("Be Consistent");
[Link](a == b);
Page 63 of 214
Java Complete Study Notes | By Jatin Sir
// ?
[Link]([Link](b)); // ?
[Link]([Link]()); // ?
[Link]([Link]()); // ?
}
}
Questions:
(Q1) a == b → Does it compare references or values?
(Q2) [Link](b) → Will it return true or false? Why?
(Q3) [Link]() and [Link]() → Will they match?
public class EqualityCheck {
public static void main(String[] args) {
int a = 10; // primitive
int b = 10;
[Link](a == b); // ?
String s1 = "Value your time";
String s2 = new String("Value your time");
[Link](s1 == s2); // String object
[Link]([Link](s2)); // ?
// ?
}
}
(Q1) What will be the output of a == b? Why?
(Q2) Are s1 and s2 referring to the same object in memory? Why or why not?
(Q3) What does s1 == s2 compare? Content or memory reference?
(Q4) Why does [Link](s2) return true even though s1 == s2 is false?
(Q5) How many objects are created in memory when we write new String("Value your time")?
(Q6) Why does == work differently for primitives and String objects?
String Intern Method
Page 64 of 214
Java Complete Study Notes | By Jatin Sir
-------------------
String Object - Heap
String data = new String ("Nothing is impossible");
Heap stores objects created with new String().
Even if the same string exists in the pool, new forces a new object in the heap.
String .intern() in Java - What it does ?
We can explicitly move a String object to the String Intern Pool by calling .intern().
intern() tells Java - Put this string in the String Intern Pool - and return the reference from the
pool.
If the string already exists in the pool → Java won’t duplicate it, it will just return the existing
reference.
If it doesn’t exist yet → Java adds it to the pool and return that reference.
public class InternExample {
public static void main(String[] args) {
String s1 = "Nothing is impossible";
Page 65 of 214
Java Complete Study Notes | By Jatin Sir
Page 66 of 214
Java Complete Study Notes | By Jatin Sir
[Link]()?
new String(abc) → creates a new object in the heap (even if abc already exists in the pool).
[Link]() → ensures the string is placed in the intern pool and returns the reference.
4. Does intern() always create a new object?
No. If the string already exists in the intern pool, it just returns the reference.
If not, it adds it to the pool and returns the new reference.
Classes & Objects
OOPS
Object-Oriented Programming (OOP) is a way of writing programs by grouping data (variables)
and behavior (methods) into objects.
It makes code easier to understand, reuse, and maintain.
Principles of OOPS
(1) Classes and Objects (2) Encapsulation (3) Abstraction (4) Polymorphism (5) Inheritance
What is a Class?
Classes are rules imposed on an object.
ie class is a blueprint or set of rules that defines how objects behave and what they can do.
What is an Object?
An object is a real-world entity that occupies memory space.
Objects are created → They should follow the rules → They perform various actions → When
tasks are completed → Objects are destroyed.
Class vs Object - Real World Example
Car is an object, a real world entity. When we create a Car object, it must follow the rules
defined in the Car class.
Car class defines rules like maximum speed, number of wheels, type of fuel, braking system.
Actions (methods) could be: start(), accelerate(), brake(), refuel().
Properties (variables) could be: color, speed, fuelType.
Real Object with Class's Properties
and Methods!
From this blueprint (class), we can create different Car objects A black car with a speed of 110
km/hr that can start, accelerate, brake, and refuel.
A white car with a speed of 200 km/hr that can start and accelerate.
This shows how an object is a real instance of a class, with its own unique property values but
the same set of actions defined by the class.
Class, Object, and Methods —
Explained with a Student Example
17. package [Link];
18. public class Student { // Student is the class name
3.
19. int age;
Page 67 of 214
Java Complete Study Notes | By Jatin Sir
3.
30. public static void main(String[] args) { // Entry point of the program
5.
Page 68 of 214
Java Complete Study Notes | By Jatin Sir
int x = 10;
6.
int[] y = new int[3];
7.
Student s1 = new Student(); // Creating a Student object in heap
31. }
32. }
Local Variable
Variables created inside a method are called Local Variables.
• They are stored in stack memory.
• Java does not initialize them with default values — we must assign a value before using
them,
otherwise we'll get a compile-time error.
• Their lifetime is only until the method finishes execution, after that, they are removed from
the
stack.
public void show() {
int x; // Local variable, no default value
[Link](x); // Compile-time error: variable x might not have been initialized
}
Program Execution Flow
Java executes the program line by line:
Line 1 → package [Link];
A package is like a folder that groups related classes together.
It also helps avoid name conflicts between classes with the same name in different projects.
Line 2 → public class Runner
java enters this class only if it has a valid main method.
Line 4 → public static void main( )
Execution starts here.
main() method is pushed into the stack memory.
The stack top pointer now points to main().
+---------------------------+
| main()
Page 69 of 214
Java Complete Study Notes | By Jatin Sir
| ← top pointer
+---------------------------+
Line 5 → int x = 10;
java excecutes RHS first → evaluate 10.
Type check → Is 10 an integer?
✅ Yes. Is it stored in an int variable? ✅ Yes.
Allocate 4 bytes for x in stack (inside main’s stack frame).
java intialize 10 to x.
Stack Memory
+---------------------------------------+
| main() is pushed into the stack
top -> | main() | x = 10
+---------------------------------------+
Line 6 → int[] y = new int[3];
java executes RHS first → new int[3] creates an array object in the heap with 3 memory slots.
Since an array is a non-primitive type, Java automatically initializes all elements to their default
values. (For int, the default value is 0)
When the array is created in the heap, it gets a memory address (e.g., 999).
When Java executes the LHS, this address is stored in y (in the stack), so y now points to
that array in the heap.
Line 7 → Student s1 = new Student();
Student s1 = new Student();
Student - user defined data type
s1 = reference variable
new Student() - Object
java excecutes RHS first - new Student() creates an object student in heap memory
java executes RHS first → new Student():
new keyword = always creates a new object in the heap.
When ever an object is created 3 things happen Step 1: Student class is loaded into memory called
Method Area
Method Area - Stores class-level information — not per-object data.
Method Area is part of JVM memory from the start, but it’s empty until classes are
loaded.
Created when the class is first loaded by the JVM (before any object is created).
Step 2: Instance variables are created in heap and initialized with default values.
Step 3: Constructor is called (if not defined, Java provides a default constructor and executes it).
The object’s memory address (e.g., 101) is created.
LHS → s1 is a reference variable in stack; it stores the address/hashcode (e.g., 101) pointing to
Page 70 of 214
Java Complete Study Notes | By Jatin Sir
+------------------------+ | | maths
=0
= null
=0
+------------------------------------+
Line 8 → } (method closing brace)
Marks the end of the main() method.
When main() ends, the stack frame for main is removed (popped) from the stack memory.
All local variables (x, y, s1) are destroyed — but the objects they pointed to in heap remain in
memory until garbage collection runs.
Stack Memory (after main ends)
+---------------------------+
+---------------------------+
Line 9 → } (class closing brace)
Marks the end of the Runner class.
At this point, program execution is complete (unless there are other threads running).
📌 Note: Note:
All instance variables in heap get default values based on their type.
A real Student object is created in heap memory with its own name, age, rollNumber, marks,
etc., ready to be used by the program, and can be accessed using the reference variable s1.
Accessing instance
Variables
What are Instance Variables?
Instance variables are variables declared inside a class but outside any method, constructor, or
block.
Instance variables are stored in heap memory along with the object, and if not explicitly
initialized, they are automatically assigned default values.
class Student {
Page 71 of 214
Java Complete Study Notes | By Jatin Sir
// instance variable
40. public void calculateTotalMarks() { // Method name should describe the action it performs
12.
double totalMarks = marksObtainedInEnglish + marksObtainedInMaths +
marksObtainedInScience;
13.
[Link]("Total Marks Obtained: " + totalMarks); // print the result
41. }
42. }
Part 2 – Creating an Object and
Page 72 of 214
Java Complete Study Notes | By Jatin Sir
45. public static void main(String[] args) { // Entry point of the program
4.
int name; // Local variable — stored in Stack
5.
6.
Student s1 = new Student(); // Object in Heap, reference (s1) in Stack
7.
[Link](s1);
10.
[Link]([Link]); // Prints 'null' (default value)
11.
[Link]([Link]); // Prints 0 (default value)
46. }
47. }
Recap on what happens during an
object creation!
---------------------------Student s1 = new Student();
-----------------------------RHS : new Student();
Java executes RHS first → new Student()
new keyword = always creates a new object in the heap.
A new object is created in heap memory.
When an object is created in the heap, 3 things happen in order:
Page 73 of 214
Java Complete Study Notes | By Jatin Sir
Step 1: The Student class is loaded into memory called Method Area.
Method Area - Stores class-level information — not object data.
Method Area is part of JVM memory from the start, but it’s empty until classes are
loaded.
class loading into the Method Area happens only the first time when we use that
class in our program. Class loading is done by class loader
Step 2: Instance variables for that object are created in heap and initialized with default
values (e.g., int = 0, String = null).
Step 3: Constructor is called.
If no constructor is defined, Java provides a default no-arg constructor and executes it.
Constructor then sets the instance variables to the values we specify (if any).
The object’s memory address (e.g.101 or a hashcode) is created internally.
LHS : Student s1
s1 is a reference variable stored in the stack.
It holds the address/hashcode that points to the object in heap.
Now, Let's understand - What is a
reference variable ?
Imagine we have a locker in a gym.
The locker contains our gym shoes, water bottle, and towel (locker is our object with its items =
instance variables).
We don’t carry the entire locker around. Instead, we are given a locker key with a number
written on it.
That key doesn’t hold the items itself — it just points to where our locker is in the gym.
In Java terms:
Object = The locker (where all instance variables live).
Reference variable = The locker key (knows the location of the locker in memory).
Instance variables = The items inside the locker (the actual data).
We can have multiple keys (references) to the same locker (object), but the data is still stored in
one place.
No matter how many keys we have, they all open the same locker and see the same things
inside.
If we change something in the locker using one key, the change is visible to anyone using the
other keys.
What happens if we print reference
variable - s1?
[Link](s1); // o/p - [Link]@512ddf17
Printing a reference variable gives the hashcode generated by the hashCode() method
Hashcode > [Link]@512ddf17
Page 74 of 214
Java Complete Study Notes | By Jatin Sir
50. public static void main(String[] args) { // Entry point of the program
5.
6. Student s1 = new Student(); // Object creation
51. [Link] = "Neil"; // Assigning values to instance variables
52. [Link] = 10;
53. [Link] = 30;
54. [Link] = 50;
55. [Link] = 48;
56. [Link] = 47;
57. [Link] = "A";
58. [Link]([Link]); // Retrieving the value of 'name' and printing it
15. [Link]([Link]);
59. [Link]([Link]); //Retrieving the value of 'rollNumber' and printing it
17. [Link]([Link]);
60. [Link]([Link]); // Retrieving the value of 'name' and printing
it
19. [Link]([Link]);
20. [Link]([Link]);
61. [Link]();
How this value assignment happens ?
Page 75 of 214
Java Complete Study Notes | By Jatin Sir
Line 6
Student s1 = new Student();
An object is created in heap memory, and its instance variables are allocated space and
initialized with default values.
Stack Memory
Heap Memory
+---------------------------+
+-----------------------------------+
| main()
= null
=0
= null
+---------------------------+
+---------------------------+---+
Line 7
[Link] = "Neil";
Java checks where s1 is stored — it’s a local variable in stack memory (s1 is a local variable
inside main() method).
In the stack, s1 holds the value 777 (a reference/address pointing to the object in heap).
Java follows this reference 777 into the heap memory to locate the Student object.
Inside that object, Java finds the name instance variable.
The default value null is replaced with "Neil".
Same process happens for Lines 8–13:
How to retrieve values from instance
variables?
14. [Link]([Link]); // retrieving values by printing it off
15. [Link]([Link]);
16. [Link]([Link]);
17. [Link]([Link]);
18. [Link]([Link]);
19. [Link]([Link]);
20. [Link]([Link]);
Line 14
[Link]([Link]);
Java looks in the stack memory for the variable s1.
It finds that s1 holds the reference value 777 (hashcode/address of the object in heap).
Using this reference, Java goes to the heap memory and locates the Student object.
It checks if the object has a variable named name — Yes.
Page 76 of 214
Java Complete Study Notes | By Jatin Sir
Java retrieves the current value stored in name ("Neil") and prints it to the console.
Now We Understand –How Are
Objects and Classes Related?
We first created the Student class — this is like a blueprint.
The class only describes what a student has (properties) and can do (methods).
To actually use these properties, we need to create an object from the class.
After creating the object, we can assign values to its variables and use its methods.
In short:
A class is the plan — like a blueprint of a student, describing properties like name, age, marks,
and grade, and methods like calculating total marks.
An object is the real thing made from that plan — a real student with an actual name, age,
marks, and grade.
What Are the Problems or Drawbacks
Here?
Right now, instance variables in our Student class can be accessed directly from outside the
class.
This means anyone can assign invalid values to them without any checks.
Example:
Setting age = -110 — clearly invalid, but nothing stops us from doing it.
Setting rollNumber = 0 — may not be valid for our system.
Setting grade = "ABC" — might not match our allowed grading system.
62. [Link] = "123@Neil";
63. [Link] = -110;
64. [Link] = 0;
65. [Link] = "ABC";
// Invalid age
Key issue:
Direct access to instance variables breaks control over the data and can lead to incorrect or
inconsistent objects.
Page 77 of 214
Java Complete Study Notes | By Jatin Sir
Page 78 of 214
Java Complete Study Notes | By Jatin Sir
Page 79 of 214
Java Complete Study Notes | By Jatin Sir
[Link]("John Doe");
[Link](15);
[Link](101);
Page 80 of 214
Java Complete Study Notes | By Jatin Sir
[Link]([Link]());
[Link]([Link]());
[Link]([Link]());
}
}
How to achieve encapsulation in java?
Declare instance variables private - It controls access to instance variables from outside the
class.
Provide public getter and setter methods - It prevents invalid assignments by validating values
before storing them.
How to add setetrs and getters in our
code?
1. Right-click inside your class file.
2. Select SOURCE (or use the menu bar: Code → Generate).
3. Click Generate Getters and Setters.
4. Select the fields you want to generate them for.
5. Click OK — the IDE will auto-create the methods.
This Keyword
This keyword is used to differentiate local variables with instance variables when they have the
same name.
Student Class
66. package [Link]; // Package hierarchy: com (domain) →
student → management → system → oops
67. public class Student { // Student is the class name
3.
68. private int age;
object creation
69. private int rollNumber;
70. private double marksObtainedInEnglish;
instance variables
71. private String name;
Page 81 of 214
Java Complete Study Notes | By Jatin Sir
20.
[Link] = rollNumber;
81. }
82. else{
23. [Link]("Invalid roll number");
83. }
25.
marks
84. public void calculateTotalMarks() { // Method name should describe the action it performs
Page 82 of 214
Java Complete Study Notes | By Jatin Sir
27.
double totalMarks = marksObtainedInEnglish + marksObtainedInMaths +
marksObtainedInScience;
28.
[Link]("Total Marks Obtained: " + totalMarks); // print the result
85. }
86. }
Runner Class
87. package [Link];
88. public class Runner {
89. public static void main(String[] args) { // Entry point of the program
4.
5.
6.
Student s1 = new Student(); // Object in Heap, reference (s1) in Stack
7.
8.
[Link]("Joe")// setmethod to set name
[Link](-10);
9.
10.
11.
[Link]([Link]()); // Prints 'null' (default value)
12.
[Link]([Link]()); // Prints 0 (default value)
90. }
91. }
Program Excecution
Java executes code line by line starting from the class that contains the main() method.
The main() method is the program’s entry point and runs first when the application starts.
Line 1 (Runner Class):
package [Link] - Class "Runner" belongs to this package.
Line 2 (Runner Class) :
Page 83 of 214
Java Complete Study Notes | By Jatin Sir
Page 84 of 214
Java Complete Study Notes | By Jatin Sir
= null
+---------------------------+
+---------------------------+---+
Setter Method – How It Works
Why We Need Setters ?
Instance variables are usually declared as private to protect them from direct access outside the
class, This prevents illegal value assignments from outside.
But then, how do we assign values to these private variables from outside?
By using setter methods - To assign values
Getter methods - For reading values.
Line 7 (Runner Class):
[Link]("Joe"); // Control goes to Line 13 in Student class - setName method()
Stack and Heap During setName("Joe") Execution - before the assignment to heap variable
happens
+----------------------------+
+-------------------------------+
Stack
Heap
+----------------------------+
+-------------------------------+
top-> | setName("Joe")
+----------------------------+
+----------------------------+
+-------------------------------+
s1 is a reference variable in the stack pointing to a Student object in the heap.
When we call [Link]("Joe") > Java pushes the setName method into the stack for
execution. (All methods are always executed inside the stack)
top points to setName() as its the top most in stack - and the method currently running.
[Link]("Joe") tells Java: Look at the object that s1 is pointing to. Go into its class (Student)
and execute the method named setName.
At the moment setName starts running, the parameter variable "name" is CREATED INSIDE
STACK of setName and stores the value "Joe".
(Method parameters are local variables and are always stored in the stack.)
📌 Note: Remember - When we created the "Student" object - a "name" variable is created
in heap.
Two Variables Named "name"
So now, we have two different variables with the same name - "name":
(1) One in the stack → method parameter.
Page 85 of 214
Java Complete Study Notes | By Jatin Sir
// when java excecutes >> [Link]("Joe") >> Control goes to Line 13 in Student class
setName method()
94. }
[Link] → refers to the instance variable in heap. [with "this" keyword - It explicitly tells java
that we are referring to the instance variables of the class]
name → refers to the local variable in stack.
What If We Skip "this"?
public void setName(String name) {
name = name; // Both refer to the local variable → does nothing!
}
Java will confuse both variables as local variables.
The instance variable will never get updated.
After [Link] = name -> How it looks
in memory?
+----------------------------+
+-------------------------------+
Stack (
Heap
+----------------------------+
Page 86 of 214
Java Complete Study Notes | By Jatin Sir
+-------------------------------+
| setName("Joe")
+----------------------------+
(instance variable)
+----------------------------+
+-------------------------------+
Line 15 (Student Class)
} – Closing brace for the setName method.
At this point, Java pops the setName method frame out of the stack.
Now, only main() remains in the stack, and top points back to main().
Line 8 (Runner Class)
95. [Link](-22);// Control goes to Line 17 in student object
s1 is a reference variable in the stack pointing to student object in the heap.
When we call setRollNumber(-10), Java pushes the setRollNumber method into the stack for
execution.(setRollNumber is a method - methods are always excecuted inside stack)
[Link](-10) tells Java - Look at the object s1 is pointing to, Go into its class (Student)
and execute the method named setRollNumber.[Line 17 in Student class]
Roll num (local variable) is set as -10 inside the stack .
Line 17 (Student Class) - How
validation works inside setter?
96. public void setRollNumber(int rollNumber){
18.
97. if(rollNumber >=1)
20.
[Link] = rollNumber;
98. }
99. else{
23. [Link]("Invalid roll number");
100. }
java will excecute Line num 17 - java checks the validation written inside the setRollNumber
method
(1) Is rollNumber > 1? OR (2) Is rollNumber = 1 ?
Both condition are false >> So java is not going to excecute
So [Link] = rollNumber >> assignment won't happen and java will excecute else block
• and print - Invalid roll number.
"RollNumber" in heap will remain 0 as its default value.
Page 87 of 214
Java Complete Study Notes | By Jatin Sir
When wrong values are rejected in a setter, the instance variable keeps its old value — which
could be the default value if no valid assignment has been made yet.
Line 24 (Student Class)
} - closing brace
end of setName method
java pops out the setName method rom the stack.
We only have main() inside the stack and top will point to the main()
Constructors
What is a Constructor?
A constructor is a special method in a class, which has the same name as the class name.
What s the job of the constructor?
The job of the constructor is to initialize instance variables during object creation.
How to create a constructor in an IDE
(IntelliJ/Eclispse)?
Open class file in the editor.
Place the cursor inside the class but outside any existing methods.
Right-click → select Source → Generate Constructor using Fields.
What is a default constructor?
It’s the constructor Java provides automatically if we don’t write any constructor in our class.
It has no parameters and no body code .
class Student {
int age;
String name;
// Student() { super(); }
}
Student Class - How constructor in a
class look like?
101. package [Link];
102. public class Student { // Student is the class name
3.
4.
int age;
Page 88 of 214
Java Complete Study Notes | By Jatin Sir
memory
5.
int rollNumber; // instance variables are properties of class // cannot be static
6.
String name; // instance variables are assigned with default values, if not initialized
7.
103. public Student(String name, int age, int rollNumber) {
10.
11.
12.
13.
[Link] = name;
[Link] = age;
[Link] = rollNumber;
14.
15.
16.
17.
18.
[Link] = marksObtainedInEnglish;
[Link] = marksObtainedInMaths;
[Link] = marksObtainedInScience;
[Link] = grade;
}
19.
public void setRollNumber(int rollNumber){ // added a setter with validation for rollnumber
104. if(rollNumber >=1)
21.
[Link] = rollNumber;
Page 89 of 214
Java Complete Study Notes | By Jatin Sir
105. }
106. else{
24. [Link]("Invalid roll number");
107. }
108. }
Student Runner Class
package [Link];
public class Runner {
public static void main(String[] args) {
Student s1 = new Student("Neil", 10, 26, 40, 30, 41, "B");// // Object creation → calls the
constructor and assigns values to instance variables
[Link]("John"); // Setter updates the 'name' instance variable from "Neil" → "John"
[Link](-10); // Setter validation fails → rollNumber remains 26 (from constructor)
[Link]([Link]());
[Link]([Link]());
}
}
What happens during Object creation?
During object creation - we pass all the values of instance variables, and constructor pass it and
use it for assigning to instance variables.
Student s1 = new Student("Neil", 10, 26, 40, 30, 41, "B");
Constructor is called ONCE - ONLY DURING OBJECT CREATION.
2. How is Constructor different from
Setter ?
------------------------------------------------------------------------------------------------------------------------------------
-----| Constructor
| Setter
-------------------------------------------------------------------------- |
| Constructor is Called ONLY ONCE during object creation. | Setter can be called MULTIPLE
TIMES after object creation.
object creation.
the programmer.
| Example: new Student("Neil", 17, 25)
| Example - [Link]("Uday")
------------------------------------------------------------------------------------------------------------------------------------
-------
Properties of Constructors
(1) Has the same name as the class.
(2) No return type (not even void).
Page 90 of 214
Java Complete Study Notes | By Jatin Sir
Page 91 of 214
Java Complete Study Notes | By Jatin Sir
Constructor Overloading
Multiple constructors with same name inside a class but with different parameter lists.
public Person() { }
// Default
// 1 parameter
Page 92 of 214
Java Complete Study Notes | By Jatin Sir
2. String name;
110. int age;
4.
5. Student(String name, int age) {
// Constructor
6.
[Link] = name; // Assign parameter to instance variable in Heap
7.
[Link] = age;
111. }
9.
112. public static void main(String[] args) {
11.
Student s = new Student("Neil", 10); // Object creation - constructor is called
113. }
114. }
Execution Steps: How Constructor
Assigns Values to Instance Variables
1. main() starts in Stack memory.
○ Program begins with main method.
○ main() is loaded into the Stack memory.
+----------------------+
+----------------------+
| Stack
top ->| main()
+----------------------+
2. Reference variable s created in Stack.
○ Code: Student s = new Student("Neil", 10);
○ s is a reference variable stored in the Stack.
○ It will point to the Student object in the Heap.
○ new Student(...) creates an object in Heap > new keyword allocates
memory in Heap.
○ Instance variables (name, age) are created with default values.
Step 2: new Student("Neil", 10) creates object in Heap
Step 2: new Student("Neil", 10) creates object in Heap
+----------------------+
+-------------------------------+
Page 93 of 214
Java Complete Study Notes | By Jatin Sir
Stack
Heap
+----------------------+
+-------------------------------+
3. Constructor is called, when the obj is created → parameters go to Stack.
● Constructor Student(String name, int age) runs.
● A stack is created for the constructor.
● Parameters ("Neil", 10) are stored as local variables in Stack.
+----------------------+
+-------------------------------+
Stack
Heap
+----------------------+
+-------------------------------+
4. Values copied from Stack to Heap's instance variables. [Link] = name; //
assigns "Neil" [Link] = age; // assigns 10
○ The local variable 'name' in Stack (value = "Neil") is assigned to the
instance variable 'name' in Heap.
○ The local variable 'age' in Stack (value = 10) is assigned to the instance
variable 'age' in Heap.
5. When the Constructor finishes excecution→ local variables disappear.
○ Constructor is removed from stack.
○ Local variables in Stack are gone.
○ Heap object remains with values updated.
6. Object remains in Heap, referenced by s in Stack.
○ s in Stack holds the reference (e.g., 777).
○ That points to the Student object in Heap.
○ Object lives as long as a reference exists.
Bringing It All
Together: Setters,
Getters &
Constructors →
POJO
Now that we have learned:
(1) How to make variables private ?
(2) How to create public setter and getter methods ?
(3) How to define constructors to initialize objects ?
It’s time to bring all of these together to create something powerful yet simple — a POJO class!
Page 94 of 214
Java Complete Study Notes | By Jatin Sir
// Constructor
Page 95 of 214
Java Complete Study Notes | By Jatin Sir
return name;
}
Page 96 of 214
Java Complete Study Notes | By Jatin Sir
}
OUTPUT - Student [name=Neil, age=10, rollNumber=30]
Without toString() - What is going to be
the o/p?
s1 is the reference variable which store hashcode of the object
If we print s1 without toString() - [Link](s1) - s1 returns hashcode >>
[Link]@512ddf17
Why use toString()?
Helps during debugging or logging, so we can see the exact values of variables.
Without overriding, calling [Link](obj) prints the hashcode by default.
Overriding allows printing meaningful data about the object.
How toString() looks internally (inside
Object class) ?
public String toString() {
return getClass().getName() + "@" + [Link](hashCode());
}
What happens step by step ?
getClass().getName() >> Gets the runtime class of the object (e.g., Person, Student, Car).
Example → "Person"
hashCode() >> Returns a unique number (int) that represents the object in memory.
Example → 2052879
[Link](hashCode()) >> Converts that number into hexadecimal format.
Example → "7a81197d"
Then it combines them like: Person@7a81197d
Output (default):
Person@7a81197d
Summary
toString() exists in Object class, so all Java objects have it.
Default → className@hashCode.
Overriding → meaningful object data.
Best practice → always override toString() for user-defined classes.
How to Compare
Java Objects equals() &
Hashcode() !
class Student {
private String name;
public Student(String name) {
[Link] = name;
Page 97 of 214
Java Complete Study Notes | By Jatin Sir
}
}
public class MainApp {
public static void main(String[] args) {
Student s1 = new Student("Anu");
Student s2 = new Student("Ann");
Student s3 = new Student("Ann");
[Link]([Link](s2));
[Link]([Link](s3));
same
}
}
equals()
equals() is a method used to compare two objects in java.
2 objects are said to be equal > When they belong to the same class type.
> values of instance variables should be same
> 2 objects are going to have same hashcode
📌 Note: NOTE: When 2 objects are equal >> they will have same hashcode value!
Internal representation of .equal()
@Override
public boolean equals(Object obj) { // comparing 2 objects// parameter is of parent class//
datatype of reference is obj
if (this == obj) { // this talks about the current instance// comparing [Link](s1)
return true; // then return true!
}
Page 98 of 214
Java Complete Study Notes | By Jatin Sir
if (obj == null )// passing null - couldn't compare which doesn't exist ie obj reference is
NULL
return false; // return false!
if(getClass() != [Link]()) { // getClass() gives the class type // if 2 classes are not
equal - return false
return false; // return false
}
// Then do type casting - after that value of each instance variable of one object is
Page 99 of 214
Java Complete Study Notes | By Jatin Sir
}
class Child extends Parent {
}
📌 Note: Important Terms
Superclass (Parent Class) → the class whose properties are inherited.
Subclass (Child Class) → the class that inherits the parent’s properties.
Child can only access non-private features of the parent.
Types of Inheritance
Single Inheritance - One child inherits
from one parent.
Class A (Parent)
Class B (Child)
class A
{
}
class B extends A
{
}
Multilevel Inheritance
Class A (Grandparent)
Class B (Parent)
Class C (Child)
class A { }
class B extends A { }
class C extends B { }
Multiple Inheritance (Not Supported in
Java)
Class A
Class B
\
/
Reason: Ambiguity
Solution: Use Interfaces if multiple inheritance is needed.
\
/
\
/
Class C ← Error
How to define instance variables in
parent class when a child class
extends the parent?
We use "protected" as access modifier, so that child classes can directly access parent class
variables, while still keeping them hidden from the outside world.
// Parent class
class Person {
> super keyword - to call parent class constructor from child class constructor
Class Student (child) extends Person
(parent)
1 class Person {
2 protected String name; protected int age;
3
4 // Constructor 1 (overloaded)
5 public Person(String name) {
6
[Link] = name;
7
[Link] = 18; // default
8
[Link]("Parent Constructor 1 (name only)");
9}
10
11 // Constructor 2 (overloaded)
12 public Person(String name, int age) {
13
[Link] = name; [Link] = age;
14
[Link]("Parent Constructor 2 (name and age)");
15 }
16 }
17
18 class Student extends Person {
19 int rollNo;
20
21 // Child constructor → calls parent Constructor 2
22 public Student(String name, int age, int rollNo) {
23
super(name, age); // super() → calls parent Constructor 2
24
[Link] = rollNo;
25
[Link]("Child(Student) constructor called");
26 }
27
5}
6}
How constructor chaining works in the
above code?
Student s1 = new Student("Athira", 25, 101);
Line num 3 in Runner class calls the Student constructor (String, int, int).
Inside it → super(name, age) is invoked (Line 23 in the Student class).
This triggers Parent Constructor 2 (String, int) (Line 12 in Person class).
"Athira", 25 are passed to parent.
After parent finishes, child constructor sets rollNo = 101.
Flow - Runner (line 3) → Student(String,int,int) → super(name, age) → Person(String,int)
Student s2 = new Student("Neil", 202);
Calls the Student constructor (String, int).
Inside it → super(name) is invoked (Line 30 in the Student class).
This triggers Parent Constructor 1 (String) (Line 5 in Person class).
Parent assigns name = "Neil" and gives default age = 18.
After parent finishes, child constructor sets rollNo = 202.
Flow - Runner → Student(String,int) → super(name) → Person(String)
Summary
Constructor chaining is shown where Student(String,int,int) chains to Person(String,int) via
super(name, age) and Student(String,int) chains to Person(String) via super(name).
s1 → goes to Constructor 2 in parent because super(name, age) matches (String,int).
s2 → goes to Constructor 1 in parent because super(name) matches (String).
2 ways to do constructor chaining super & this !
super() → In Inheritance, super() is used, if we want to call parent class constructor from the
child class constructor.
this() → this keyword is used to do constructor chaining within the same class.
📌 Note: Note :
this (without parenthesis) → Refers to the current object’s instance variables, usually when local
variables or constructor parameters have the same name.
Example: [Link] = name;
Excecution - Class B
(Superclass/Parent)
& Class C
(Sub-class/Child)
------------------- [Link] ------------------1 package [Link];
------------------- [Link] ------------------1 package [Link];
2
2
3 public class B {
3 public class C extends B {
4 private int x;
4 private int z;
5 private int y;
5
6
6 public C(int x, int y, int z) {
7 public B(int x, int y) {
7
super(x, y); // call parent constructor
8
super(); // calls Object constructor
8
this.z = z;
9
this.x = x;
9}
10
this.y = y;
10
11 }
11 @Override
12
12 public void add() {
13 public void add() {
13
[Link](getX() + z);
14
[Link](x + y);
14 }
15 }
15
16
16 // inherits getX(), setX() from B
17 public int getX() {
17
18
return x;
18 }
19 }
19
20
20
21 public void setX(int x) {
22
this.x = x;
23 }
24 }
}
Runner Class
1 package [Link];
2
3 public class Runner {
4 public static void main(String[] args) {
5
B b = new B(10, 20);
6
[Link]();
7
8
C c = new C(10, 20, 30);
9
[Link]();
10 }
11 }
Excecution
Program execution begins from the main method inside the Runner class.
Line 4: Runner - Excecution happens in stack - main is loaded to mmry >> top points to main()
Line 5: B b = new B(10, 20); > Java excecutes RHS First
> object is created
> class is loaded into method area
> instance variables r created
> constructor is called
> obj B is created in heap mmry
> x & y - instance variables r created and initialized with default values
constructor is called and control goes to class B - Line 7
public B(int x,inty) - constructor's parameters x and y are local variables, created in stack
memory
x and y in stack get values 10 and 20
this.x = x
> Here value of local variable x is passed to instance variable x.
> value of local variable y is passed to instance variable y.
when the constructor finishes excecution - local variables r removed from stack n control goes to
main().
Now java excecutes LHS - B is assigned a hashcode and is pointed towards onject in heap
Next, control goes to class B - Line 13 - add ()
add() method is loaded in stack
> (1) java first checks - Is there a local variable x and y - No.
> (2) Then java checks - whether the class has instance variable x and y - yes
Java calculates x + y = 20
Now, control goes to Runner class > Line 8 > C c = new C(10, 20, 30) > Java excecutes RHS
first
4 private int z;
5 protected int y;
5
5
6
6 public B(int x, int y) {
6 public C(int x, int
y, int z) {
7 public A(int x, int y) {
7
super(x, y);
7
super(x, y);
8
super();
8}
8
this.z = z;
9
this.x = x;
9
9}
10
this.y = y;
10 public void add() {
10
11 }
11
[Link](getX() + getY());
11
@Override
12
12 }
12 public void add() {
13 public int getX() {
13
13.
[Link] (getX()getY(z);
14
return x;
14 public int getX() {
14
}
15 }
15
return [Link]();
15
16
16 }
16 public int getZ() {
17 public void setX(int x) {
17
17
return z;
18
this.x = x;
18 public void setX(int x) {
18 }
19 }
19
[Link](x);
19
20
20 }
20 public void setZ(int z)
{
21 public int getY() {
21
21
this.z = z;
22
return y;
22 public int getY() {
22 }
23 }
23
return [Link]();
23 }
24
24 }
25 public void setY(int y) {
25
26
this.y = y;
26 public void setY(int y) {
27 }
27
[Link](y);
28
28 }
29 @Override
29
30 public String toString() {
30 @Override
31
return "A [x=" + x + ", y=" + y + "]"; 31 public String toString() {
32 }
32
return "B [x=" + getX() + ", y=" + getY() + "]";
33 }
Runner Class for A , B & C
1 public class ARunner {
public class CRunner {
2 public static void main(String[] args) {
public static void main(String[] args) {
3 A a = new A(10, 20);
C c = new C(50, 60, 70);
4 [Link](a);
[Link]();
5 [Link]([Link]());
[Link]([Link]())
6 [Link]([Link]()); }
[Link](c);}
}
public class BRunner {
public static void main(String[] args) {
B b = new B(30, 40); // calls A(int,int) via super()
[Link]();
// prints ?
the constructor!
In traditional constructors, we’d pass all the variables like - SimpleBuilderForStudent(String
name, int roll, String course, Integer phone)
But with the Builder Design Pattern, we do it differently!
Let's park it here - We will decide whom to pass to constructor - after meeting an important
person!
private SimpleBuilderForStudent(?????) // Constructor parked without passing anything for now.
Step 2: Create a static inner class &
Step 3: declare the same instance
variables as outer class
Static Inner Class - Create a STATIC INNER CLASS called - "Builder" inside our outer class.
The Builder holds all the data needed to build the object.
It uses the same variables as the outer class (but without final).
This inner Builder class acts as a data carrier.
Instead of passing each value separately to the outer class constructor > we pass the whole
Builder object — which contains all the necessary values.
So now we clearly understand whom we should pass to the constructor —It’s the Builder, not
the individual variables.
public static class Builder { // Static Inner Class
private String name;
private int rollNumber;
private int phoneNumber;
private String course;
}
Step 4: Create Setter Methods in
Builder - called Builder Methods!
The setter methods in the Builder class is used to assign values to the variables in the static
Builder class.
These values are stored temporarily and later used to create the final object when .build() is
called.
public Builder setRollNumber(int rollNumber) { // Return type of the method setRollNumber Builder
// Since we're inside the static inner class Builder - This method
[Link] = rollNumber;
}
One Important Thing to Note:
The RETURN TYPE of the setter method is Builder — but why?
Because we're inside the static inner class Builder, and this method needs to return the same
Builder object (i.e., this) to allow method chaining.
What do the builder methods do?
Each method in the builder class returns the same builder object(this) , so we can chain one
call after another like a fluent sentence.
This is called method chaining.
Method Chaining : Builder -> set name -> return Builder -> set rollNumber > return Builder.
So now, the static inner class named Builder holds all the instance variables and their setter
methods.
Step 5: Create a Constructor in the
outer Class
Since the Builder class already holds all the required values through its instance variables and
setter methods,
we don’t need to pass each variable separately to the outer class constructor.
Instead, we pass the Builder object itself, and the outer class can access all the values from it to
initialize its own fields.
private SimpleBuilderForStudent(Builder builder) { // Constructor with builder object
Builder → The class name of the static inner class (used as the data type)
builder → The reference variable (holds the data when passed as a parameter)
Why the name Builder ? Can we give
any name to the static inner class?
Yes! Technically, We can name the static inner class anything we want. It's just a class.
But, Builder is the convention (not a rule) — and naming it "Builder" makes our code - (1) Easier
to read (2) Instantly recognizable to other's going through the code.
why the constructor takes Builder as a
parameter ?
private SimpleBuilderForStudent(Builder builder)
The parameter type of the constructor is Builder because that's the name of the static nested
class inside outer class - SimpleBuilderForStudent.
We pass a Builder object to this constructor so that we can access all the values that were set
using the Builder methods and use them to initialize the outer class fields.
Step 6: Add a build() Method in
Builder! What it does?
The build() method is written inside the Builder class — and it is the final step of the Builder
Design Pattern.
• Creates the final object of the outer class
• Finalizes the object construction process
• Takes the values stored in the Builder object
// This build() method is called at the end in the runner class, after all the setter
methods
are chained.
}
Step 7: Create a Runner Class and
Call the Builder
Why do we need a Runner class?
The Runner class is used to test and create objects using the Builder pattern.
It helps demonstrate how we can set values using method chaining and finally build the actual
object of the outer class.
What happens inside the Runner class?
We first create an object of the Builder class.
Then we use method chaining to set the required fields.
At the end, we call .build() — this is the step that creates the final object of the outer class.
SimpleBuilderForStudent student = new [Link]()
.setName("Athira")
.setRollNumber(101)
.setCourse("Java")
.build(); // Creates the final object
What does .build() do?
.build() is a method defined inside the static Builder class.
It uses new SimpleBuilderForStudent(this) to call the private constructor of the outer class.
It passes the current Builder object (this) to the constructor.
The constructor extracts all the values from the Builder object and initializes the outer class’s
fields.
This works because both the Builder class and the constructor exist inside the same outer class,
even if the constructor is private.
How to handle Optional Fields in Builder ?
In the Builder pattern, if a field is optional (like phoneNumber) - we simply don’t call its setter in
the Runner class.
The field will take its default value (e.g., 0 for int, null for String).
Notes:
If we skip setPhoneNumber( ) >> it will stay as default 0.
If we skip setCourse() >> it will stay as null.
We don’t need any special logic — just don’t call the setter!
Do we need to write the setter method for an optional field?
Yes, we should write the setter — even if it's optional.
But why write it if we’re not always passing values?
Because the Builder Pattern gives users the choice to set only the fields they want. We’re not
forcing them — we’re just making the option available.
Even if the field is not always set, it’s part of the class design — and without the setter: The user
can’t set the value even if they want to and object becomes inflexible and incomplete.
Builder Design code with comments !
package builderdesignpatternpractice;
public class SimpleBuilderForStudent {
// Mandatory
// Optional
// Step 5: Constructor of outer class accepts Builder object - Instead of passing each
field, we
@Override
public String toString() {
return "SimpleBuilderForStudent{" +
"name='" + name + '\'' +
", rollNumber=" + rollNumber +
", phoneNumber=" + phoneNumber +
", course='" + course + '\'' +
'}';
}
}
}
public Builder setCourse(String course) {
[Link] = course;
return this;
}
// Mandatory
.setRollNumber(101)
// Mandatory
// mandatory
.build();
[Link](student);
}
}
Exception Handling
Exception refers to an unwanted interruption that affects the normal flow of a program.
What is Exception Handling?
Exception Handling is finding ways to handle the unwanted interruptions.
We can handle this unwanted interruption caused by the risky code by giving proper error
messages and make the code robust and user friendly!
Why Exception Handling?
We write code assuming things will work perfectly. But what if something goes wrong?
Situations like:
(1) Reading a file – the file path may be wrong, or the file may not exist
(2) Connecting to a database – the DB might be down
(3) Accessing a null object – causes NullPointerException
(4) Accessing a missing element – causes ElementNotFound-type errors
These interruptions should be handled properly, else the program will crash.
Benefits of Exception Handling
Helps display proper error messages
Improves the user experience
Easier debugging — trace the exact line where the error occurred
Makes code robust and reliable
Hierarchy of Exception Classes
Object (Parent class)
[Link] package)
│ extends
Object is the parent of all classes in java (belongs to
┌────┴────┐
│Throwable│ ←
Throwable is the general class for all exceptions (belongs to
[Link])
extends
└───┬────-┘ extends
┌────────┴────────┐
│
│
(class)Exception
Error (class)
Throwable is the parent class of both Exception and Error
Belongs to the [Link] package
Throwable has two child classes - Exception & Error
Exception → can be handled using try-catch
Error → cannot be handled (e.g., OutOfMemoryError)
Types of Exceptions
(1) Checked Exceptions and
(2) Unchecked Exceptions
Checked Exceptions
Checked by the compiler at compile time
Why Are Some Exceptions
"Checked"?
Exceptions like FileNotFoundException and SQLException are checked by the compiler
because:
There is a high chance that things can go wrong in these cases
These problems are often out of our control — and Java wants us to handle them properly
markdown
Since these are very common real-world risks that occur at runtime, Java wants us to prepare
for them in advance — during compilation.
If we don’t handle them - Java throws a compile-time error - These are known as Checked
Exceptions.
Unchecked Exceptions
Unchecked exceptions are RUNTIME Exceptions that are not checked by the compiler during
compilation.
• The program compiles successfully, but may fail at runtime.
• Java does not force you to handle them using try-catch or throws.
• These exceptions occur due to programming mistakes or memory-related issues, such as:
• Accessing null references → NullPointerException
• Dividing by zero → ArithmeticException
• Accessing invalid array indexes → ArrayIndexOutOfBoundsException
Explanation:
NullPointerException → when we try to use an object that is null
ArrayIndexOutOfBoundsException → when we access an array index that doesn't exist
ArithmeticException → when we perform illegal math (like 10 / 0)
Who is the Parent of Exception?
All exceptions inherit from Throwable, which in turn extends Object, the parent of all classes in
Java.
// We use `try-catch` to handle exceptions and stop the program from crashing
unexpectedly.
// Handling code
}
Different Types of Exceptions
(1) Arithematic Exception
ArithmeticException happens when you perform an illegal math operation in Java — most
commonly dividing a number by zero.
public class Main {
try {
result = a / b;
allowed
} catch (ArithmeticException e) { // 'catch' block → runs if an ArithmeticException happens
[Link]("can't divide a number by zero!"); // Custom friendly message to user
[Link]([Link]()); // Shows Java's built-in short message (e.g., "/ by
zero")
[Link]();
// This will still run because we handled the exception instead of letting the program
crash
[Link]("Result: " + result); // Prints '0' because division was not successful
[Link]("Hello");
}
}
(2) ArrayIndexOutOfBoundsException
ArrayIndexOutOfBoundsException happens when we try to access an array index that does not
exist.
Java arrays have fixed sizes, so valid indexes are from 0 to length - 1.
public class Main {
public static void main(String[] args) {
a[0] = 10;
a[1] = 20;
a[2] = 30;
try {
[Link]("Hello");
}
}
3. NullPointerException
A NullPointerException occurs when a reference variable is not pointing to any object, but we try
to access its methods or properties.
public class Main {
public static void main(String[] args) {
Person p = null; // Reference variable 'p' is not pointing to any object
try {
[Link]([Link]()); // Trying to call a method on null →
NullPointerException
} catch (NullPointerException e) {
[Link]("Object is null!"); // Custom friendly message
[Link]([Link]()); // Shows Java's built-in message (usually null)
[Link]();
}
[Link]("Program continues..."); // Proves program didn’t crash
}
}
Practise Questions
[Link] Between Exception and
Error in Java ?
Exception
Represents conditions a program can recover from.
Can be caught and handled using try-catch.
Example:
FileNotFoundException → Ask user to choose another file.
SQLException → Retry database connection.
Error
Represents serious problems that are usually not recoverable.
Should not be caught and handled in most cases (though technically you can catch them, it’s
not recommended).
Examples:
OutOfMemoryError → Program ran out of heap space.
StackOverflowError → Infinite recursion.
2. Explain the difference in behavior
and exceptions between:
What happens if we try to access methods or properties on each of these variables?
String name = null; // Initialized with null
String name2;
The File class in Java acts as a reference to a file or directory path on the system.
It helps us to interact with external files or directories — such as checking if they
exist,
creating new ones, deleting them, or retrieving their properties (like name, path, size).
7
File file = new
File("/Users/athira/Documents/2025/Java_Project/Javaprograms_SDET/[Link]");
// Create new file - Creating a new file object to attempt file creation
8
File newfile = new
File("/Users/athira/Documents/2025/Java_Project/Javaprograms_SDET/[Link]");
9
10
11
12
13
14
15
try {
if([Link]()){
[Link]("File " + [Link]() + " created successfully");
}
else{
[Link]("Cannot create file - file already exists");
}
// // Handle any IOException during file creation
16
} catch (IOException e) {
17
[Link]("Cannot create file " + [Link]() + ", something went
wrong");
18
[Link]();
19
}
20
21
22
23
24
25
26
27
28
29
30
31
32
33 }
34 }
getName()
Returns the file name along with its extension.
- Line 11 → To get the name of the newly created file.
- Line 17 → To show the name of the file when creation fails (inside `[Link]`).
- Line 21 → To get the name of an existing file.
- Line 28 → To confirm the name of the deleted file.
getAbsolutePath()
Returns the complete path of the file.
Line 22: Returns the complete path of the file, starting from the root directory.
createNewFile()
delete()
Deletes the file from the file system.
Returns TRUE – If the file was deleted successfully
Returns FALSE – If the file could not be deleted (e.g., file doesn't exist or is in use)
Line 27 → Checks whether the file can be deleted using `[Link]()`
Line 28 → Prints confirmation if deleted
Line 31 → Prints error if deletion fails
Always assume file operations may fail — prepare our program to recover smoothly.
10
11
12
13
14
15
16
17
// Create a directory
File logDirectory = new File("logs");
18
19
20 }
21 }
What is Serialization?
Serialization is the process of converting a Java object into a byte stream so that it can
be:
Important
>
If a class does not implement Serializable, and we try to serialize it, we'll get a
NotSerializableException.
What is [Link]?
It is a file that stores the object data in binary format [ blob file – Binary Large Obj]
It is not human-readable
We can use Deserialization to get the object back from it later
Advantages of Serialization
Data Persistence: Information can be securely stored and can be reloaded at any time
without
the need for re-entry.
Automation: The process of saving and retrieving data becomes automated, reducing manual
intervention.
Error Reduction: By minimizing human input, the chances of errors in data entry are
significantly
reduced.
What is Deserialization?
Deserialization is the reverse process Reading the byte stream and converting it back to
the original Java object.
We need to read the file(blob/stream file) and create object out of it. So we need
fileinput stream
for reading it
try {
fis = new FileInputStream("[Link]"); // Opens the .ser file to read bytes
ois = new ObjectInputStream(fis);
// Converts byte stream back to Java object
Contact data = (Contact) [Link]();//
// Read the object from the file and convert it back into a Contact object
[Link](data);
// Prints the deserialized object
//It's called type casting because we're converting the object’s type from Object
(generic)
to Contact (specific) using a cast.
// Always close streams after use
[Link]();
[Link]();
} catch (IOException | ClassNotFoundException e) {
[Link](); // Handles both IO and class load failures
}
Final Takeaway
If we want to store Java objects permanently - use Serialization to save them to a .ser
file.
Deserialization to bring them back when needed.
Serialization &
Deserialization using
File!
Using the File class in serialization/deserialization gives extra control over file
handling
(existence, metadata, directories), unlike basic serialization which directly uses file
paths.
Note :
When we serialize an array (e.g., Student[]), the .ser file stores the entire array
object.
But when we deserialize, the method readObject() always returns a generic Object.
That’s why we must cast it back to the specific type.
Student[] data = (Student[]) [Link]();
Concept Check!
Use custom writeObject() and readObject() methods to handle that field manually.
Array of Objects
Let’s revisit Array!
Array
Array is a linear data structure, which store similar kind of data!
It stores multiple values of the same data type in contiguous memory locations.
It allows data access efficiently using an index.
Arrays are non-primitive. They are type safe.
int[] a = new int[3];
int is a primitive type.
Java creates an array of 3 int slots in heap memory.
The reference variable a is stored in the stack.
a[0] = 100; // adding values to array
a[1] = 101;
a[2] = 103;
Stack
Heap
---------------------------------------| a | ---> | [100] [101] [103]
|
----------------------------------------
Stack
Heap
------------------------------------------------| p | ---> | [null] [null] [null]
|
-------------------------------------------------
CONCEPT CHECK
Collection
Java Collection Framework (JCF) Introduced in java 1.2
> Java Collection Framework (JCF) is a UNIFIED ARCHITECTURE that provides a set of
classes and interfaces to store, retrieve, manipulate, and communicate aggregate data
efficiently.
> It was designed to bring all commonly used data structures under one standardized
structure,
making them easier to use, manage, and extend.
┌────────────────────┐
| Iterable (interface)
└────────▲───────────┘
│
extends
│
┌────────────────────────┐
| Collection (interface) |
└───────▲────▲────▲──────┘
│ │ │
extends │ extends
│
┌──────────────── ┐ ┌───────────────┐
┌────────────────────────┐
| List (interface)| | Set (interface)| | Queue (interface)
|
└──────▲──────────┘ └──────▲─────────┘
└────────▲────────────── ┘
│
│
│
implements
implements
implements
│
│
│
┌────────────────────┐ ┌────────────────────┐
┌────────────────────────────┐
| ArrayList (class) | | HashSet (class) | | PriorityQueue (class)
|
|LinkedList (class) | | LinkedHashSet
| | ArrayDeque (class)
|
| Vector (class) | | TreeSet (class) | |
|
| Stack (class)
|
|
What Is Iterable?
Iterable <E> is the root interface in the collection hierarchy. It allows an object to be
traversed
(iterated) through a collection, enabling sequential access to its elements.
Enables traversal of elements using:
Interfaces in JCF
1. Collection
Extends Iterable
Common methods: add(), remove(), contains(), size(), isEmpty(), clear()
2. List
order of insertion is maintained.
Allows duplicates and nulls.
Access via index.
Implementations: ArrayList, LinkedList, Vector, Stack.
3. Set
No duplicates
No guaranteed order (depends on implementation)
Implementations: HashSet, LinkedHashSet, TreeSet
4. Queue
FIFO structure
Used for task scheduling, buffers
// Allowed
[Link](123);
// Compile-time error
Role of Polymorphism:
This is runtime polymorphism in action — where a parent reference (interface) points to
a child object (class) and calls overridden methods.
Legacy Classes:
Vector, Stack, and Hashtable were introduced before Java 1.2
Synchronized but less preferred in modern development
Modern Alternatives:
Use ArrayList instead of Vector
Use Deque instead of Stack
Use ConcurrentHashMap instead of Hashtable for multithreaded use.
Collection - Brain Teasers!
Q1: What’s the difference between Collection and
Collections?
| Collection
| Collections
Q2: How is Uniqueness Maintained in HashSet and
HashMap?
Where did we study hashCode() + equals()?
We studied this in the context of both HashSet and HashMap, because:
Both use HASHING to store data
Both rely on hashCode() and equals() to check for uniqueness
> HashSet:
• Internally uses a HashMap where each element is added as a key.
• [Link]("apple") → becomes → [Link]("apple", PRESENT)
> Before inserting:
• First checks the element’s hashCode() to find the bucket
• Then uses equals() to ensure it’s not a duplicate
> HashMap:
• Keys are unique.
If two keys have the same hashCode(), it uses equals() to resolve collisions.
Duplicate keys overwrite the old value.
Q3: How does HashMap handle collisions?
WWhen two keys have the same hash index, Java creates a LinkedList inside that bucket to
store both entries.
Each new node is added to that list — this is how HashMap handles collisions.
[Link](16, "Java");
[Link](32, "Python"); // if both map to index 0 (16%16 = 0, 32%16 = 0)
Q4: What is the default initial capacity of a HashMap?
16 buckets (0 to 15)
And the load factor is 0.75 → so it resizes when it's 75% full.
Q5. What is the default initial capacity of an ArrayList?
10 elements
When full, it increases capacity by 50%
new capacity = old capacity + (old capacity / 2)
Q5. Which data structure maintains insertion order? Which
doesn’t?
LinkedHashSet?
Q17: Does Set allow duplicates?
No. Set maintains uniqueness using hashCode() and equals().
Q18. Can Set contain null?
HashSet allows one null value.
Q19. How do different Set and Map types handle null?
Q20 How many null keys and values can a HashMap have?
Q21: Difference between HashMap and LinkedHashMap?
Q22. What happens if you insert a duplicate key into a
HashMap?
The new value replaces the old one associated with that key.
Q23. Difference between keySet() and entrySet()?
keySet() returns only keys.
entrySet() returns key-value pairs.
Q24. Convert Set to List?
Set<String> set = new HashSet<>();
List<String> list = new ArrayList<>(set);
Q25. Convert Map keys to List?
List<String> keys = new ArrayList<>([Link]());
Q26. Convert Map values to List?
List<String> values = new ArrayList<>([Link]());
Q27. What is Iterable in Java?
It’s the root interface for all collection classes that can be looped using for-each.
It provides the method: Iterator<T> iterator();
All classes like List, Set, Queue implement Iterable.
Q28. What is the difference between Iterable and Iterator?
| Feature
| Iterable
| Iterator
Q29. How to use Iterator to loop through a List?
List<String> names = new ArrayList<>();
Iterator<String> it = [Link]();
while ([Link]()) {
[Link]([Link]());
}
Q30. What happens if you modify a List while iterating?
why?
ConcurrentModificationException is thrown.
Java’s iterators are fail-fast. That means: If the structure of the list changes while we're iterating
over it, the iterator detects it and fails
immediately to prevent unpredictable behavior.
Q31. How to safely remove elements during iteration ?
Iterator<String> it = [Link]();
while ([Link]()) {
String val = [Link]();
if ([Link]("apple")) { // "apple" is the target value to delete
[Link](); // Safe removal
}
}
Q32. Does Iterator work on Arrays?
No. Arrays do not implement Iterable.
Use traditional for loop or [Link](array) to convert and iterate.
Q33. Is there a difference between iterate() and iterator() in
Java?
| Method
| Is it Exist? | Belongs To
| Purpose
| iterator() | Yes
| iterate() | Yes
collections |
| Iterable
Every class that implements Iterable (like List, Set) must override iterator().
iterate() – Not a valid method in core Java Collections.
[Link]() - exists in Java 8 - used to generate a stream of elements.
Q.34. What are the key methods in the Iterator interface?
Q35. What are the methods of the Iterable interface?
Q36. What is the relationship between Iterable and Iterator
interfaces?
ArrayList
Imagine we are building a Contact App with 100s of contacts.
Can we use an Array?
No — because:
Arrays have a fixed size, which must be declared at the time of creation.
In a contact app, we’ll frequently add or remove contacts, and we don’t know the total
number in advance.
So, arrays are not suitable for such dynamic operations.
> By default, ArrayList creates 10 memory slots (capacity) in the heap to store objects.
> This resizing happens behind the scenes — giving you the power of a flexible array without
the manual effort!
Why Is ArrayList Fast? What is the
Time Complexity ?
Time complexity describes how the performance of an operation scales with the size of the
input.
ArrayList works like a normal array — all elements are stored next to each other in memory.
So when we say get(3), Java knows exactly where the 3rd element is.
It jumps directly to that spot — no searching, no looping.
This direct access makes retrieval very fast, even for large lists — and this happens in
CONSTANT TIME, i.e., O(1).
Time Complexity of ArrayList is O(1).
How to Make an ArrayList Thread-Safe
(Synchronized)?
2 ways - (1) By Using [Link]() & (2)
Using CopyOnWriteArrayList
(1) By Using [Link]()
By using collections utility method synchronizedList and pass the arraylist
List<String> syncList = [Link](new ArrayList<>());
But ArrayList has another drawback: It stores data in contiguous memory locations.
So when we do frequent insertions or deletions (especially in the middle), it becomes slow due
to shifting of elements.
So, What’s the Solution ? LinkedList !
LinkedList is a data structure where nodes are not stored together in memory. Each node just
knows where the next node is.
Why LinkedList Works Better for Certain Scenarios ?
1. Insertion and deletion are faster (no shifting!)
121. It works well even if memory is fragmented (no need for big continuous blocks)
3. Nodes are connected using references, not physical positions
4. Supports bi-directional traversal if it's a Doubly Linked List
What is Time Complexity ?
Time Complexity is a way to measure how efficient a data structure is, based on how the
performance changes when you give it more data to handle.
What is the Time Complexity of linkedList and Why?
Time Complexity of LinkedList is O(1) - Constant Time, when we have a direct reference to the
node or its predecessor.
Operation takes roughly the same amount of time no matter how big the list [Link], Even if the list
gets bigger, Time complexity remains same as O(1), with no extra time
Time Complexity of LinkedList is O(N) - Linear Time, when accessing an element by index or
Searching for an element by value .
Why O(1)?
In a LinkedList
• nodes are NOT stored in continuous memory.
• To insert or delete, we just update the next (and maybe prev) pointers to skip or connect
around the node.
• This pointer adjustment takes **constant time — O(1).
Before deletion:
[10] → [20] → [30]
After deleting 20:
[10] ─────────→ [30]
No shifting. No resizing. Just a simple reference change.
In an ArrayList
• If we want to insert or delete an element from the middle:
• we have to shift all the other elements to fill the gap or make space — which takes O(n)
time.
• This makes the operation O(n) in the worst case.
Singly LinkedList
The reference in each node allows traversal in only one direction (Single direction) —
from the first node (head) to the last node - forward movement.
We can’t go backward because nodes don’t store a reference to the previous node.
Doubly Linked List
In a Doubly Linked List, each node contains references to both the previous and the
next node.
This allows traversal in both directions (bi-directional traversing):
(1) Forward (head → tail)
(2) Backward (tail → head)
Bi-directional traversing makes traversal more efficient compared to a singly linked list.
📌 Note: NOTE :Java's LinkedList class uses Doubly Linked List
structure internally.
┌──────┬────┬──────┐
│ null │ 10 │ 666 │ ─────
└──────┴────┴──────┘
│
Prev D next
│
↑
▼
| 777 (address) ┌──────┬────┬──────┐
│ 777 │ 11 │ null │ last node
└──────┴────┴──────┘
Prev D next
↑
666 (address)
This is the head → No previous node, so Prev = null
Node 1
777 is the memory address of the first node.
Address of previous Node is null because no previous node exists(this is the head)
666 is the pointer to the next node.
Node 2
777 is the address of the previous node (Node 1). It allows backward movement.
11 is the actual data stored.
null in the next field as there is no node after this → so this is the last node (tail).
Circular LinkedList
In a circular linked list, each node stores: Data + reference (pointer) to the next node.
The last node does not point to null, Instead, it points back to the first node.
This creates a continuous loop where, we can keep traversing from one node to the
Data Reference
+-------+-----------+
+-------+-----------+
9 666
10 555
+-------+-----------+
+-------+-----------+
^
V
555 --> +-------+-----------+
+-------+-----------+
+--------------------------------- | 11 | 777 |
+-------+-----------+
Key Notes
● ArrayList provides fast access and update, but is slower for deletions due to
shifting.
● LinkedList excels in insertion and deletion, especially when done at the head or
tail.
Frequently Asked in Interviews
How to reverse a LinkedList ?
Why do we use linkedlist for manipulation and not ArrayList?
When it comes to frequent insertions and deletions, especially in the middle or beginning,
LinkedList outperforms ArrayList due to its internal structure.
##
✅ Key Reason:
• LinkedList uses nodes with pointers — allowing quick changes without shifting
elements.
Deque.
public class LinkedList<E> implements List<E>, Deque<E>
> LinkedList is a class that follow the rules of both List and Deque interfaces.
> That means LinkedList must provide implementations for all the methods declared in both
interfaces.
> Since Deque extends Queue, LinkedList also inherits all the behaviors of a Queue.
// polymorphism or upcasting
1. List is the parent interface, and LinkedList is the child class that implements it.
2. Because of polymorphism (upcasting), we can easily switch between different
List implementations like ArrayList, Vector, LinkedList, etc., without rewriting our
code.
3. This approach is great for flexibility.
4. Our code becomes more reusable and follows good design principles.
> List<String> namesList = new LinkedList<>();
> List<String> namesList = new ArrayList<>();
When we want to use methods that belong specifically to the LinkedList class and the
Deque interface, if we use List as parent type
(1) We can only access methods defined in the List interface.
(2) We cannot access methods that are specific to LinkedList and Deque.
(3) Even though the object is a LinkedList, Java limits method access based on the
reference type — in this case, the parent interface List.
LinkedList<String> namesList = new LinkedList<>();
This gives us full access to all methods of List, Deque, and LinkedList.
List<String> namesList = new LinkedList<>();
This gives us access to only the methods defined in the List interface,as Java limits
access
based on the reference type (List, in this case — the parent interface).
> Insertion in a LinkedList is fast because elements are inserted by simply updating the
links (pointers) between nodes, without shifting other elements as in an ArrayList.
[Link](1, "javascript"); // Inserting the element at INDEX 1
**The node that was previously at index 1 (address 002) moved to index 2, and all the
following
nodes were pushed forward.**
Address: 001
┌────────┬────────────┬────────┐
INDEX 0
│ Prev │ Data │ Next │
│ null │ python │ 005 │
└────────┴────────────┴────────┘
▲
│
│
▼
Address: 005
┌────────┬────────────┬────────┐
INDEX 1
│ Prev │ Data │ Next │
│ 001 │ javascript │ 002 │
└────────┴────────────┴────────┘
▲
│
│
▼
Address: 002
┌────────┬──────────┬────────┐
INDEX 2
│ Prev │ Data │ Next │
│ 005 │ java │ 003 │
└────────┴──────────┴────────┘
▲
│
│
▼
Address: 003
┌────────┬──────────┬────────┐
INDEX 3
│ Prev │ Data │ Next │
│ 002 │ postman │ 004 │
└────────┴──────────┴────────┘
▲
│
│
▼
Address: 004
┌────────┬──────────┬────────┐
INDEX 4
│ Prev │ Data │ Next │
│ 003 │ java │ 111 │
└────────┴──────────┴────────┘
(3) remove(object)
// (3) Removes the first occurrence of the object "java" - returns boolean - indicating
whether the
object was found and removed!
boolean data2 = [Link]("java");
[Link](data2); // Output: true
Note:
Collections store objects, not primitive type.
Java Collections use Generics, which only work with objects — not primitive types. So,
we use wrapper classes like Integer, Double, Character, etc., instead of int, double, or
char.
Revisit Wrapper Classes ➜](../19_Wrapper_Class/[Link])
remove(Object o) returns a boolean, indicating whether the object was found and
removed.
Common Methods
(1) size()
(2) clear()
(1) size()
[Link]([Link]()); // Returns the number of elements in the LinkedList
(2) clear()
[Link](); // Removes all elements from the LinkedList
[Link](namesList); // Output: [] ➤ List is now empty
Method
Return
Type
Description
remove(Object o)
boolean
specified element
contains(Object o)
boolean
addAll(Collection
c)
boolean
Method
Return
Type
Description
get(int index)
Element
set(int index,
val)
Element
remove()
Element
remove(int index)
Element
Return Type
Description
size()
int
clear()
void
What is Deque?
Deque stands for Double Ended Queue.
Double Ended Queue means - we can insert and remove elements from both ends —
front and back.
Deque allows BI-DIRECTIONAL traversal — from front to back and back to front !
right
[Link](null);
null]
Methods in Deque
addFirst(e)
➤ Adds the element at the front of the list
addLast(e)
➤ Adds the element at the end of the list
get(index)
➤ Retrieves the element at the specified index (from List)
getFirst()
➤ Retrieves the first element in the list
getLast()
➤ Retrieves the last element in the list
offerFirst(e) ➤ Adds element at the front and returns true if successful
offerLast(e)
➤ Adds element at the end and returns true if successful
pollFirst()
Brain Teasers
Q1: What’s the difference between addFirst() and
offerFirst()?
Both add an element to the front (head), but for:
addFirst() - return type is void
offerFirst() returns type is boolean
Vector
Hierarchy Diagram
Syntax:
Vector<Integer> vector = new Vector<Integer>();
Key Characteristics
Internally works like an array (with indexed elements).
Resizes automatically: When full, the capacity doubles (2x).
All methods are thread-safe → Safe for multi-threaded environments.
[Link]([Link]());
[Link]([Link]());
[Link]([Link](2));
[Link]([Link](2));
// ------------------------------[Link](3);
[Link](0);
// Legacy method
[Link](new Integer(20));
// Total elements
// Collection
[Link]([Link](myData));
}
}
Stack
+-------------------------+
+-------------------------+
↑
extends
+--------------------------+
+--------------------------+
↑
implements
+-------------------------+
Synchronized | Vector (class)
| Legacy Class (retained only for backward compatibility)
Thread safe +-------------------------+ since java 1
↑
extends
+------------------------+
Synchronized | Stack (class)
| LIFO ,
Thread safe +------------------------+
child of vector
Properties of Stack
Stack is a Linear Data structure from [Link].
It is a re-sizable array.
Follows LIFO - Last in Frst Out principle.
Stack is a child of vector. Since vector is synchronized - Stack is also synchronised( thread
safe).
JVM uses stack data structure for method excecution.
Stack Working - LIFO: Last-In First-Out
Imagine we have 5 books stacked on a table — one over the other.
We place Book 1 on the table.
Then you place Book 2 on top of Book 1.
Then Book 3 on top of Book 2...
Then Book 4, then finally Book 5 on top.
Top of Stack
┌──────────┐ ◄── Book 5 (last inserted)
├──────────┤
├──────────┤
In Stack, the top pointer always points to the last added element.
├──────────┤
├──────────┤
| Book 1 | ◄── Book 1 (first inserted)
└──────────┘
Bottom of Stack
Now if we want to take a book - The only way is to take the top book first.
That means - We take Book 5, then Book 4, then Book 3, and so on...
We CANNOT access Book 1 directly unless we remove all the books above it.
This is how a Stack works in Java - The last item added (Book 5) is the first one to be removed
>> This is called LIFO (Last-In First-Out).
Stack Operations
push() > Add elements to stack.
pop() > Removes and returns the top element.
peek() > Returns the top element without removing it.
empty() > cReturns true if the stack has no elements, otherwise returns false.
search() - Search whether element present in stack or not. > If present,
[Link](element) returns the 1-based position from the top of the stack. > If not
present, it returns -1.
Initial Stack:
After POP:
After PEEK:
┌──────────┐
┌──────────┐
┌──────────┐
| Book 3 | ← Top
| Book 2 | ← Top
| Book 2 | ← Peeked (still in stack)
├──────────┤ [Link]() ├──────────┤ Popped
├──────────┤
[Link]("Kashmir");
[Link]("LinkedHashset is" + Linkedhashset);
Treeset
TreeSet<String> treeSet = new TreeSet<>();
[Link]("India");
[Link]("Scotland");
[Link]("Netherlands");
[Link]("Kashmir");// Kasmir will be added once as set is idempotent
[Link]("Kashmir");
[Link]("Kashmir");
[Link]("Treeset is" + treeSet);
How to retrieve elements from Set?
The Set interface does not provide a get() method — because Sets are **not index-based.
To access elements, we use Iterator (from the Iterator interface) or **enhanced for-each loops.
For each for Iteration
for (String data : treeSet){
[Link](data);
}
Iterator for traversing through set
Iterator<String> dataIterator = [Link]();
while([Link]()){
[Link](dataIterator);
}
Iterator ➤ Declares a variable dataIterator of type Iterator (used to loop through String
elements).
[Link]() ➤ Calls the iterator() method on treeSet. ➤ This method returns an
Iterator object that knows how to go through the TreeSet.
Assignment (=) ➤ The returned Iterator object is stored in the dataIterator variable.
while ([Link]()) ➤ Checks if there is a next element available in the set.
➤ Returns true if there is one, otherwise false.
[Link]() ➤ Retrieves the next element in the set. ➤ Moves the pointer
forward to the next item.
[Link](...) ➤ Prints the current element.
Iterator We are declaring a variable dataIterator of type Iterator Interface — used to loop
through String values. [Link]() This calls the iterator() method on treeSet,
which returns an Iterator object.
Iterator is an interface from [Link].
iterator() is a method inside the Iterable interface.
All collections like List, Set, etc., implement Iterable, so we can use .iterator() on them.
🔸 LinkedHashSet → ✅ Maintains insertion order
🔸 HashSet → ❌ No guaranteed order
🔸 TreeSet → ✅ Sorted order (not insertion order)
Methods in set
// contains() - check whether a particular obj is present or not in the set - returns
boolean
[Link]([Link]("India"));
[Link]([Link]("Delhi"));
[Link]([Link]("Kashmir"));
[Link](hashset);
}
How to access elements in set through
index?
Not possible as set doesn't have any index
Is there a way where
we can access set
elemenst through
index?
Yes, convert it into set and then we can access elements using get().
HashSet<String> hashset = new HashSet<String>();
[Link]("India");
[Link]("Scotland");
[Link]("Netherlands");
[Link]("Kashmir");// Kasmir will be added once as set is idempotent
[Link]("Kashmir");
[Link]("Kashmir");
ArrayList<String> al = new ArrayList<>(hashset);
[Link](al);
[Link]([Link](0));
Internal Working Of
Hashset
We are focusing on understanding the
following 2 points :
(1) How set maintains uniqueness ?**
(2) why hashset does not maintain order?**
To understand how Set maintains uniqueness, it's helpful to learn how Map works
internally. So, Good to have a read about Map first.
> So instead of writing all that logic again, HashSet reuses HashMap internally.
## What happens when we create a Hashset ?
**When we create a HashSet, Java internally creates a HashMap to store its elements**.
// The element (1) added to the HashSet becomes the KEY in the
internal HashMap
+----------------+
+-------------------+
+-----------------------+
▲
PRESENT is a constant(static final Object)
Wanna see - How [Link]() works
internally?
public boolean add(E e) { // e becomes the key (element added in set)
return [Link](e, PRESENT) == null; // add () method of hashset internally calls put()
method of map.
// == null >> Checks if [Link]() return null — meaning it’s a new entry
// Logic:
}
The put() method of HashMap doesn’t directly insert elements.
Since HashSet uses a HashMap internally, calling [Link](e) ends up calling [Link](e,
PRESENT), which in turn calls putVal().
putVal( ) performs the actual insertion into the appropriate bucket.
What putVal() Does?
(1) Calculates the hash
HashCode is a numeric representation of an object.
For numbers, the hashcode is the number itself
(2) Finds the right bucket
Using the formulae - hashCode % capacity
(3) Handles collisions
If another key already exists at the same bucket
(4) Checks for duplicates
Compares using equals() to see if the key already exists
(5) Inserts or updates
Adds a new entry or replaces the existing one if the key matches
Do Elements in a HashSet Maintain
Order?
> No — the elements in a HashSet do not maintain insertion order.
> That’s because:
(1) A HashSet does not support index-based storage like a List or Array.
(2) It stores elements based on their hashcode, not the order in which they were added.
(3) Internally, it uses a HashMap, which organizes elements into buckets based on
hashing rather than positions.
So when we iterate over a HashSet, the order may appear random or shuffled, and it
can vary depending on the hash distribution.
[Link](0);
[Link](1);
[Link](16);
[Link](2);
[Link](32);
[Link](17);
HashSet Class
extends AbstractSet<E>
NOTE:
private transient HashMap<E,Object> map; // The map is the field being marked as
transient.
Do not serialize the map when converting the object to a byte stream (e.g., during file
save or network transfer).
NOTE:
Serialization is the process of converting an object into a byte stream, so it can be: (1)
Saved to a file (2) Transferred over a network
We don’t want to serialize the entire internal structure — like buckets, hashcodes, load
factors, etc.
Instead, Java uses custom serialization logic to write only the actual set elements, not
the full map.
Smaller File Size: Avoids writing unnecessary internal data to file or stream.
static final int hash(Object key) { // Step 1: Get the key's hashCode()
// Otherwise →
// 1. Calculate hashCode and store in 'h'
// 2. Right shift h by 16 bits
// 3. XOR the original and shifted values for better distribution
}
Behind the scenes, HashSet uses a HashMap — and HashMap allows ONE NULL KEY.
When we add null, the key is treated specially, It directly goes to bucket 0 - INDEX 0 of
Only one null is allowed in a Set, because Set doesn’t allow duplicates.
Because a HashSet uses a HashMap to store its elements, the contains() method in
HashSet checks whether the element exists as a key in the internal map.
This means: Lookup is extremely fast, even with large data sets.
The speed is one of the main reasons HashSet is preferred when checking for
existence.
Index (Bucket)
+------+
+----------+----------+----------+----------+
| value | next
+----------+----------+----------+----------+
| 1 |
+------+
| 2 |
Hashcode >> is the numeric representation of an object and hashcode of a
number is number itself.
+------+
| 3 |
+------+
| 4 |
value → A dummy constant object called PRESENT (used to complete the
key–value pair)
+------+.
| 5 |
collision
+------+
| 6 |
+------+
| 7 |
+------+
| 8 | ← Bucket 8
+------+
| 9 |
+------+
| 10 | Each index in the array is called a BUCKET.
+------+
| 11 |
+------+
| 12 | ← Bucket 12
+------+
| 13 |
+------+
| 14 |
+------+
| 15 |. ← Bucket 15
+------+
Each bucket can hold one or more nodes, depending on how many elements map to
the same index.
Each Node contains the following:
final int hashCode; // hashCode → Numeric representation of the key (used to find
the bucket index)
final K key;
V value;
// value → A dummy constant object called PRESENT (used to
complete the key–value pair)
[Link](0);
[Link](1);
[Link](2);
When these elements are added to the HashSet, they are stored internally as shown
below (assuming default capacity = 16):
+------+
+-----------------------+ // hashCode = 0
| 0 | ───────► | [0][0][C][null]
+------+
| // key = 0
+------+
+-----------------------+
| 1 | ───────► | [1][1][C][null]
+------+
+-----------------------+
+------+
+-----------------------+
| 2 | ───────► | [2][2][C][null]
+------+
← hashCode 1, key 1
← hashCode 2, key 2
+-----------------------+
When we add an element to a HashSet, Java internally calculates the bucket index
using:
This remainder determines the bucket index where the element will be stored.
Since the default capacity is 16, the available bucket indexes range from 0 to 15.
A hash collision occurs when two different keys produce the same hash index, meaning
they are assigned to the same bucket in the hash table.
When two elements map to the same bucket, Java stores them as nodes in a linked list
inside that bucket.
+------+
+----------+----------+----------+----------+
| value | next
|.
C = dummy
+----------+----------+----------+----------+
| 0
| 888
the bucket
+----------+----------+----------+----------+
↓
+----------+----------+----------+----------+ Each bucket can store one or
more entries (as nodes), especially
| 0
| 16
| null
| in case of collisions.
+----------+----------+----------+----------+
↑
(888 = memory address of next node)
> If the number of entries in a single bucket exceeds a threshold, the linked list is
automatically converted into a balanced tree .
A HashSet in Java uses HASHING to determine where to store elements and equality
checks to avoid duplicates.
This is done using two important methods: (1) hashcode() (2) equals()
This hash is used to determine the bucket/index where the element should go in
memory
(2) equals() - equals() checks if two objects are logically equal, even if they land in
the
same bucket due to matching or colliding hashCode() values.
hashCode() is just a number used for bucket placement — and it’s possible for two
different objects to return the same hashcode. This is called a hash collision.
That’s why Java calls equals() to confirm whether the objects are truly duplicates before
rejecting the second one.
For proper functioning - hashCode() and equals() must work together to ensure
uniqueness in a HashSet. - That’s how HashSet avoids storing duplicate elements.
This means: when the number of elements exceeds 75% of the capacity, the HashSet
will resize (usually by doubling the capacity).
All existing elements are rehashed (i.e., their bucket positions are recalculated).
They are placed into new buckets based on the new capacity.
This helps maintain constant-time performance for operations like add(), contains(), and
remove().
This process is automatic and internal — developers don't need to manually resize the
set.
Example:
If initial capacity = 16
Then 16 × 0.75 = 12
Comparison: HashSet vs
LinkedHashSet vs TreeSet
| Feature
TreeSet
| HashSet
| LinkedHashSet
|----------------------|----------------------------------|-------------------------------
-----------------|-------------------Order Maintained?
Yes (sorted order)
| No
Internal Structure
| Red-Black Tree
| HashMap
Time Complexity
O(log n)
| O(1) average
| O(1) average
|Null Allowed?
| Yes (only one null allowed)
No (throws `NullPointerException`)
| Yes
Use Case
| Fast lookups with no ordering
access
| Maintain sorted order
Summary
🔹HashSet and HashMap are classes in Java that implement the Set and Map
interfaces respectively.
🔹 The internal map is marked transient to avoid being serialized with all internal
structure.
🔹 Time Complexity for operations like add(), remove(), contains() = **O(1)** (on
average)
🔹 Compared to:
- LinkedHashSet → maintains insertion order
- TreeSet → maintains sorted order, slower operations (O(log n))
🔹 Use a HashSet when : We need fast search, insertion, and deletion (average time
complexity: O(1))
Map
> Map is part of the Java Collections Framework, but it DOES NOT EXTEND the
COLLECTION INTERFACE.
> This is because Map stores KEY - VALUE Pairs, unlike other collections like `List` or
`Set`, which store individual elements.
Map Hierarchy
NOTE :
Hashmap (class) implements Map (interface)
(1) The class gets access to all the method signatures in the interface.
(2) The class must write the actual logic (method bodies) for them.
What is a Map ?
> Map is an interface that represents an associative array. Association is between KEY
& VALUE.
+------------------+
| Map (Key, Value) |
+------------------+
> Whenever we want to retrieve the value, we use the KEY that is associated with that
VALUE. This is called LOOK UP OPERATION (Retrieval).
> LOOK UP happens at super speed.
> Perfomance of get() or the LOOK UP opearation is O(1) > Time Complexity.
> Example use: Counting frequencies, lookups (dictionary).
What is Timecomplexity O(1) means?
> No matter how many key-value pairs are stored in the map, whether it's 10, 100,
1000, or even 1 million.
> Retrieving a value using a KEY will take the same amount of time > So
TimeComplexity is Constant - O(1)
What is Hashing ? How Hashing
Works in HashMap ?
Hashing is the process of converting a LARGE OBJECT into a FIXED LENGTH
INTEGER VALUE — called hashCode.
This integer value is the bucket index where the NODE will be stored ( KEY & VALUE)
Reduces collisions and enables fast retrieval.
bucketIndex = hashCode % capacity;
Example: Hashcode of an Integer Object
Integer i = 10;
+-----------+-------------+
[Link]([Link]());
// e.g., 2008614266
[Link]([Link]());
// 2008614266
}
}
Why this proves the CONTRACT ?
Both s1 and s2 have the same content
So .equals() returns true
And their hashCode() is also the same
This proves:If two objects are equal using .equals() -> they must have the same
hashCode().
Why Should We Override Both in
Custom Classes?
Whenever we create a custom class (like Student, Employee, etc.) and plan to use it in
hash-based collections like HashMap or HashSet,
we must override both equals() and hashCode().
.equals() → defines logical equality (based on instance variables)
.hashCode() → ensures equal objects go to the same bucket
> Overriding equals() and hashCode() ensures:
>>Objects with same values are treated as equal
>>Hash-based collections work as expected
What Happens If We Don’t Override?
[Link]([Link](s2)); // false
[Link]([Link]() == [Link]()); // maybe false
HashMap will treat s1 and s2 as different keys,even though they "look" the same!
Overriding both makes sure our object behaves correctly in collections.
[Link]() vs [Link]() –
What’s The Difference?
When overriding the hashCode() method in our custom class, we typically choose
between two approaches:
1. Use the default hashCode() method inherited from the Object class.
2. Use the utility method [Link]() introduced in Java 7
What is [Link]() - The
Default Method !
[Link]() – from [Link]
Object is the parent of all classes in Java.
Default method inherited by every class in Java.
Returns a hash based on the memory address (if not overridden).
Can lead to more collisions unless overridden properly.
Not null-safe.
Usually not used directly in custom hash logic.
Object obj = new Object();
[Link]([Link]()); // Prints the default hashCode based on memory
address
@Override
public int hashCode() {
return [Link](id, name);
}
// Two Employee objects are equal if their id and name are the same
// To generate the following - Right-click inside the class > Select Generate → equals()
and hashCode()
@Override
public boolean equals(Object o) {
if (this == o) return true; // If both references point to same object
if (!(o instanceof Employee)) return false; // If not same class, return false
Employee e = (Employee) o;
return [Link] == [Link] && [Link]([Link]);
}
}
-------------------------------------------------------------------------------------------------------------------
public class EmployeeRunner {
public static void main(String[] args) {
[Link]([Link]());
// Same as e2
[Link]([Link]());
// Same as e1
[Link]([Link](e2));
// true
}
}
Internal Working Of Map !
> Map consists of an array/table which has a size of 16, with index from 0 to 15.
> Each index is called BUCKET.
> When we create a hashmap - HashMap <Integer,String> hmap = new HashMap(),
the follwing table will be created internally!
HashMap Memory Layout !
Index (Bucket)
+------+
NODE - The actual object stored inside a bucket
+----------+----------+----------+----------+
| 0 | ----> | hashCode | key
+------+
| value | next
| <-- Elements in a NODE.
+----------+----------+----------+----------+
+------+
Hashcode >> is the numeric representation of an object and hashcode of a
number is number itself.
+------+
Key → The element added to the HashSet
+------+
value → A dummy constant object called PRESENT (used to complete the
key–value pair)
+------+.
collision
+------+
----------------------------
NODE !
NODE is the the actual object stored inside a bucket.
Each NODE contains the following:
class Node <K, V> {
final int hashCode; // hashCode → Numeric representation of the key (used to find
the bucket index)
final K key;
V value;
}
How to Insert an Element Inside a
HashMap?
Syntax:
[Link](10, "java");
Internally, this calls:
public V put(K key, V value)
When we call [Link](10, "java"), it triggers the put(K key, V value) method defined in
the Map interface and implemented by the HashMap class.
The HashMap then calculates the hash code for the key, finds the correct bucket, and
stores the key-value pair as a Node in that location.
Step-by-Step Internal Process:
Step 1: Calculate the hash of the key
hash(10) = 10 // In Java, Integer's hashCode() returns the number itself
Step 2: Create a Node at bucket index 10.
BUCKET
+------+
+----------+----------+----------+----------+
| 10 | ──────────────► | hashCode | key
+------+
| 10
| 10
| java
| null
| value | next
+----------+----------+----------+----------
Inserting Another Key:
Why Modulus (or Bitwise AND)?
We often explain HashMap index calculation using %, because it's easy to understand.
Internally (in real HashMap source code): Java actually uses bitwise AND (&) instead of
% to keep the index within range (0 to 15)
hash & (n - 1) Where n = array length (e.g., 16) // instead of hash % size
32 & (16 - 1) = 32 & 15 = 0
Why Java Uses & Instead of %? Because & is much faster than %
HashMap always maintains its array size as a power of 2 (16, 32, etc.), so hash % size
and hash & (size - 1) will always give the same result.
Collision Example
[Link](64, "RestAssured");
hash(64) = 64
64 % 16 = 0
Bucket index = 0, but index 0 already has key 32.
Both 32 and 64 are stored in the same bucket as a linked list chain >> Collision Occurs!
What is a Collision?
A collision happens when two different KEYS map to the same bucket index after
hashing.
In this case: Keys: 32 and 64
Same bucket: 0
What happens when collision occurs?
Collision will results in creating a linkedlist .
When multiple keys generate the same bucket index (after applying hash function),
Java handles this by creating a LINKEDLIST at that index.
Each new (key, value) pair is added as a NODE in the LinkedList.
BUCKET
+------+
+----------+----------+----------+----------+
| 0 | ──────────────► | hashCode | key
| value | next
+------+
|0
| 32
| python | ──┐
+----------+----------+----------+
│
▼
+----------+----------+-------------+----------+
| hashCode | key
|0
| 64
| value
| next
| RestAssured | null
+----------+----------+----------+--------------+
How to retrieve value from Linkedlist ?
[Link](101, "A");
[Link](102, "R");
[Link](103, "S");
// ---------------------------------------
// hashCode() returns a number used by Java to decide where to store the entry
// To find the bucket index → Java does: hash % arraySize (usually 16 initially)
// So keys 101, 102, and 103 are stored at different buckets based on the result
[Link]("Traversing employeeMap:");
for ([Link]<Integer, String> entry : [Link]()) {
// ---------------------------------------
}
}
Summary
| Method
| Purpose
| get(key)
| Retrieves the value for a given key
| keySet()
| Returns a Set of all keys
| entrySet()
Comparisons
(1) Local Variable Vs Instance Variable
| Feature
| Local Variable
| Instance Variable
a class
| Declared inside a method or block
| Declared inside
| Scope
Accessible by all methods of the class (via object)
memory
| Stored in stack memory
| Stored in heap
| Default Value
| No default value, must be initialized before use
values (e.g., null, 0, false)
as the object exists
| Default
| Exists as long
| Access Modifier.
| No access modifier
modifiers (public, private, protected)
| Can have access
| Storage Location
(object)
| Stored in the heap
| Stored in the stack
| Connection to Heap.
| No connection to heap
variable in the stack points to the object in the heap
| Reference
------------------------------------------------------------------------------------------------------------------------------------
------------------------
Key points
Local variables must be explicitly initialized before they can be accessed. If they are not
initialized, trying to access them will lead to a compiler error.
Instance variables do not require initialization explicitly, they will be initialized to default
values when the object is created.
int x; // Declaring local variable without initializing
[Link](x); // Error: variable x might not have been initialized
(2) Constructors Vs Setters
Constructor
Setter
Special method with the SAME NAME
AS CLASS
NORMAL METHOD follows
camelCase naming.
Has NO RETURN TYPE
Has a return type (commonly void).
Called ONCE during object creation.
Can be called multiple times after
object creation.
Used to INITIALIZE instance variables.
Used to UPDATE/CHANGE
instance variables.
If only constructors are used → object
is Immutable (values fixed after
creation).
Using setters makes object Mutable
(values can be updated).
Can be overloaded (multiple
constructors with different parameters).
Setters are not overloaded, but
multiple setters exist (one per
variable).
Values are assigned only once when
the object is created.
Values can be updated any number
of times.
class Person {
String name;
Person(String name) {