Java Basics — Part 1: Language Fundamentals
"Senior engineers who can't explain stack vs heap or why
Integer == Integerfails at 128 have no business designing distributed systems." — Staff Engineer, Google
Real Incident: The Ariane 5 Explosion (1996)
A 64-bit floating point number was cast to a 16-bit signed integer. The value exceeded 32,767, causing an overflow exception that crashed the inertial guidance system. The $370 million rocket self-destructed 37 seconds after launch. Java's strict type system and runtime overflow behavior exist because of disasters like this. Understanding primitives, type promotion, and overflow isn't academic — it's engineering discipline.
1. Java Platform — JDK vs JRE vs JVM
%%{init: {'theme': 'base', 'themeVariables': {'fontSize': '13px', 'fontFamily': 'Inter, -apple-system, sans-serif'}, 'flowchart': {'nodeSpacing': 30, 'rankSpacing': 50, 'padding': 12, 'curve': 'basis'}, 'sequence': {'actorMargin': 60, 'messageMargin': 40}, 'class': {'padding': 12}}}%%
flowchart LR
JDK["JDK\n= JRE + Dev Tools"] --> JRE["JRE\n= JVM + Libraries"]
JRE --> JVM["JVM\n= Runs Bytecode"]
JDK -.-> T1["javac · jdb\njavadoc · jar"]
JRE -.-> T2["java.lang · util\nio · net · sql"]
JVM -.-> T3["ClassLoader\nJIT · GC"]
style JDK fill:#DBEAFE,stroke:#93C5FD,color:#1E40AF
style JRE fill:#D1FAE5,stroke:#6EE7B7,color:#065F46
style JVM fill:#FEF3C7,stroke:#FCD34D,color:#92400E
style T1 fill:#EFF6FF,stroke:#93C5FD,color:#1E40AF
style T2 fill:#ECFDF5,stroke:#6EE7B7,color:#065F46
style T3 fill:#FFFBEB,stroke:#FCD34D,color:#92400E Compilation Flow — Source to Execution
%%{init: {'theme': 'base', 'themeVariables': {'fontSize': '13px', 'fontFamily': 'Inter, -apple-system, sans-serif'}, 'flowchart': {'nodeSpacing': 30, 'rankSpacing': 50, 'padding': 12, 'curve': 'basis'}, 'sequence': {'actorMargin': 60, 'messageMargin': 40}, 'class': {'padding': 12}}}%%
flowchart LR
A[".java\n(source)"] ==> B[".class\n(bytecode)"]
B ==> C["JVM loads\n& interprets"]
C ==> D["JIT → native\n(hot methods)"]
style A fill:#DBEAFE,stroke:#93C5FD,color:#1E40AF
style B fill:#D1FAE5,stroke:#6EE7B7,color:#065F46
style C fill:#FEF3C7,stroke:#FCD34D,color:#92400E
style D fill:#FEE2E2,stroke:#FCA5A5,color:#991B1B | Term | What It Is | Key Insight |
|---|---|---|
| JDK | Development Kit = JRE + tools (javac, jdb, javadoc, jconsole) | You need this to write Java |
| JRE | Runtime = JVM + core libraries (java.lang, java.util, etc.) | You need this to run Java |
| JVM | Virtual Machine: ClassLoader + Execution Engine + GC | Platform-dependent (one per OS) |
| JIT | Just-In-Time compiler inside JVM | Compiles hot bytecode to native — why Java gets faster over time |
WORA Principle: Java is platform-independent (Write Once, Run Anywhere). The JVM is platform-dependent — each OS has its own implementation. Your .class files run unchanged on any JVM.
Interview Gold
"Java is compiled AND interpreted. javac compiles to bytecode (platform-independent). The JVM interprets bytecode, and the JIT compiler further compiles hot paths to native machine code for performance. This is why long-running Java apps outperform cold starts — the JIT optimizes based on runtime profiling (C1/C2 tiered compilation)."
2. Data Types — All 8 Primitives
Primitive Types (8 total — memorize the sizes)
| Type | Size | Default | Min Value | Max Value | Wrapper |
|---|---|---|---|---|---|
byte | 1 byte | 0 | -128 | 127 | Byte |
short | 2 bytes | 0 | -32,768 | 32,767 | Short |
int | 4 bytes | 0 | -2,147,483,648 | 2,147,483,647 | Integer |
long | 8 bytes | 0L | -9.2 x 10^18 | 9.2 x 10^18 | Long |
float | 4 bytes | 0.0f | ~1.4 x 10^-45 | ~3.4 x 10^38 | Float |
double | 8 bytes | 0.0d | ~4.9 x 10^-324 | ~1.8 x 10^308 | Double |
char | 2 bytes | ' ' | 0 (unsigned) | 65,535 | Character |
boolean | JVM-specific | false | — | — | Boolean |
Reference Types
Everything that is not a primitive: String, arrays, objects, interfaces, enums, records.
String name = "Vamsi"; // reference → String object in pool/heap
int[] numbers = {1, 2, 3}; // reference → array object on heap
Employee emp = new Employee(); // reference → Employee object on heap
List<String> list = null; // reference → nothing (null)
Key difference: Primitives hold the value directly on the stack. References hold the memory address of an object on the heap.
Type Promotion Rules (Widening)
%%{init: {'theme': 'base', 'themeVariables': {'fontSize': '13px', 'fontFamily': 'Inter, -apple-system, sans-serif'}, 'flowchart': {'nodeSpacing': 30, 'rankSpacing': 50, 'padding': 12, 'curve': 'basis'}, 'sequence': {'actorMargin': 60, 'messageMargin': 40}, 'class': {'padding': 12}}}%%
flowchart LR
BYTE["byte"] ==> SHORT["short"]
SHORT ==> INT["int"]
CHAR["char"] ==> INT
INT ==> LONG["long"]
LONG ==> FLOAT["float"]
FLOAT ==> DOUBLE["double"]
style BYTE fill:#DBEAFE,stroke:#93C5FD,color:#1E40AF
style SHORT fill:#DBEAFE,stroke:#93C5FD,color:#1E40AF
style INT fill:#D1FAE5,stroke:#6EE7B7,color:#065F46
style CHAR fill:#FEF3C7,stroke:#FCD34D,color:#92400E
style LONG fill:#D1FAE5,stroke:#6EE7B7,color:#065F46
style FLOAT fill:#FEE2E2,stroke:#FCA5A5,color:#991B1B
style DOUBLE fill:#FEE2E2,stroke:#FCA5A5,color:#991B1B byte b = 10;
int i = b; // OK: widening (automatic)
byte x = i; // COMPILE ERROR: narrowing requires cast
byte y = (byte) i; // OK: explicit cast (may lose data)
// TRAP: byte + byte = int (promotion to int for arithmetic)
byte a = 10, c = 20;
byte result = a + c; // COMPILE ERROR! a + c is int
byte result = (byte)(a + c); // OK
The float Precision Trap
long to float is widening but loses precision! A long has 64-bit integer precision; a float has only ~7 decimal digits. long l = 123456789L; float f = l; — value becomes 1.23456792E8 (rounded).
3. Variables — Local, Instance, Static
Lifecycle
%%{init: {'theme': 'base', 'themeVariables': {'fontSize': '13px', 'fontFamily': 'Inter, -apple-system, sans-serif'}, 'flowchart': {'nodeSpacing': 30, 'rankSpacing': 50, 'padding': 12, 'curve': 'basis'}, 'sequence': {'actorMargin': 60, 'messageMargin': 40}, 'class': {'padding': 12}}}%%
flowchart LR
S["Static\n(class load → shutdown)"] ~~~ I["Instance\n(new → GC'd)"] ~~~ L["Local\n(method call → return)"]
style S fill:#DBEAFE,stroke:#93C5FD,color:#1E40AF
style I fill:#D1FAE5,stroke:#6EE7B7,color:#065F46
style L fill:#FEF3C7,stroke:#FCD34D,color:#92400E | Type | Declared Where | Stored Where | Default | Must Initialize? |
|---|---|---|---|---|
| Local | Inside method/block | Stack | None | YES (compile error otherwise) |
| Instance | Class body (no static) | Heap (inside object) | Type default (0, null, false) | No |
| Static | Class body (with static) | Metaspace (method area) | Type default | No |
public class VariableDemo {
static int classCount = 0; // static: one per class, lives in Metaspace
private String name; // instance: one per object, lives on heap
private final int id; // instance + final: must be set in constructor
public void process() {
int temp = 42; // local: lives on stack frame
// int x; System.out.println(x); // COMPILE ERROR: local not initialized
}
}
Variable Shadowing
public class Shadow {
int x = 10; // instance variable
public void method(int x) { // parameter shadows instance variable
System.out.println(x); // prints parameter value
System.out.println(this.x); // prints instance variable (10)
int x = 5; // COMPILE ERROR: can't shadow local with another local
}
}
Interview Gold
"Local variables are NOT thread-safe by coincidence — they live on the thread's own stack frame. Instance/static variables live on the heap and are shared, so they need synchronization for thread safety."
4. Operators
Arithmetic, Relational, and Logical
| Category | Operators | Notes |
|---|---|---|
| Arithmetic | + - * / % | / on integers truncates: 7/2 = 3 |
| Relational | == != < > <= >= | == on objects compares references |
| Logical | && || ! | Short-circuit: right side may not execute |
| Bitwise | & | ^ ~ | Operate on individual bits |
| Shift | << >> >>> | >>> is unsigned (fills with 0) |
| Assignment | = += -= *= /= %= <<= >>= | Compound operators include implicit cast |
| Unary | ++ -- + - ~ ! | Pre vs post increment matters |
| Ternary | ? : | result = condition ? val1 : val2; |
| instanceof | instanceof | Pattern matching in Java 16+: if (obj instanceof String s) |
Short-Circuit Evaluation (Critical for Interviews)
// && : if left is false, right is NEVER evaluated
String s = null;
if (s != null && s.length() > 0) { } // safe: s.length() never called
// || : if left is true, right is NEVER evaluated
if (list.isEmpty() || list.get(0).isValid()) { } // safe if empty
// & and | : ALWAYS evaluate BOTH sides (no short-circuit)
if (s != null & s.length() > 0) { } // NullPointerException!
Bitwise and Shift Operators (FAANG Favorite)
// XOR swap (no temp variable)
int a = 5, b = 3;
a = a ^ b; // a=6
b = a ^ b; // b=5
a = a ^ b; // a=3
// Check if power of 2
boolean isPow2 = (n > 0) && ((n & (n - 1)) == 0);
// Multiply/divide by 2 (faster than * or /)
int doubled = n << 1; // n * 2
int halved = n >> 1; // n / 2
// Unsigned right shift
int x = -1; // 11111111 11111111 11111111 11111111
int y = x >>> 1; // 01111111 11111111 11111111 11111111 = 2147483647
int z = x >> 1; // 11111111 11111111 11111111 11111111 = -1 (sign extended)
Compound Assignment Implicit Cast
byte b = 10;
b = b + 1; // COMPILE ERROR: b + 1 is int
b += 1; // OK! compound operators include implicit cast to (byte)
b++; // OK! same implicit cast
5. Control Flow
if/else and switch
// Traditional switch (statement, falls through)
switch (day) {
case MONDAY:
case FRIDAY:
System.out.println("Working hard");
break; // forget this = fall-through bug
default:
System.out.println("Other");
}
// Java 14+ switch EXPRESSION (no fall-through, returns value)
String result = switch (day) {
case MONDAY, FRIDAY -> "Working hard";
case SATURDAY, SUNDAY -> "Weekend!";
default -> {
String computed = "Day " + day.ordinal();
yield computed; // yield for multi-line blocks
}
};
Interview Gold
Java 14+ switch expressions are exhaustive — the compiler enforces that all possible values are handled (either with cases or a default). This eliminates an entire class of bugs.
Loops — for, while, do-while
// Enhanced for-each (Java 5+)
for (String item : list) { } // can't modify list during iteration
// Labeled break/continue (useful for nested loops)
outer:
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
if (matrix[i][j] == target) {
System.out.println("Found at [" + i + "][" + j + "]");
break outer; // exits BOTH loops
}
}
}
// do-while: always executes AT LEAST once
do {
response = callService();
} while (response.isRetryable());
Loop Interview Traps
// Infinite loop: int overflow wraps around
for (int i = 0; i >= 0; i++) { } // runs 2.1 billion times then i becomes negative
// ConcurrentModificationException
for (String s : list) {
if (s.equals("remove")) list.remove(s); // THROWS
}
// Fix: use Iterator.remove() or list.removeIf(s -> s.equals("remove"))
6. Arrays
Declaration and Initialization
// Declaration
int[] numbers; // preferred style
int numbers2[]; // C-style (legal but discouraged)
// Static initialization
int[] primes = {2, 3, 5, 7, 11};
// Dynamic initialization
int[] arr = new int[10]; // all zeros
String[] names = new String[5]; // all null
// Multi-dimensional
int[][] matrix = new int[3][4]; // 3 rows, 4 columns
int[][] jagged = new int[3][]; // rows of different lengths
jagged[0] = new int[2];
jagged[1] = new int[5];
Arrays Utility Class
import java.util.Arrays;
Arrays.sort(arr); // O(n log n) — DualPivotQuicksort
Arrays.binarySearch(sortedArr, key); // O(log n) — array MUST be sorted
Arrays.fill(arr, 0); // fill all elements
Arrays.copyOf(arr, newLength); // resize/copy
Arrays.equals(arr1, arr2); // element-by-element comparison
Arrays.deepEquals(matrix1, matrix2); // for multi-dimensional
Arrays.stream(arr).sum(); // stream operations
Array vs ArrayList
| Feature | Array | ArrayList |
|---|---|---|
| Size | Fixed at creation | Dynamic (grows 1.5x) |
| Primitives | Supports directly | Only objects (autoboxing) |
| Performance | Slightly faster (no boxing) | Slight overhead |
| Type safety | Covariant (unsafe) | Generic (safe at compile time) |
| API | .length field | .size() method, rich API |
| Memory | Compact | Object overhead per element |
| Multi-dimension | Native support | List of Lists |
// Array covariance trap (compiles but fails at runtime)
Object[] arr = new String[3];
arr[0] = 42; // Compiles! Throws ArrayStoreException at runtime
// ArrayList generics catch this at compile time
List<String> list = new ArrayList<>();
// list.add(42); // COMPILE ERROR — type safety
Continue to Part 2