Java's famous "Write Once, Run Anywhere" (WORA) principle works because code compiles to bytecode, not machine code. The JVM on each platform interprets or JIT-compiles that bytecode.
| Component | Full Name | Purpose | Who Needs It? |
|---|---|---|---|
| JDK | Java Development Kit | Develop + compile + debug + run | Developers ✅ |
| JRE | Java Runtime Environment | Run compiled Java programs | End users |
| JVM | Java Virtual Machine | Execute bytecode on any OS | Part of JRE |
The JVM initially interprets bytecode line-by-line (slow). The JIT compiler identifies "hot" methods called frequently and compiles them to native machine code for maximum speed. This is why Java gets faster the longer it runs!
Understanding memory is crucial for debugging OutOfMemoryError, StackOverflowError, and for writing efficient code.
StackOverflow: Too many method calls (usually infinite recursion). OutOfMemory: Heap is full — too many objects, memory leaks, or insufficient heap size. Fix with -Xmx flag.
| Type | Size | Range | Default | Example |
|---|---|---|---|---|
| byte | 1 byte | -128 to 127 | 0 | byte b = 100; |
| short | 2 bytes | -32,768 to 32,767 | 0 | short s = 30000; |
| int | 4 bytes | ~±2.1 billion | 0 | int i = 42; |
| long | 8 bytes | ~±9.2 × 10¹⁸ | 0L | long l = 123456789L; |
| float | 4 bytes | ~7 decimal digits | 0.0f | float f = 3.14f; |
| double | 8 bytes | ~15 decimal digits | 0.0 | double d = 3.14159; |
| char | 2 bytes | '\u0000' to '\uffff' | '\u0000' | char c = 'A'; |
| boolean | ~1 bit | true / false | false | boolean b = true; |
Primitives are stored directly on the stack. Reference types (objects) store a reference on the stack pointing to actual data on the heap. Java provides wrapper classes for each primitive.
Java caches Integer values from -128 to 127. Within this range, == works because both variables point to the same cached object. Outside this range, always use .equals()!
| Category | Operators | Example |
|---|---|---|
| Arithmetic | + - * / % | 10 % 3 = 1 |
| Assignment | = += -= *= /= %= | x += 5 (x = x+5) |
| Comparison | == != > < >= <= | 5 > 3 → true |
| Logical | && || ! | true && false → false |
| Bitwise | & | ^ ~ << >> >>> | 5 & 3 → 1 |
| Ternary | ? : | x > 0 ? "pos" : "neg" |
| instanceof | instanceof | obj instanceof String |
| Unary | ++ -- + - ! | i++ vs ++i |
&& stops evaluating if first operand is false. || stops if first is true. This matters when the second operand has side effects (e.g., method calls).
| Modifier | Class | Package | Subclass | World | Use For |
|---|---|---|---|---|---|
public | ✅ | ✅ | ✅ | ✅ | API methods, constants |
protected | ✅ | ✅ | ✅ | ❌ | Methods for subclasses |
| default (none) | ✅ | ✅ | ❌ | ❌ | Package-internal code |
private | ✅ | ❌ | ❌ | ❌ | Fields, helper methods |
Make fields private, provide public getters/setters. This is encapsulation — you control how your data is accessed and modified. Never expose fields directly!
ClassName.method()this keywordMath.sqrt(), countersobjectRef.method()this keywordnew is calledmyList.size()| Feature | Abstract Class | Interface |
|---|---|---|
| Instantiate? | ❌ No | ❌ No |
| Constructor? | ✅ Yes | ❌ No |
| Fields? | ✅ Any type | Only static final |
| Methods? | Abstract + concrete | Abstract + default + static |
| Inheritance? | extends (single) | implements (multiple) |
| Use when? | "IS-A" with shared code | "CAN-DO" capability |
| Operation | ArrayList | LinkedList | Vector |
|---|---|---|---|
| get(index) | O(1) | O(n) | O(1) |
| add(end) | O(1)* | O(1) | O(1)* |
| add(index) | O(n) | O(1)** | O(n) |
| remove | O(n) | O(1)** | O(n) |
| search | O(n) | O(n) | O(n) |
| Thread-safe? | ❌ | ❌ | ✅ (slow) |
| Map Type | Order | Null Keys? | Thread-safe? | Get/Put |
|---|---|---|---|---|
| HashMap | No order | 1 null key | ❌ | O(1) |
| LinkedHashMap | Insertion order | 1 null key | ❌ | O(1) |
| TreeMap | Sorted by key | ❌ No null | ❌ | O(log n) |
| Hashtable | No order | ❌ No null | ✅ (slow) | O(1) | ✅ | O(1) |
| ConcurrentHashMap | No order | ❌ No null | ✅ (fast) | O(1) |
Comparable<T>compareTo(T o)Collections.sort(list)Comparator<T>compare(T a, T b)list.sort(comparator)throwsException (not RuntimeException)RuntimeExceptionthrow is used inside a method to actually throw an exception object. throws is used in the method signature to declare that the method might throw a checked exception.
Automatically closes resources (streams, connections, readers) when the try block exits — no need for manual finally cleanup.
When multiple threads access shared data, race conditions occur. Synchronization ensures only one thread accesses critical sections at a time.
Instead of manually creating threads, use ExecutorService to manage a pool of reusable threads — much more efficient and professional.
If you don't call shutdown(), your program will hang forever because the thread pool keeps running. Use try-finally or try-with-resources patterns.
Lambdas are anonymous functions — concise syntax for implementing functional interfaces (interfaces with exactly one abstract method).
| Functional Interface | Method | Input | Output | Example |
|---|---|---|---|---|
| Predicate<T> | test(T) | T | boolean | n -> n > 0 |
| Function<T,R> | apply(T) | T | R | s -> s.length() |
| Consumer<T> | accept(T) | T | void | s -> print(s) |
| Supplier<T> | get() | none | T | () -> new ArrayList() |
| UnaryOperator<T> | apply(T) | T | T | s -> s.toUpperCase() |
| BinaryOperator<T> | apply(T,T) | T, T | T | (a,b) -> a + b |
Streams provide a declarative pipeline for processing collections. They are lazy (intermediate ops don't run until a terminal op is called) and don't modify the source.
Producer Extends — use ? extends T when you only READ from a structure. Consumer Super — use ? super T when you only WRITE to a structure. This maximizes API flexibility.
You've completed the ultimate Java notes — from JVM architecture to modern Java 17+ features. Keep coding, keep building!
"First, solve the problem. Then, write the code." — John Johnson