Java Reference Notes (Parts 1–7)
Part 1 — Java Fundamentals
Core idea: Java compiles to bytecode → JVM runs it on any OS. Write once, run anywhere.
JDK vs JRE vs JVM
| Term | Contains | Purpose |
|---|---|---|
| JVM | bytecode interpreter | Runs .class files |
| JRE | JVM + standard libraries | Runtime environment |
| JDK | JRE + compiler + tools | Development environment |
Primitive Types
| Type | Size | Default | Example |
|---|---|---|---|
byte |
1B | 0 | byte b = 10; |
short |
2B | 0 | short s = 1000; |
int |
4B | 0 | int x = 42; |
long |
8B | 0L | long n = 99999L; |
float |
4B | 0.0f | float f = 3.14f; |
double |
8B | 0.0 | double d = 3.14159; |
char |
2B Unicode | \u0000 |
char c = 'A'; |
boolean |
logical | false |
boolean ok = true; |
Reference types live on the heap; variables hold references. A reference with no object is null.
Control Flow
// Switch expression (modern)
switch (day) {
case 1 -> System.out.println("Mon");
default -> System.out.println("Other");
}
// Enhanced for
for (String s : names) System.out.println(s);Stack vs Heap
| Area | Stores | Lifetime |
|---|---|---|
| Stack | locals, method calls | until method returns |
| Heap | objects, instance vars | until GC collects |
Dog d = new Dog(); // reference on stack → object on heapStrings
- Immutable — modifications create a new object.
==checks reference equality.equals()checks content.- Use
StringBuilderin loops — string concatenation with+creates a new object each time.
StringBuilder sb = new StringBuilder("Hi");
sb.append(" there");
sb.toString(); // "Hi there"Key Pitfalls
==vsequals()— classic trap.- Instance vars get defaults (0/null/false); local vars do not — compiler error if uninitialized.
- Static methods can't access instance vars directly.
- Default no-arg constructor disappears once you define any constructor.
- Pass-by-value always. References are copied, not objects.
- GC timing is not guaranteed — only eligibility matters.
Part 2 — Object-Oriented Programming
The four pillars: Encapsulation, Abstraction, Inheritance, Polymorphism.
Encapsulation
public class BankAccount {
private double balance;
public void deposit(double amount) {
if (amount > 0) balance += amount;
}
public double getBalance() { return balance; }
}private state + controlled public access. Enables validation, hides internals, future-proofs the API.
Inheritance
class Animal {
void eat() { System.out.println("Eating..."); }
}
class Dog extends Animal {
void bark() { System.out.println("Woof!"); }
}- Single inheritance only (
extendsone class). - Private members are not inherited.
super()calls parent constructor — must be first statement.
Method Overriding vs Overloading
| Feature | Overloading | Overriding |
|---|---|---|
| Where | Same class | Subclass modifies superclass |
| Signature | Must differ | Must be identical |
| Resolved | Compile-time | Runtime |
| Polymorphism | No | Yes |
Rules for overriding: same signature, can't reduce visibility, can't override final or static.
Polymorphism
Animal a = new Dog(); // reference type = Animal, object type = Dog
a.eat(); // Dog's eat() runs — determined at runtimeReference type controls what you can call. Object type controls what runs.
Abstract Classes vs Interfaces
| Feature | Abstract Class | Interface |
|---|---|---|
| Inheritance | One | Many |
| Methods | Abstract + concrete | Abstract + default/static (Java 8+) |
| Variables | Any | public static final only |
| Constructors | Yes | No |
| Use case | Shared partial logic | Shared contract |
abstract class Animal {
abstract void makeSound(); // subclass must implement
void sleep() { /* shared */ } // optional override
}
interface Flyable {
void fly();
default void land() { System.out.println("Landing..."); } // Java 8+
}Object Class
Every class extends java.lang.Object. Always override both equals() and hashCode() together — breaking one breaks hashed collections.
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Dog)) return false;
return this.name.equals(((Dog) o).name);
}
@Override
public int hashCode() { return name.hashCode(); }IS-A vs HAS-A
- IS-A → inheritance (
Dog extends Animal) - HAS-A → composition (prefer this — less coupling)
class Car {
private Engine engine = new Engine(); // HAS-A
}Design Principles
- Prefer composition over inheritance.
- Program to an interface:
List<String> list = new ArrayList<>(); - Encapsulate what varies.
- Don't call overridable methods in constructors — the subclass isn't fully initialized yet.
Key Pitfalls
- Constructors are not inherited.
- Static methods are hidden, not overridden.
- Overusing
instanceofsignals poor polymorphic design. - Avoid deep inheritance chains — they become unmaintainable.
Part 3 — Core Java Concepts
static vs Instance
| Static | Instance | |
|---|---|---|
| Memory | One per class | One per object |
| Access | ClassName.method() |
object.method() |
| Can access instance members? | No | Yes |
final
| Usage | Effect |
|---|---|
final variable |
No reassignment after init |
final method |
Cannot be overridden |
final class |
Cannot be extended |
Blank finals must be assigned in constructor:
class Config {
final int port;
Config(int port) { this.port = port; }
}Initialization Order
class Sample {
static { System.out.println("1. Static block"); } // once, on class load
{ System.out.println("2. Instance block"); } // each instantiation
Sample() { System.out.println("3. Constructor"); } // each instantiation
}Wrapper Classes + Autoboxing
| Primitive | Wrapper |
|---|---|
int |
Integer |
double |
Double |
char |
Character |
boolean |
Boolean |
Integer a = 5; // autoboxing
int b = a + 1; // unboxing
int i = Integer.parseInt("42");
String s = Integer.toString(100);⚠️ Unboxing
nullthrowsNullPointerException.
Nested / Inner Classes
| Type | Access to outer | Instantiation |
|---|---|---|
| Member inner | Yes | new Outer().new Inner() |
| Static nested | No (static only) | new Outer.Nested() |
| Local inner | Yes (effectively final vars) | Inside method only |
| Anonymous | Yes | Inline at usage point |
// Anonymous (old style)
Runnable r1 = new Runnable() { public void run() { ... } };
// Lambda (prefer this)
Runnable r2 = () -> System.out.println("Running");Enums
enum Level {
LOW(1), MEDIUM(2), HIGH(3);
private final int code;
Level(int code) { this.code = code; }
public int getCode() { return code; }
}Type-safe constants. Can have fields, methods, constructors.
Immutable Classes
Recipe: final class + private final fields + no setters + defensive copies of mutable fields.
final class Employee {
private final String name;
private final int id;
Employee(String name, int id) { this.name = name; this.id = id; }
public String getName() { return name; }
}var (Java 10+)
Local variables only. Type inferred at compile time — not dynamic.
var list = new ArrayList<String>(); // inferred as ArrayList<String>Access Modifiers
| Modifier | Same Class | Same Package | Subclass | Everywhere |
|---|---|---|---|---|
public |
✓ | ✓ | ✓ | ✓ |
protected |
✓ | ✓ | ✓ | ✗ |
| default | ✓ | ✓ | ✗ | ✗ |
private |
✓ | ✗ | ✗ | ✗ |
Part 4 — Advanced Java
Exception Handling
try {
int x = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("Caught: " + e.getMessage());
} finally {
System.out.println("Always runs — cleanup here");
}- Catch specific exceptions first, broader ones last.
finallyalways runs — even if youreturninsidetry.- Try-with-resources — auto-closes anything implementing
AutoCloseable:
try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
System.out.println(br.readLine());
} catch (IOException e) {
e.printStackTrace();
}- Checked exceptions (must declare or catch):
IOException,SQLException. - Unchecked exceptions (runtime, no forced handling):
NullPointerException,IllegalArgumentException.
Custom exception:
class InvalidUserException extends Exception {
InvalidUserException(String msg) { super(msg); }
}Collections Framework
Collection
├── List → ArrayList, LinkedList
├── Set → HashSet, LinkedHashSet, TreeSet
└── Queue → PriorityQueue, ArrayDeque
Map → HashMap, LinkedHashMap, TreeMap
Choose by behavior:
| Collection | Order | Duplicates | Null | Performance |
|---|---|---|---|---|
ArrayList |
insertion | yes | yes | O(1) get |
LinkedList |
insertion | yes | yes | O(1) add/remove at ends |
HashSet |
none | no | one null | O(1) add/contains |
TreeSet |
sorted | no | no | O(log n) |
HashMap |
none | keys unique | yes | O(1) |
LinkedHashMap |
insertion | keys unique | yes | O(1) |
TreeMap |
key-sorted | keys unique | no | O(log n) |
// Safe removal during iteration — never use for-each to remove
Iterator<String> it = names.iterator();
while (it.hasNext()) {
if (it.next().equals("Bob")) it.remove(); // safe
}Generics
Type-safe containers. Errors caught at compile time, not runtime.
class Box<T> {
private T value;
void set(T v) { value = v; }
T get() { return value; }
}
// Bounded type
class Calculator<T extends Number> {
double add(T a, T b) { return a.doubleValue() + b.doubleValue(); }
}Wildcards:
| Syntax | Meaning | Use when |
|---|---|---|
? |
any type | read-only, unknown type |
? extends T |
T or subtype | reading from structure |
? super T |
T or supertype | writing to structure |
⚠️ Type erasure: generic type info is removed at runtime.
List<String>andList<Integer>are bothListat runtime.
Comparable vs Comparator
// Comparable — natural ordering, built into the class
class Student implements Comparable<Student> {
int id;
public int compareTo(Student s) { return this.id - s.id; }
}
// Comparator — external, flexible, multiple orderings
Comparator<Student> byName = (a, b) -> a.name.compareTo(b.name);
list.sort(byName);Key Pitfalls
ConcurrentModificationException— modifying a collection while iterating with for-each.equals()/hashCode()mismatch — breaksHashMap,HashSet.- Mutable objects as
HashMapkeys — hash changes, key is lost. - Raw types (
Listinstead ofList<String>) — lose all type safety.
Part 5 — Concurrency & Threads
Core problem: Multiple threads sharing mutable state → race conditions, data corruption, deadlocks.
Creating Threads
// Option 1 — extend Thread (limits flexibility)
class MyThread extends Thread {
public void run() { System.out.println("Running"); }
}
new MyThread().start();
// Option 2 — implement Runnable (preferred)
Thread t = new Thread(() -> System.out.println("Running"));
t.start();⚠️
start()schedules the thread.run()just calls the method on the current thread. Never callrun()directly.
Thread Lifecycle
| State | Description |
|---|---|
| New | Created, not started |
| Runnable | Ready or actively running |
| Blocked/Waiting | Waiting for lock or signal |
| Timed Waiting | sleep() or wait(timeout) |
| Terminated | Finished execution |
Synchronization
class Counter {
private int count = 0;
public synchronized void increment() { count++; } // method-level lock
public void decrement() {
synchronized(this) { count--; } // block-level lock
}
}synchronizedon an instance method locksthis.synchronizedon a static method locks theClassobject.wait()releases the lock.sleep()does not.
volatile
Guarantees visibility across threads. Does not guarantee atomicity.
volatile boolean running = true; // reads/writes always go to main memoryUse for simple flags. For increment/decrement, use AtomicInteger.
Atomic Operations
AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet(); // thread-safe, no synchronization needed
count.compareAndSet(5, 10); // CAS operationExecutors (Prefer Over Raw Threads)
ExecutorService pool = Executors.newFixedThreadPool(4);
pool.submit(() -> System.out.println("Task"));
pool.shutdown(); // don't forget thisCallable + Future (Get Results from Threads)
Callable<Integer> task = () -> 5 + 10;
ExecutorService ex = Executors.newSingleThreadExecutor();
Future<Integer> result = ex.submit(task);
System.out.println(result.get()); // blocks until done
ex.shutdown();ReentrantLock (More Control Than synchronized)
Lock lock = new ReentrantLock();
lock.lock();
try {
// critical section
} finally {
lock.unlock(); // always in finally
}Inter-Thread Communication
// Must be inside synchronized block
wait(); // releases lock, waits for notify()
notify(); // wakes one waiting thread
notifyAll(); // wakes allDeadlocks
Caused by two threads each holding a lock the other needs. Avoid by:
- Always acquiring locks in the same order.
- Keeping lock scope minimal.
- Using
tryLock()with timeout.
Concurrent Collections
Prefer over manually synchronized wrappers:
| Class | Use Case |
|---|---|
ConcurrentHashMap |
Thread-safe map, segment-level locking |
CopyOnWriteArrayList |
Read-heavy, rare writes |
ArrayBlockingQueue |
Producer-consumer pattern |
ThreadLocal
Per-thread variable — each thread gets its own copy. Common in web frameworks for user context.
ThreadLocal<Integer> threadId = ThreadLocal.withInitial(() -> 0);Key Pitfalls
- Calling
run()instead ofstart()— no new thread created. sleep()doesn't release locks — can cause contention.- Missing
volatileon shared flags — thread may never see update. - Forgetting
finallyaroundlock.unlock()— deadlock on exception.
Part 6 — I/O, Files, and Serialization
Always use try-with-resources. Always use buffered streams for performance.
I/O Stream Hierarchy
| Type | Root Classes | For |
|---|---|---|
| Byte streams | InputStream / OutputStream |
Binary data (images, files) |
| Char streams | Reader / Writer |
Text data |
Reading and Writing Files
// Text — buffered for performance
try (BufferedReader br = new BufferedReader(new FileReader("input.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {
String line;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
}
}// Binary
try (FileInputStream in = new FileInputStream("file.bin");
FileOutputStream out = new FileOutputStream("copy.bin")) {
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buf)) != -1) {
out.write(buf, 0, bytesRead);
}
}NIO (java.nio.file) — Prefer for Modern Code
Path path = Paths.get("data.txt");
// Check existence
Files.exists(path);
// Read all lines
List<String> lines = Files.readAllLines(path);
// Write
Files.write(path, lines);
// Copy
Files.copy(path, Paths.get("backup.txt"), StandardCopyOption.REPLACE_EXISTING);
// Walk directory tree
try (Stream<Path> paths = Files.walk(Paths.get("/projects"))) {
paths.filter(Files::isRegularFile).forEach(System.out::println);
}Serialization
Persist an object's state to disk or network.
class Employee implements Serializable {
private static final long serialVersionUID = 1L; // versioning
private String name;
private int id;
transient String password; // excluded from serialization
}
// Write
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("emp.ser"))) {
out.writeObject(new Employee("Alice", 101));
}
// Read
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("emp.ser"))) {
Employee e = (Employee) in.readObject();
}transient— skips that field during serialization.serialVersionUID— if it mismatches on deserialization,InvalidClassExceptionis thrown. Always declare it explicitly.
Externalizable vs Serializable
Serializable |
Externalizable |
|
|---|---|---|
| Control | Automatic | Full manual control |
| Performance | Slower | Faster (you choose what to write) |
| Methods needed | None | writeExternal() + readExternal() |
Key Pitfalls
- Forgetting to close streams → resource leak. Use try-with-resources.
- Unbuffered streams are slow — always wrap with
Buffered*. - Serializing sensitive data without
transient— passwords end up on disk. - Missing
serialVersionUID— class changes break deserialization silently.
Part 7 — Networking & Distributed Systems
TCP = reliable, ordered, connection-based. UDP = fast, connectionless, no delivery guarantee.
TCP Client/Server
// Server
ServerSocket server = new ServerSocket(5000);
Socket socket = server.accept(); // blocks until client connects
DataInputStream in = new DataInputStream(socket.getInputStream());
System.out.println(in.readUTF());
server.close();
// Client
Socket socket = new Socket("localhost", 5000);
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
out.writeUTF("Hello Server");
socket.close();For production: wrap in BufferedReader/BufferedWriter and handle each client in a thread pool.
UDP (Datagrams)
// Sender
DatagramSocket socket = new DatagramSocket();
byte[] data = "Hello".getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getLocalHost(), 5000);
socket.send(packet);
socket.close();
// Receiver
DatagramSocket socket = new DatagramSocket(5000);
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
System.out.println(new String(packet.getData()).trim());URL — Quick HTTP Read
URL url = new URL("https://example.com");
try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) {
br.lines().forEach(System.out::println);
}RMI vs Sockets vs Modern Alternatives
| Option | Level | Use Case |
|---|---|---|
| Sockets | Low (bytes) | Custom protocols, max control |
| RMI | High (objects) | Legacy distributed Java objects |
| REST + JSON | High | Standard web APIs — use this |
| gRPC | High + typed | High-performance microservices |
| WebSockets | Persistent | Real-time bidirectional |
| Kafka/RabbitMQ | Async | Event-driven, decoupled systems |
RMI is legacy. For anything new, use REST or gRPC. For event-driven systems, use a message broker.
InetAddress
InetAddress addr = InetAddress.getByName("www.example.com");
addr.getHostName(); // domain name
addr.getHostAddress(); // IP stringKey Pitfalls
- Not running server before client — connection refused.
- Not closing sockets → resource leak.
- UDP has no delivery guarantee — implement your own ACK for reliability.
- RMI requires registry running, policy files, and is a pain in firewalled environments. Use REST.
Quick Reference — When to Use What
Collections
| Need | Use |
|---|---|
| Fast random access | ArrayList |
| Fast insert/delete at ends | LinkedList or ArrayDeque |
| Unique elements, fast lookup | HashSet |
| Unique + sorted | TreeSet |
| Key-value, fast lookup | HashMap |
| Key-value + insertion order | LinkedHashMap |
| Key-value + sorted keys | TreeMap |
| Thread-safe map | ConcurrentHashMap |
Concurrency
| Need | Use |
|---|---|
| Thread pool | ExecutorService |
| Thread-safe counter | AtomicInteger |
| Return value from thread | Callable + Future |
| Shared flag across threads | volatile boolean |
| Fine-grained locking | ReentrantLock |
| Per-thread state | ThreadLocal |
I/O
| Need | Use |
|---|---|
| Read text file | BufferedReader + FileReader |
| Write text file | BufferedWriter + FileWriter |
| Read binary | FileInputStream |
| Modern file ops | Files + Paths (NIO) |
| Persist objects | ObjectOutputStream / ObjectInputStream |