Phase 1
Java Fundamentals & JVM Execution Model
📂 Module 1_ Problem Solving & Java programming \\ 01_Java_Introduction_Demos
1.1 What is Java? The "Write Once, Run Anywhere" (WORA) Architecture
📖 Cay S. Horstmann • Core Java Volume I — Chapter 4, Section 4.5 ("Method Parameters")
“In computer science folklore, it is often said that Java passes objects 'by reference'. Many programmers believe this. This is untrue. In Java, a method cannot modify any parameter variable that is passed to it. Java always uses call by value.”
- Axiom 1Primitives: Copied directly. Changes inside method frame have zero effect on caller variables.
- Axiom 2Object References: Reference address is copied. Calling mutators affects the shared heap object.
- Axiom 3Reassignment: Reassigning the parameter reference only changes the local frame copy; caller variable remains unchanged.
⚡ Pro-Tip / Interview Memory Hook: The Hotel Key Card Analogy
Java photocopies the room key card. The cleaner can repaint the room walls blue, but throwing away their copy for Room 202 does NOT change YOUR key card back in main()!
💡 Real-World Analogy: The Universal Translator
Imagine you write a book in English. If you want people in Germany, Japan, and France to read it:
• The C/C++ Way: You hire three separate translators to translate your book into German, Japanese, and French beforehand. If you change one paragraph, you must re-translate all three native books from scratch!
• The Java Way (WORA): You translate your English book into an intermediate, universal language called Bytecode. Then, anyone who has a local interpreter (the JVM) on their computer can read that same Bytecode instantly! You compile your code only once, and it runs anywhere a JVM exists.
• The C/C++ Way: You hire three separate translators to translate your book into German, Japanese, and French beforehand. If you change one paragraph, you must re-translate all three native books from scratch!
• The Java Way (WORA): You translate your English book into an intermediate, universal language called Bytecode. Then, anyone who has a local interpreter (the JVM) on their computer can read that same Bytecode instantly! You compile your code only once, and it runs anywhere a JVM exists.
📖 Formal Definition: Java Platform Independence
Java is a high-level, class-based, object-oriented, concurrent language. Platform independence is achieved not by compiling to machine-specific native assembly (like x86_64 or ARM64 machine code), but by translating source code (
.java) into an intermediate, architecture-neutral instruction set called Java Bytecode (.class files). The Java Virtual Machine (JVM) translates this bytecode into CPU-specific native instructions at runtime.
🖊 Architectural Flow: Source Code → Bytecode → JVM Execution [Core Java Vol I Ch 1-2]
Mermaid Diagram
graph TD
Source["Java Source File (.java)
Human-readable OOP code"] Compiler["javac Compiler
(Compiles to architecture-neutral Bytecode)"] Bytecode["Java Bytecode (.class file)
Portable OpCodes"] subgraph JVM ["JVM Runtime Environment (Platform Dependent)"] ClassLoader["Class Loader Subsystem
(Loads, Links & Initializes classes)"] Verifier["Bytecode Verifier
(Validates memory safety & security)"] subgraph Engine ["Execution Engine"] Interpreter["Interpreter
(Fast line-by-line initial execution)"] JIT["JIT Compiler (HotSpot C1 / C2)
(Compiles hot loops into native machine code)"] GC["Garbage Collector
(Reclaims unused heap memory)"] end end OS_HW["Target CPU Architecture & Operating System
(Windows x64, Linux ARM64, macOS Apple Silicon)"] Source -->|javac Hello.java| Compiler Compiler --> Bytecode Bytecode -->|java Hello| ClassLoader ClassLoader --> Verifier Verifier --> Interpreter Interpreter -.->|Hot code detected| JIT JIT --> OS_HW Interpreter --> OS_HW
Human-readable OOP code"] Compiler["javac Compiler
(Compiles to architecture-neutral Bytecode)"] Bytecode["Java Bytecode (.class file)
Portable OpCodes"] subgraph JVM ["JVM Runtime Environment (Platform Dependent)"] ClassLoader["Class Loader Subsystem
(Loads, Links & Initializes classes)"] Verifier["Bytecode Verifier
(Validates memory safety & security)"] subgraph Engine ["Execution Engine"] Interpreter["Interpreter
(Fast line-by-line initial execution)"] JIT["JIT Compiler (HotSpot C1 / C2)
(Compiles hot loops into native machine code)"] GC["Garbage Collector
(Reclaims unused heap memory)"] end end OS_HW["Target CPU Architecture & Operating System
(Windows x64, Linux ARM64, macOS Apple Silicon)"] Source -->|javac Hello.java| Compiler Compiler --> Bytecode Bytecode -->|java Hello| ClassLoader ClassLoader --> Verifier Verifier --> Interpreter Interpreter -.->|Hot code detected| JIT JIT --> OS_HW Interpreter --> OS_HW
💡 Real-World Analogy: JDK vs JRE vs JVM (The Restaurant Kitchen)
• JVM (The Chef): The actual cook who reads the recipe (bytecode) and cooks the meal on the spot. Without the chef, no cooking happens.
• JRE (The Dining Restaurant): The Chef (JVM) + the kitchen tools, standard spices, and plates (Java Class Libraries). It has everything needed to serve meals to customers, but you cannot invent or develop new recipes here.
• JDK (The Culinary School & Recipe Lab): The entire restaurant (JRE) + chef training tools, recipe notebooks, knives, and measuring instruments (
• JRE (The Dining Restaurant): The Chef (JVM) + the kitchen tools, standard spices, and plates (Java Class Libraries). It has everything needed to serve meals to customers, but you cannot invent or develop new recipes here.
• JDK (The Culinary School & Recipe Lab): The entire restaurant (JRE) + chef training tools, recipe notebooks, knives, and measuring instruments (
javac, jshell, jdb debugger). Developers need the JDK to build software.
| Component | Full Name | What it Contains | Who Needs It? |
|---|---|---|---|
| JVM | Java Virtual Machine | Execution engine (Interpreter + JIT Compiler + Garbage Collector) and runtime data areas (Heap, Stack, Metaspace). | Target machine executing bytecode. |
| JRE | Java Runtime Environment | JVM + Java Core Class Libraries (java.base, java.util, etc.) and configuration files. |
End users who only want to run pre-compiled Java applications. |
| JDK | Java Development Kit | JRE + Developer tools: javac (compiler), jshell (REPL), jdb (debugger), javap (disassembler), jar (archiver). |
Software engineers writing, debugging, and compiling Java code. |
❓ Interview Question: Is Java 100% compiled or 100% interpreted?
Simple Explanation: Java is both!
1. First step (Compilation): The
2. Second step (Interpretation & JIT): When you run the program, the JVM's interpreter starts executing bytecode instructions line-by-line right away so your app boots up quickly.
3. HotSpot Optimization: As your application runs, the JVM monitors which methods or loops run thousands of times (called "hot spots"). The Just-In-Time (JIT) compiler compiles those hot spots directly into native CPU machine code so they run at blistering, near-native C++ speeds.
1. First step (Compilation): The
javac compiler translates human-written .java code into .class bytecode.
2. Second step (Interpretation & JIT): When you run the program, the JVM's interpreter starts executing bytecode instructions line-by-line right away so your app boots up quickly.
3. HotSpot Optimization: As your application runs, the JVM monitors which methods or loops run thousands of times (called "hot spots"). The Just-In-Time (JIT) compiler compiles those hot spots directly into native CPU machine code so they run at blistering, near-native C++ speeds.
1.2 Structure of a Java Program, the main() Method & Naming Conventions
💡 Why is main() declared as "public static void main(String[] args)"?
Think of the JVM as a security guard outside your building. When you tell the guard to launch your application:
• public: The guard is standing outside your package. If the entrance is marked
• static: The guard does NOT have to construct an instance of your building in memory just to ring the doorbell. A
• void: When the program finishes, it doesn't return a value to the caller. Process exit codes are communicated via
• String[] args: The parameters you pass from the command terminal (e.g.
• public: The guard is standing outside your package. If the entrance is marked
private or protected, the guard is locked out and cannot start the program!
• static: The guard does NOT have to construct an instance of your building in memory just to ring the doorbell. A
static method exists at class-level and can be invoked immediately without creating an object on the heap.
• void: When the program finishes, it doesn't return a value to the caller. Process exit codes are communicated via
System.exit(code).
• String[] args: The parameters you pass from the command terminal (e.g.
java App dev 8080) are captured as an array of strings.
📂 Module 1.../01_Java_Introduction_Demos/JavaIntroductionDemos/src/com/accenture/ltt/codingstandards/PasswordValidator.java
package com.accenture.ltt.codingstandards;
/**
* Standard Production Class Demonstrating Java Naming Conventions:
* - UpperCamelCase for Classes and Interfaces (PasswordValidator)
* - lowerCamelCase for Methods and Variables (validatePassword, userPassword)
* - UPPER_SNAKE_CASE for Constants (MIN_PASSWORD_LENGTH)
*/
public class PasswordValidator {
// Constant: static final in UPPER_SNAKE_CASE
private static final int MIN_PASSWORD_LENGTH = 8;
/**
* Validates if a password string meets length criteria.
* @param password the password input to check
* @return true if valid, false otherwise
*/
public boolean validatePassword(String password) {
// Always defend against NullPointerException
if (password == null) {
return false;
}
return password.length() >= MIN_PASSWORD_LENGTH;
}
public static void main(String[] args) {
PasswordValidator validator = new PasswordValidator();
// Read from command-line argument if provided, otherwise use fallback
String testPassword = (args.length > 0) ? args[0] : "Secur3P@ssw0rd";
boolean isValid = validator.validatePassword(testPassword);
System.out.println("Password valid? " + isValid);
}
}
👁 Line-by-Line Code Breakdown & Explanation
-
Line 1
package com.accenture.ltt.codingstandards;— Defines the namespace package. Prevents class naming collisions across large enterprise codebases. -
Line 12
private static final int MIN_PASSWORD_LENGTH = 8;— A class constant.staticmeans only 1 copy exists in memory.finalmeans its value can never be reassigned. -
Line 20
if (password == null) { return false; }— Defensive Programming. Calling.length()on a null reference would crash your server with aNullPointerException. -
Line 30
(args.length > 0) ? args[0] : "..."— Uses the ternary operator to check if command-line arguments were supplied inargsbefore attempting array indexing.
1.3 Variables, Data Types, Memory Allocation & The Pass-By-Value Proof
💡 Real-World Analogy: Stack Memory vs Heap Memory
• Stack Memory (The Sticky Notepad on your desk): Fast, organized, temporary. Every time a method is called, Java slaps a fresh sticky note (Stack Frame) on your notepad. Local variables (
• Heap Memory (The Big Storage Warehouse): Large, flexible, shared. Whenever you say
int x = 5) are written right on the note. When the method finishes, the sticky note is ripped off and thrown in the trash instantly!
• Heap Memory (The Big Storage Warehouse): Large, flexible, shared. Whenever you say
new Employee(), a large box is placed in the warehouse. Your sticky note (Stack) doesn't hold the whole box; it only writes down the warehouse aisle address (e.g. Aisle 4, Shelf 12) — that's an Object Reference.
🖊 Architectural Memory Model: Stack Call Frames vs Heap Objects
Mermaid Diagram
graph LR
subgraph StackSpace ["Thread Stack (LIFO per Method Call)"]
Frame1["Stack Frame: main()
- int age = 28
- double salary = 95000.0
- Employee empRef = 0x55AA (Memory Address)"] end subgraph HeapSpace ["Java Heap Space (Shared Object Storage)"] EmpObject["Employee Object @ 0x55AA
- String name = 'Alice'
- int id = 101"] end Frame1 -->|Reference Address Pointer| EmpObject
- int age = 28
- double salary = 95000.0
- Employee empRef = 0x55AA (Memory Address)"] end subgraph HeapSpace ["Java Heap Space (Shared Object Storage)"] EmpObject["Employee Object @ 0x55AA
- String name = 'Alice'
- int id = 101"] end Frame1 -->|Reference Address Pointer| EmpObject
📖 Master Class: Why Java is ALWAYS Strictly Pass-by-Value (Explained Simply)
One of the most failed interview questions in Java is: "Does Java pass objects by reference or by value?"
Many developers answer: "Primitives are passed by value, but objects are passed by reference."
THIS ANSWER IS 100% WRONG! In Java, everything is strictly passed by value, with zero exceptions.
🔑 The Hotel Key Card Analogy (Understand this once, remember forever)
Imagine you check into a hotel room (Room 101).
• The Hotel Room itself is the Object living on the Heap.
• The Plastic Key Card in your wallet is the Reference Variable on your Stack. The key card holds the address code to Room 101.
Now, you call a method:
1. Java makes a PHOTOCOPY of your key card! It gives the copy to the cleaner.
2. The cleaner takes their copy, goes to Room 101, opens the door, and repaints the walls blue (
3. When the method finishes, you walk back to Room 101 with your original key card. Are the walls blue? YES! Because both key cards unlocked the exact same physical room.
4. NOW HERE IS THE CRUCIAL TEST: Suppose while in the method, the cleaner throws their key card in the bin and gets a brand-new key card to Room 202 (
Does YOUR key card back in main() suddenly change to Room 202?
OF COURSE NOT! Your key card still opens Room 101! The cleaner only changed their copy of the key card.
Conclusion: Java did NOT pass the room by reference. Java copied the key card (the address value) and passed the copy by value!
• The Hotel Room itself is the Object living on the Heap.
• The Plastic Key Card in your wallet is the Reference Variable on your Stack. The key card holds the address code to Room 101.
Now, you call a method:
cleanRoom(keyCard). What does Java do?
1. Java makes a PHOTOCOPY of your key card! It gives the copy to the cleaner.
2. The cleaner takes their copy, goes to Room 101, opens the door, and repaints the walls blue (
room.setColor("blue")).
3. When the method finishes, you walk back to Room 101 with your original key card. Are the walls blue? YES! Because both key cards unlocked the exact same physical room.
4. NOW HERE IS THE CRUCIAL TEST: Suppose while in the method, the cleaner throws their key card in the bin and gets a brand-new key card to Room 202 (
room = new Room(202)).
Does YOUR key card back in main() suddenly change to Room 202?
OF COURSE NOT! Your key card still opens Room 101! The cleaner only changed their copy of the key card.
Conclusion: Java did NOT pass the room by reference. Java copied the key card (the address value) and passed the copy by value!
📂 PassByValueProof.java — The Definitive Code Proof
public class PassByValueProof {
static class Dog {
String name;
Dog(String name) { this.name = name; }
}
public static void modifyDog(Dog d) {
// 1. Mutating the object through the copied reference
d.name = "Max (Renamed)";
// 2. Attempting to reassign the reference to a brand new object
d = new Dog("Rocky (New Dog)");
System.out.println("Inside method, dog name is: " + d.name);
}
public static void main(String[] args) {
Dog myDog = new Dog("Buddy");
System.out.println("1. Before method call: " + myDog.name);
modifyDog(myDog);
// What will myDog.name print here?
System.out.println("2. After method call: " + myDog.name);
}
}
👁 Output & Step-by-Step Execution Proof
Console Output:
1. Before method call: Buddy
Inside method, dog name is: Rocky (New Dog)
2. After method call: Max (Renamed) <--- NOT Rocky!
-
Step 1
In
main(),myDogis created. It holds the address0x100on the stack pointing to the Dog object named "Buddy" on the heap. -
Step 2
When
modifyDog(myDog)is called, Java makes a copy of address0x100and puts it into parameter variabledin a new stack frame. -
Step 3
d.name = "Max (Renamed)"follows address0x100to the heap and updates the name field. That change is visible everywhere. -
Step 4
d = new Dog("Rocky")creates a new object at address0x200. Variabledon the method's stack frame now points to0x200. ButmyDogback inmain()STILL holds0x100! -
Step 5
When
modifyDog()exits, its stack frame is discarded.myDoginmain()is completely untouched by the reassignment. That is conclusive proof that Java is pass-by-value!
| Primitive Type | Bit Width | Byte Size | Value Range | Default Value | Wrapper Class |
|---|---|---|---|---|---|
byte | 8-bit | 1 byte | -128 to 127 | 0 | java.lang.Byte |
short | 16-bit | 2 bytes | -32,768 to 32,767 | 0 | java.lang.Short |
int | 32-bit | 4 bytes | -231 to 231-1 (~ ±2.14B) | 0 | java.lang.Integer |
long | 64-bit | 8 bytes | -263 to 263-1 (suffix L) | 0L | java.lang.Long |
float | 32-bit | 4 bytes | IEEE 754 floating point (suffix f) | 0.0f | java.lang.Float |
double | 64-bit | 8 bytes | IEEE 754 64-bit double precision | 0.0d | java.lang.Double |
char | 16-bit | 2 bytes | 0 to 65,535 (Unicode UTF-16 code units) | '\\u0000' | java.lang.Character |
boolean | JVM-dep | 1 bit logical | true or false | false | java.lang.Boolean |
📂 Module 1.../01_Java_Introduction_Demos/JavaIntroductionDemos/src/com/accenture/ltt/DatatypeDemo.java
package com.accenture.ltt;
public class DatatypeDemo {
public static void main(String[] args) {
int number = 25; // 32-bit integer
double price = 19.99; // 64-bit IEEE 754 float
char letter = 'A'; // 16-bit Unicode character
boolean flag = true; // 1-bit boolean flag
// Widening Type Casting (Implicit conversion: int -> double, no precision loss)
double widenedVal = number;
// Narrowing Type Casting (Explicit cast: truncates decimal 19.99 to 19)
int truncatedPrice = (int) price;
// Autoboxing (Primitive int automatically wrapped in java.lang.Integer)
Integer boxed = number;
// Unboxing (Object unwrapped back to primitive)
int unboxed = boxed;
// Local Variable Type Inference (Java 10+ var keyword)
var message = "Inferred as java.lang.String at compile time";
System.out.println("Widened: " + widenedVal + " | Truncated: " + truncatedPrice);
System.out.println("Boxed: " + boxed + " | Inferred: " + message);
}
}
👁 Type Casting & Conversions Explained
-
Widening (Implicit)
Moving from smaller type to larger type (e.g.
int→double). Like pouring water from a 1-liter bottle into a 5-liter bucket: guaranteed safe, no overflow, handled automatically by the compiler. -
Narrowing (Explicit)
Moving from larger type to smaller type (e.g.
double→int). Like pouring a 5-liter bucket of water into a 1-liter bottle: water will spill! Java forces you to explicitly write(int) price, acknowledging that decimals will be truncated (19.99 becomes 19). -
Autoboxing & Unboxing
Introduced in Java 5. The compiler automatically converts primitives (
int) to their wrapper objects (Integer) and back. Warning for Backend Engineers: Unboxing anullwrapper object (e.g.Integer val = null; int x = val;) throws a hiddenNullPointerException! - var (Local Type Inference) Introduced in Java 10. Java is STILL statically typed! The compiler infers the exact type at compile time based on the right-hand value. It is NOT dynamic typing like JavaScript.
1.4 Operators, Bitwise Shifts & Precedence Hierarchy
💡 Real-World Analogy: Short-Circuit AND (&&) vs Normal AND (&)
Imagine you have a security checklist: 1. Does the user have a VIP badge? AND 2. Call the manager to verify.
• Short-circuit
• Bitwise / Non-short-circuit
• Short-circuit
&&: If step 1 fails (user has NO VIP badge), there is zero reason to call the manager. The overall result is already false, so Java skips evaluating the second expression! This prevents null pointer crashes: if (user != null && user.isActive()).
• Bitwise / Non-short-circuit
&: Checks both conditions unconditionally, even if the first was already false.
📂 Module 1.../01_Java_Introduction_Demos/JavaIntroductionDemos/src/com/accenture/ltt/OperatorDemo.java
package com.accenture.ltt;
public class OperatorDemo {
public static void main(String[] args) {
int a = 10, b = 5;
// Arithmetic operators
System.out.println("Addition: " + (a + b)); // 15
System.out.println("Multiplication: " + (a * b)); // 50
// Relational operator
System.out.println("Is a > b? " + (a > b)); // true
// Short-circuit Logical AND (b < 10 is evaluated ONLY because a > 5 is true)
System.out.println("Logical AND: " + (a > 5 && b < 10)); // true
// Bitwise shift operators: Left shift (a << 1) multiplies by 2
int shifted = a << 1; // 10 * 2 = 20
System.out.println("10 << 1 = " + shifted);
// Ternary conditional operator: (condition) ? if_true : if_false
String status = (a >= 10) ? "Eligible" : "Ineligible";
System.out.println("Status: " + status);
}
}
👁 Line-by-Line Operator Mechanics & In-Memory Evaluation
-
Line 12
(a > 5 && b < 10)— Short-Circuit Evaluation: The JVM evaluatesa > 5first. Since it evaluates totrue, it proceeds to checkb < 10. If the left side werefalse, the right side would NEVER be executed. This is vital in production:if (order != null && order.isValid())will never throw aNullPointerExceptionbecauseorder.isValid()is skipped iforderis null! -
Line 15
a << 1— Bitwise Left Shift: Shifts bits in binary representation of 10 (00001010) 1 position left to become 20 (00010100). Mathematically equivalent to multiplying by2^1. Right shift (>> 1) divides by 2. This is the fastest arithmetic operation at the CPU register level. -
Line 19
(a >= 10) ? "Eligible" : "Ineligible"— Ternary Operator: A compact inlineif-elseexpression. Returns one of two values based on a boolean condition. Preferred in clean Java for simple assignments without verboseif-elsebranching.
1.5 Control Flow, Loops, Labeled Breaks & Switch Pattern Matching
📂 Module 1.../01_Java_Introduction_Demos/JavaIntroductionDemos/src/com/accenture/ltt/LoopDemo.java
package com.accenture.ltt;
public class LoopDemo {
public static void main(String[] args) {
// 1. Classic for loop: Used when exact iteration count is known
for (int i = 1; i <= 3; i++) {
System.out.println("For loop count: " + i);
}
// 2. While loop: Pre-tested condition (might execute 0 times if condition false)
int j = 1;
while (j <= 3) {
System.out.println("While count: " + j);
j++;
}
// 3. Do-While loop: Post-tested condition (GUARANTEED at least 1 execution)
int k = 10;
do {
System.out.println("Do-While executes once even though 10 < 5 is false!");
} while (k < 5);
// 4. Labeled Break: Break completely out of NESTED outer loop directly
searchBlock:
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
if (row == 1 && col == 1) {
System.out.println("Target found at row 1, col 1! Breaking searchBlock...");
break searchBlock; // Exits BOTH loops immediately
}
}
}
}
}
👁 Loop Constructs & Labeled Jump Control Explained
-
For vs While
Use a
forloop when bounds are deterministic. Use awhileloop when waiting for an external event or dynamic state change (e.g. reading from a socket buffer until EOF). -
Do-While Guarantee
The condition
(k < 5)is checked after the body executes. Even if initial conditions fail, the body runs at least once (ideal for interactive CLI menus: prompt user at least once before validating). -
Labeled Break
Line 24:
break searchBlock;— Normally,breakonly exits the innermost loop. By labeling the outer loop (searchBlock:), Java jumps entirely out of both nested loops without needing messy boolean flags!
📂 Module 2_ Java LTS Features/.../PatternSwitchDemo.java — Modern Java 17+ Switch
package com.accenture.ltt.ui.tester.java17;
public class PatternSwitchDemo {
public static void main(String[] args) {
Object obj = "Java 17 LTS Backend";
// Modern Java 17+ Pattern Matching Switch:
// 1. No break statements needed (arrow syntax -> prevents fall-through bugs)
// 2. Directly tests object type and binds cast variable (String s) in 1 step!
// 3. Handles 'null' safely without NullPointerException
String result = switch (obj) {
case String s -> "String of length " + s.length() + ": " + s.toUpperCase();
case Integer i -> "Integer multiplied: " + (i * 2);
case null -> "Safe null handling";
default -> "Other object type: " + obj.getClass().getSimpleName();
};
System.out.println(result);
}
}
👁 Java 17+ Pattern Matching Switch Breakdown
-
Switch as an Expression
Notice
String result = switch (obj) { ... };. The switch evaluates directly to a value and assigns it toresult. In older Java, switch was only a statement requiring mutable accumulator variables outside the block. -
Pattern Matching (Type Test + Cast)
case String s -> ...tests whetherobjis an instance ofStringAND automatically casts it into variablesin one atomic step. Eliminates tedious, bug-prone manual casting (if (obj instanceof String) { String s = (String) obj; }). -
Arrow Syntax (No Fall-Through)
The
->operator executes only the matching branch and immediately returns. Nobreak;statement is required, eliminating 30 years of accidental switch fall-through bugs. -
Safe Null Handling
Historically, passing
nullinto a switch statement threw an immediateNullPointerException. Java 17+ allows an explicitcase null ->branch to cleanly handle missing values.
1.6 Methods, Arrays, Jagged Matrices & Variable-Length Arguments (Varargs)
💡 Real-World Analogy: Jagged Arrays vs Regular 2D Arrays
• Regular 2D Array: A square grid or spreadsheet. Every row has the exact same number of columns (e.g. 3x3).
• Jagged (Ragged) Array: An apartment building where the 1st floor has 2 apartments, the 2nd floor has 5 apartments, and the 3rd floor has 1 penthouse apartment. In Java, a 2D array is simply an "array of arrays" — each inner array can have a completely different length!
• Jagged (Ragged) Array: An apartment building where the 1st floor has 2 apartments, the 2nd floor has 5 apartments, and the 3rd floor has 1 penthouse apartment. In Java, a 2D array is simply an "array of arrays" — each inner array can have a completely different length!
📂 Module 1.../02_Arrays_Strings_Demos/ArraysDemo/src/com/accenture/ltt/ArrayDemo.java
package com.accenture.ltt;
import java.util.Arrays;
public class ArrayDemo {
// Varargs (int... numbers): Under the hood, the compiler creates an int[] array!
public static int calculateSum(String label, int... numbers) {
int total = 0;
for (int n : numbers) {
total += n;
}
System.out.println(label + ": " + total);
return total;
}
public static void main(String[] args) {
// 1. One-dimensional array
int[] scores = { 85, 92, 78, 95, 88 };
// Arrays utility class
Arrays.sort(scores);
System.out.println("Sorted scores: " + Arrays.toString(scores));
// 2. Jagged (Ragged) Multidimensional Array
int[][] jaggedMatrix = new int[3][];
jaggedMatrix[0] = new int[] { 10, 20 }; // 2 elements
jaggedMatrix[1] = new int[] { 30, 40, 50, 60 }; // 4 elements
jaggedMatrix[2] = new int[] { 70 }; // 1 element
// 3. Varargs method call: Can pass 0, 1, or many arguments
calculateSum("Department A Total", 100, 250, 400);
calculateSum("Single Item", 50);
}
}
👁 Arrays, Jagged Matrices & Varargs Mechanics
-
Memory Layout
In Java, arrays are objects stored on the Heap. An array variable (
int[] scores) on the stack holds only a reference to the contiguous memory block on the heap. Accessing an index outside[0, length - 1]throwsArrayIndexOutOfBoundsExceptionat runtime. -
Jagged Array Structure
Line 24:
int[][] jaggedMatrix = new int[3][];— This allocates an outer array of 3 object references on the heap, all initiallynull. Lines 25-27 allocate 3 separate inner arrays of varying lengths (2, 4, 1). This saves significant memory compared to allocating a rigid, oversized rectangular matrix. -
Varargs Compilation
Line 6:
public static int calculateSum(String label, int... numbers)— At compile time,javacreplacesint... numberswith an actual arrayint[] numbers. When called withcalculateSum("Total", 100, 250, 400), Java quietly generatesnew int[]{100, 250, 400}behind the scenes. - Varargs Constraints A method can have only ONE varargs parameter, and it MUST be the LAST parameter in the argument list. Otherwise, the compiler cannot disambiguate where ordinary arguments end and variable arguments begin.
1.7 Method Overloading, Recursion & Call Stack Lifecycle
📂 MethodOverloadingAndRecursionDemo.java — Overloading vs Recursion
public class MethodOverloadingAndRecursionDemo {
// Method Overloading: Same method name, different parameter signatures
public static int multiply(int a, int b) {
return a * b;
}
public static double multiply(double a, double b) {
return a * b;
}
public static int multiply(int a, int b, int c) {
return a * b * c;
}
// Recursion: Method calling itself with a mandatory Base Case
public static long factorial(int n) {
// 1. BASE CASE: Stops infinite recursion and prevents StackOverflowError!
if (n <= 1) {
return 1;
}
// 2. RECURSIVE STEP: Method calls itself with smaller subproblem (n - 1)
return n * factorial(n - 1);
}
public static void main(String[] args) {
// Compile-time polymorphism resolves the exact method from argument types:
System.out.println("Overloaded int: " + multiply(5, 4));
System.out.println("Overloaded double: " + multiply(2.5, 4.0));
// Recursion: 5! = 5 * 4 * 3 * 2 * 1 = 120
System.out.println("Factorial of 5: " + factorial(5));
}
}
👁 Line-by-Line Breakdown: Method Overloading & The Recursive Call Stack
-
Overloading Rule
Overloading is resolved at compile time by
javacbased on method name + parameter types/count. Warning: Changing ONLY the return type (e.g.int foo()vsvoid foo()) is NOT valid overloading and causes a compile error! -
Recursion Base Case
Line 18:
if (n <= 1) return 1;— The crucial exit condition. Every recursive call pushes a new stack frame onto the thread stack. If the base case is omitted or unreachable, stack frames multiply endlessly until the JVM throws a fatalStackOverflowError! -
Stack Unwinding
factorial(5)pauses and callsfactorial(4), which callsfactorial(3)... down tofactorial(1). Once the base case returns 1, each paused frame on the stack multiplies and unwinds back up tomain()yielding 120.