0% found this document useful (0 votes)
2 views44 pages

Java Complete Handwritten Notes

This document is a comprehensive guide to Java, covering topics from basic to advanced concepts including variables, OOP principles, core APIs, collections, generics, functional programming, concurrency, and design patterns. It provides structured notes with examples, best practices, and interview preparation tips. The content is organized into distinct parts, each focusing on different aspects of Java programming.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views44 pages

Java Complete Handwritten Notes

This document is a comprehensive guide to Java, covering topics from basic to advanced concepts including variables, OOP principles, core APIs, collections, generics, functional programming, concurrency, and design patterns. It provides structured notes with examples, best practices, and interview preparation tips. The content is organized into distinct parts, each focusing on different aspects of Java programming.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

JAVA
— complete notes —
Basics to Advanced

variables · OOP · collections · generics


lambdas & streams · concurrency · JDBC
design patterns · interview cheat-sheet
Table of Contents
PART 1 · FOUNDATIONS
☕ What is Java? 01
Variables & Data Types 02
➕ Operators 03
Control Flow: if / switch 04
Loops 05
Arrays 06

PART 2 · OOP
Classes & Objects 07
Constructors 08
The 4 Pillars — Overview 09
Encapsulation 10
Inheritance 11
Polymorphism 12
⚖ Abstract Classes vs Interfaces 13
static & final keywords 14
Access Modifiers & Packages 15

PART 3 · CORE APIs


Strings & StringBuilder 16
Wrapper Classes & Autoboxing 17
Exception Handling 18
Enums & Nested Classes 19

PART 4 · COLLECTIONS
Collections Framework — the Map 20
List: ArrayList vs LinkedList 21
Set: Hash / Linked / Tree 22
Map: HashMap / TreeMap / LinkedHashMap 23
Queue, Deque & Stack 24
Iterator, Comparable & Comparator 25

PART 5 · GENERICS
Generics 26

PART 6 · FUNCTIONAL JAVA (8+)


λ Lambda Expressions 27
Functional Interfaces 28
Streams API 29
❓ Optional 30

PART 7 · CONCURRENCY
Threads Basics 31
Synchronization & Locks 32
⚙ Executor Framework 33

PART 8 · ADVANCED
File I/O & NIO 34
Serialization 35
Serialization 35
Reflection 36
Annotations 37
Memory Management & GC 38
JDBC Basics 39
Design Patterns 40
✅ Best Practices & Interview Cheat-Sheet 41
PART 1 · FOUNDATIONS 01 / 41

☕What is Java?
Big picture
Java = platform-independent , object-oriented, compiled
+ interpreted language (Sun, 1995; now Oracle/OpenJDK).
Motto: "Write Once, Run Anywhere" (WORA) — code class name
compiles to bytecode , not machine code. MUST match file
Bytecode runs inside the JVM, so the *same .class file* name ([Link])!
runs on Windows/Linux/Mac.

JDK vs JRE vs JVM


JVM (Java Virtual Machine) → engine that actually *runs*
bytecode. Not portable itself (each OS has its own JVM).
JRE (Runtime Env) = JVM + core libraries → needed to
*run* java programs.
JDK (Development Kit) = JRE + compiler(javac) +
debugger + tools → needed to *write & compile*.
so: JDK ⊃ JRE ⊃ JVM (each one contains the next)

Compilation flow
[Link] --(javac)--> [Link] (bytecode) --(java,
JVM)--> output
JVM has 3 jobs: Class-loader → Bytecode verifier →
Interpreter/JIT compiler
JIT (Just-In-Time) compiler converts hot bytecode →
native machine code at runtime for speed!

// example
public class Hello {
public static void main(String[] args) {
[Link]("Hello, World!");
}
}

✍ every app needs this exact main() signature — JVM's entry point

Java Notes — handwritten style · pg. 1


PART 1 · FOUNDATIONS 02 / 41

Variables & Data Types


8 primitive types (not objects!)
byte (1B, -128→127) short (2B) int (4B) ✱most used
long (8B, needs L suffix)
float (4B, needs f suffix) double (8B) ✱default for primitives store
decimals VALUE directly;
char (2B, single UTF-16 char, single quotes 'A') boolean references store an
(true/false) ADDRESS to the
everything else (String, arrays, custom classes) is a object
REFERENCE type

Declaring & rules


type name = value; → e.g. int age = 21;
var (Java 10+) = local type inference → var x = 10;
(compiler figures out int) — only for LOCAL vars
final keyword → makes variable a *constant*, can't be
reassigned
Naming: camelCase for vars/methods, PascalCase for
classes, UPPER_SNAKE for constants

Type casting
Widening (implicit/safe): int → long → float → double
Narrowing (explicit, may lose data): double d=9.7; int i=
(int)d; // i=9

// example
int score = 95;
double gpa = 8.9;
char grade = class="c-string">'A';
final double PI = 3.14159; class="c-comment">// constant
var city = "Pune"; class="c-comment">// inferred
as String

long population = 8_000_000_000L; class="c-comment">//


underscores OK!
int truncated = (int) gpa; class="c-comment">// narrowing
-> 8

Java Notes — handwritten style · pg. 2


PART 1 · FOUNDATIONS 03 / 41

➕Operators
Categories
Arithmetic: + - * / % (modulo = remainder)
Relational: == != > < >= <= → returns boolean
Logical: && (AND) || (OR) ! (NOT) — these are short- == vs .equals()
circuit (2nd operand skipped if not needed) is the #1 Java
Bitwise: & | ^ ~ << >> >>> (>>> = unsigned right interview trap!
shift, fills with 0)
Assignment: = += -= *= /= %=
Ternary: condition ? valueIfTrue : valueIfFalse

Gotchas
== on objects/Strings compares *references* not
content → use .equals() for value comparison!
int / int = int (truncates!) → 7/2 = 3, not 3.5. Cast one
operand: (double)7/2 = 3.5
% works on doubles too: 7.5 % 2 = 1.5

// example
int a = 10, b = 3;
[Link](a / b); class="c-comment">// 3
(int division)
[Link](a % b); class="c-comment">// 1
[Link]((double)a/b);class="c
-comment">//
3.333...

String s1 = new String("hi");


String s2 = new String("hi");
[Link](s1 == s2); class="c-comment">//
false! (diff objects)
[Link]([Link](s2)); class="c-comment">//
true (same content)

Java Notes — handwritten style · pg. 3


PART 1 · FOUNDATIONS 04 / 41

Control Flow: if / switch


if - else if - else
Standard branching. Braces optional for single statement
(but always use them — best practice).
Nested ifs get messy fast → prefer switch or polymorphism forgetting
for many branches. break; is a classic
bug — cascades
switch statement into next case!
Works on: int, char, String (Java 7+), enum. NOT
double/boolean .
Each case needs break, else it "falls through" to next case
(classic bug!).
Java 14+ modern switch expression: arrow syntax `case
X -> result;` no fall-through, returns a value.

// example
class="c-comment">// classic switch (watch the fall-
through!)
int day = 3;
switch (day) {
case 1: [Link]("Mon"); break;
case 2: [Link]("Tue"); break;
case 3: [Link]("Wed"); break;
default: [Link]("?");
}

class="c-comment">// modern switch EXPRESSION (Java 14+)


String name = switch (day) {
case 1, 7 -> "Weekend-ish";
case 2, 3, 4, 5, 6 -> "Weekday";
default -> "Invalid";
};

Java Notes — handwritten style · pg. 4


PART 1 · FOUNDATIONS 05 / 41

Loops
The 4 loop forms
for (init; condition; update) → use when iteration count is
known
while (condition) → checks *before* each run, may run 0 for-each can't
times modify the index or
do-while (condition) → checks *after*, guaranteed to run go backwards —
at least once use classic for then
enhanced for / for-each: for(Type item : collection) →
clean iteration, no index, read-only

Loop control
break → exits the loop entirely
continue → skips to next iteration
labeled break/continue → break out of *outer* loop from
inside a nested loop: `outer: for(...) { break outer; }`

// example
for (int i = 0; i < 5; i++) {
if (i == 3) continue; class="c-comment">// skip 3
[Link](i);
}

int[] nums = {10, 20, 30};


for (int n : nums) { class="c-comment">// for-each
[Link](n);
}

outer:
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++) {
if (j == 1) break outer; class="c-comment">//
kills BOTH loops
}

Java Notes — handwritten style · pg. 5


PART 1 · FOUNDATIONS 06 / 41

Arrays
Basics
Fixed-size, same-type, zero-indexed container. Declared
as: int[] arr; or int arr[];
[Link] → property (no parens!) gives size. Default array size can
values: 0 for numbers, false, null for objects. NEVER change
Arrays are objects on the heap even for primitives — arr once created —
itself is a reference. that's what List is
for!
Multi-dimensional
int[][] grid = new int[3][4]; → 3 rows, 4 cols ("array of
arrays")
Can be *jagged* (ragged): rows of different lengths — new
int[3][]; then assign each row separately.

Arrays utility class


[Link](arr), [Link](arr), [Link](a,b),
[Link](arr, val), [Link](arr, newLen)
[Link](arr, key) → array MUST be sorted
first!

// example
int[] scores = {90, 85, 77, 92};
class="c-
[Link]([Link]);
comment">// 4

int[][] matrix = {{1,2},{3,4},{5,6}};


for (int[] row : matrix)
row));
[Link]([Link](

6); class="c-
int[] copy = [Link](scores,
comment">// pads with 0s
class="c-
[Link](scores);
comment">// in-place ascending

Java Notes — handwritten style · pg. 6


PART 2 · OOP 07 / 41

Classes & Objects


Core idea
Class = blueprint (fields + methods). Object = actual
instance created with `new`.
Fields (a.k.a instance variables) hold *state*; methods static belongs to
define *behavior*. the CLASS, not the
Every object gets its own copy of instance fields; static object
fields are shared across ALL objects.

The `this` keyword


Refers to the current instance . Used to disambiguate field
vs parameter with same name.
Also used to call another constructor in the same class:
this(args) — must be FIRST line.

// example
class Car {
String model; class="c-comment">// instance field
static int count; class="c-comment">// shared across
ALL Car objects

Car(String model) {
[Link] = model; class="c-comment">//
[Link] = param
count++;
}
void drive() { [Link](model + " driving
");
}
}

Car c1 = new Car("Tesla");


Car c2 = new Car("BMW");
[Link]([Link]); class="c-comment">// 2 ->
shared!

Java Notes — handwritten style · pg. 7


PART 2 · OOP 08 / 41

Constructors
Rules
Same name as class, NO return type (not even void).
If you write zero constructors, Java auto-generates a no-
arg default constructor. super(...) if
The moment you write ANY constructor, the free default used, MUST be the
one disappears! very first statement
Constructor overloading: multiple constructors,
different parameter lists.
Constructor chaining: this(...) calls another constructor
in same class; super(...) calls parent's constructor.

// example
class Point {
int x, y;
Point() { this(0, 0); } class="c-
comment">// chains to below
Point(int x, int y) {
this.x = x; this.y = y;
}
}

class Point3D extends Point {


int z;
Point3D(int x, int y, int z) {
super(x, y); class="c-comment">// MUST be first
line -> calls parent ctor
this.z = z;
}
}

Java Notes — handwritten style · pg. 8


PART 2 · OOP 09 / 41

The 4 Pillars — Overview


Encapsulation
Bundle data + methods together; hide internal state via
private fields + public getters/setters.
"data hiding" → protects invariants, controls how state these 4 words =
changes. guaranteed
interview question
Abstraction
#1
Show *what* an object does, hide *how*. Achieved via
abstract classes & interfaces.

Inheritance
"is-a" relationship. Child class reuses/extends parent's
fields+methods via extends.

Polymorphism
"many forms" — same method name behaves differently.
Overloading (compile-time) vs Overriding (runtime).

Java Notes — handwritten style · pg. 9


PART 2 · OOP 10 / 41

Encapsulation
How to do it
Mark fields private. Expose controlled access via public
getters/setters.
Lets you validate input, make fields read-only (getter without
only), or change internal representation later without encapsulation,
breaking callers. anyone could set
balance = -9999
// example directly!
class BankAccount {
private double balance; class="c-comment">// hidden!

public double getBalance() { return balance; }

public void deposit(double amt) {


if (amt > 0) balance += amt; class="c-comment">//
validation!
else throw new IllegalArgumentException("bad
amount");
}
}

Java Notes — handwritten style · pg. 10


PART 2 · OOP 11 / 41

Inheritance
Mechanics
class Child extends Parent { } → Child inherits
public/protected members.
Java supports single inheritance only for classes (no reference TYPE
multiple class inheritance — avoids Diamond Problem). decides what you
But a class CAN implement multiple interfaces. can CALL; object
protected = visible to subclasses + same package. TYPE decides
private members are NOT inherited/visible. what runs

@Override + method hiding


@Override annotation → compiler checks you're actually
overriding (catches typos!).
Static methods are hidden, not overridden — resolved at
compile-time based on reference type.

// example
class Animal {
protected String name;
void eat() { [Link](name
+ " eats"); }
}

class Dog extends Animal {


@Override
void eat() { [Link](name
+ " eats kibble");
}
void bark() { [Link]("Woof!
"); }
}

Animal a = new Dog(); class="c-comment">// upcasting


[Link](); class="c-comment">// "eats kibble" ->
runtime
polymorphism!
class="c-comment">// [Link](); <- compi
le ERROR, Animal
ref can't see Dog methods

Java Notes — handwritten style · pg. 11


PART 2 · OOP 12 / 41

Polymorphism
Compile-time (Overloading)
Same method name, different parameter list
(type/number/order). Resolved at COMPILE time.
Return type ALONE cannot differentiate overloads. overloading =
same class,
Runtime (Overriding) different args |
Subclass provides specific implementation of a parent overriding =
method — same signature. parent-child, same
Resolved at RUNTIME based on the actual object (this is signature
"dynamic method dispatch").
Rules: same name+params, return type same/covariant,
access modifier same-or-wider, can't override
static/final/private.

// example
class MathUtil {
int add(int a, int b) { return a + b; }
class="c-comment">// overload 1
double add(double a, double b) { return a + b; }
class="c-comment">// overload 2
int add(int a, int b, int c) { return a+b+c; }
class="c-comment">// overload 3
}

class Shape { double area() { return 0; } }


class Circle extends Shape {
double r;
@Override double area() { return [Link] * r * r; }
class="c-comment">// OVERRIDE
}

Java Notes — handwritten style · pg. 12


PART 2 · OOP 13 / 41

⚖Abstract Classes vs Interfaces


abstract class
Can have both abstract (no body) AND concrete methods.
Can have constructors, instance fields, any access
modifier.
"implements" for
Use when classes share common state/code and an "is-a" interfaces, "extends"
hierarchy. Single inheritance only. for classes (and
class MyClass extends AbstractClass { must implement all abstract classes)
abstract methods }

interface
Pure contract — historically only abstract methods; Java
8+ allows default and static methods too.
Fields are implicitly public static final (constants only).
A class can implement multiple interfaces → workaround
for no multiple inheritance!
Use for "can-do" capability (Runnable, Comparable,
Serializable).

// example
interface Flyable {
void fly(); class="c-comment">//
abstract
default void land() { class="c-comment">//
Java 8+ default method
[Link]("landing...");
}
}
interface Swimmable { void swim(); }

abstract class Bird {


abstract void makeSound(); class="c-comment">//
subclass MUST implement
void breathe() { [Link]("breathing"); }
class="c-comment">// concrete
}

class Duck extends Bird implements Flyable, Swimmable {


void makeSound() { [Link]("Quack"); }
public void fly() { [Link]("flying"); }
public void swim() { [Link]("swimming"); }
}

Java Notes — handwritten style · pg. 13


PART 2 · OOP 14 / 41

static & final keywords


static
Belongs to the CLASS not instance — one copy shared by
all objects.
static methods can't use `this` or access instance (non- final list =
static) members directly. List<Integer> l;
static block { } → runs once when class is first loaded, [Link](1) is OK! only
used for static field setup. reassigning `l` is
blocked
final
final variable → constant, can't reassign (but object it refs
can still mutate internally!).
final method → cannot be overridden by subclass.
final class → cannot be extended at all (e.g. String,
Integer are final).

// example
class Config {
static int counter;
static final String VERSION; class="c-comment">//
constant, set once

static { class="c-comment">//
static initializer block
VERSION = "1.0.0";
d");
[Link]("Config class loade
}
}

final class Immutable {} class="c-comment">// cannot


be extended
ds Immutable {} <-
class="c-comment">// class Bad exten
compile ERROR

Java Notes — handwritten style · pg. 14


PART 2 · OOP 15 / 41

Access Modifiers & Packages


The 4 levels (narrowest→widest)
private → same class only
*(default/package-private, no keyword)* → same package
only rule of thumb:
protected → same package + subclasses (even in other make everything
packages) private, widen
public → everywhere access only when
truly needed
Packages
Namespace to organize/avoid class-name collisions:
package [Link];
import brings other package's public classes into scope.
Folder structure MUST mirror the package name
(com/company/app/[Link]).

Java Notes — handwritten style · pg. 15


PART 3 · CORE APIS 16 / 41

Strings & StringBuilder


String basics
Strings are IMMUTABLE — every "modification" creates a
NEW String object.
String literals live in the String Pool (part of heap) — String += in a
literals with same value are REUSED. loop of N ->
new String("x") forces a new object OUTSIDE the pool (== creates N objects!
gives false vs a literal). use StringBuilder
.intern() → pulls a String into the pool manually. instead

Common methods
length(), charAt(i), substring(start,end), indexOf(),
toUpperCase(), trim(), split(regex), replace()
StringBuilder → mutable! Use in loops for heavy
concatenation — way faster than String += in a loop.
StringBuilder methods: append(), insert(), reverse(),
delete(), toString()

// example
String a = "cat";
String b = "cat";
[Link](a == b); class="c-
comment">// true (pool, same ref)

String c = new String("cat");


[Link](a == c); class="c-
comment">// false (new object)

StringBuilder sb = new StringBuilder();


d(",");
for (int i = 0; i < 5; i++) [Link](i).appen
class="c-
[Link]([Link]());
comment">// 0,1,2,3,4,

Java Notes — handwritten style · pg. 16


PART 3 · CORE APIS 17 / 41

Wrapper Classes & Autoboxing


Why wrappers
Every primitive has an Object wrapper: int→Integer,
double→Double, char→Character,
boolean→Boolean...
unboxing a null
Needed because Collections (List, Map) can only hold Integer throws
OBJECTS, not primitives. NullPointerException!
Autoboxing = primitive → wrapper automatically. classic bug
Unboxing = wrapper → primitive automatically.

The Integer cache trap


Integer caches values -128 to 127 . Within that range
== works by coincidence; outside it, doesn't!
Always use .equals() to compare wrapper VALUES,
never ==.

// example
Integer i1 = 100, i2 = 100;
[Link](i1 == i2); class="c-
comment">// true (cached)

Integer i3 = 200, i4 = 200;


[Link](i3 == i4); class="c-
comment">// false! (not cached, diff objects)
[Link]([Link](i4)); class="c-
comment">// true (correct way)

List<Integer> list = new ArrayList<>();


[Link](5); class="c-comment">// autoboxed
int -> Integer
int x = [Link](0); class="c-comment">// unboxed
Integer -> int

Java Notes — handwritten style · pg. 17


PART 3 · CORE APIS 18 / 41

Exception Handling
Hierarchy
Throwable → Error (JVM-level, e.g. OutOfMemoryError,
don't catch) + Exception
Exception splits into: Checked (must handle/declare, e.g. never leave an
IOException — compiler enforced) and empty catch block!
Unchecked/RuntimeException (NullPointerException, ("swallowing"
ArrayIndexOutOfBounds — not enforced). exceptions = silent
bugs)
try / catch / finally
finally block ALWAYS runs (even after return!) — except
[Link]() or JVM crash.
Can catch multiple types: catch (IOException |
SQLException e)
try-with-resources → auto-closes anything implementing
AutoCloseable (streams, files, connections).
throw = actually raise an exception. throws = declare in
method signature that it might happen.
Custom exceptions: extend Exception (checked) or
RuntimeException (unchecked).

// example
try {
int[] arr = new int[3];
[Link](arr[5]); class="c-
comment">// throws ArrayIndexOOB
} catch (ArrayIndexOutOfBoundsException e) {
[Link]("Caught: " + [Link]());
} finally {
[Link]("always runs");
}

class="c-comment">// try-with-resources (auto-closes


reader!)
try (BufferedReader br = new BufferedReader(new
FileReader("[Link]"))) {
[Link]([Link]());
} catch (IOException e) { [Link](); }

class InsufficientFundsException extends Exception {


InsufficientFundsException(String msg) { super(msg); }
}

Java Notes — handwritten style · pg. 18


PART 3 · CORE APIS 19 / 41

Enums & Nested Classes


Enums
Type-safe set of constants. Are actually full classes — can
have fields, constructors, methods!
Built-in: name(), ordinal(), values(), valueOf(String). Work enum
great in switch statements. constructors are
always private
Nested class types
(implicitly) — can't
Static nested class → doesn't need outer instance:
call new Day()
[Link] obj = new [Link]();
yourself
Inner class (non-static) → tied to an outer instance, can
access outer's private fields.
Local class → defined inside a method body.
Anonymous class → no name, defined+instantiated in
one expression (common before lambdas).

// example
enum Day {
MON("Monday"), TUE("Tuesday"), WED("Wedne
sday");
private final String full;
Day(String full) { [Link] = full;
} class="c-
comment">// enum constructor!
String getFull() { return full; }
}

Day d = [Link];
[Link]([Link]() + " "
+ [Link]());
class="c-comment">// Monday 0

class="c-comment">// anonymous class


implementing an
interface inline
Runnable r = new Runnable() {
public void run() { [Link]("
running!"); }
};

Java Notes — handwritten style · pg. 19


PART 4 · COLLECTIONS 20 / 41

Collections Framework — the Map


Root interfaces
Collection (root) → List, Set, Queue all extend it. Map is
separate (key-value, not part of Collection).
List = ordered, allows duplicates, index access. always code to
Set = no duplicates, mostly unordered (except the INTERFACE:
LinkedHashSet/TreeSet). List<String> l =
Queue/Deque = FIFO/LIFO processing order. new ArrayList<>
();
Map = key→value pairs, keys unique.

Choosing wisely
Need fast index lookup + duplicates? → ArrayList
Need fast insert/delete at ends? → LinkedList / ArrayDeque
Need uniqueness + fast lookup? → HashSet
Need sorted order automatically? → TreeSet / TreeMap
Need insertion order preserved? → LinkedHashSet /
LinkedHashMap

Java Notes — handwritten style · pg. 20


PART 4 · COLLECTIONS 21 / 41

List: ArrayList vs LinkedList


ArrayList
Backed by a resizable array. get(i) = O(1) . add/remove in
middle = O(n) (shifts elements).
Grows by ~1.5x when full (creates new array, copies over) [Link](1)
— amortized O(1) add at end. removes INDEX 1,
not the value 1 —
LinkedList
classic overload
Doubly-linked list nodes. get(i) = O(n) (must walk from
gotcha!
head/tail). add/remove at ends = O(1).
Implements both List AND Deque → can be used as a
stack/queue too.
In practice: ArrayList wins ~90% of the time (better cache
locality); LinkedList rarely used now.

// example
List<String> list = new ArrayList<>();
[Link]("a"); [Link]("b"); [Link](1, "x"); class="c-
comment">// insert at index
[Link]("a"); class="c-comment">// remove by
VALUE
[Link](0); class="c-comment">// remove by
INDEX (int overload!)
[Link](list); class="c-comment">// [x, b]

[Link](list);
[Link](list);
boolean has = [Link]("b");

Java Notes — handwritten style · pg. 21


PART 4 · COLLECTIONS 22 / 41

Set: Hash / Linked / Tree


HashSet
Backed by a HashMap internally. O(1)
add/remove/contains average. NO ordering guarantee.
Uses hashCode() + equals() to detect duplicates — equal objects
override BOTH together for custom objects! MUST have equal
hashCodes —
LinkedHashSet & TreeSet breaking this
LinkedHashSet → HashSet + remembers insertion corrupts
order.
HashSet/HashMap!
TreeSet → Red-Black tree, keeps elements sorted
(natural order or custom Comparator). O(log n) ops.

// example
Set<String> hs = new HashSet<>();
[Link]("apple");
[Link]("banana"); [Link]("apple");
class="c-comment">// dup ignored
class="c-comment">// 2
[Link]([Link]());

Set<String> ts = new TreeSet<>(hs);


class="c-comment">//
[Link](ts);
[apple, banana] sorted!

needs hashCode+equals
class="c-comment">// custom object
overridden:
class Point {
int x, y;
ect o) { /* compare
@Override public boolean equals(Obj
x,y */ return true; }
n
@Override public int hashCode() { retur
[Link](x, y); }
}

Java Notes — handwritten style · pg. 22


PART 4 · COLLECTIONS 23 / 41

Map: HashMap / TreeMap /


LinkedHashMap
HashMap internals
Array of buckets ; key's hashCode() decides the bucket.
Collisions handled via linked list (or a tree if a bucket gets
big, Java 8+).
merge() replaces
get/put average O(1) , worst case O(log n) (Java 8+ the old "if
treeified buckets) or O(n) pre-Java8. containsKey ... else
Allows ONE null key, multiple null values. NOT thread-safe put" boilerplate!
(use ConcurrentHashMap for that).

Variants
LinkedHashMap → preserves insertion order (or access
order — great for LRU cache!).
TreeMap → keys sorted automatically, O(log n),
implements NavigableMap (floorKey, ceilingKey etc).

Key methods
put, get, getOrDefault(k, default), containsKey, remove,
keySet(), values(), entrySet()
merge/compute/computeIfAbsent → powerful for
counters & grouping in one line!

// example
Map<String, Integer> wordCount = new HashMap<>();
String[] words = {"a","b","a","c","a"};
for (String w : words)
[Link](w, 1, Integer::sum); class="c-
comment">// increments count!
[Link](wordCount); class="c-comment">// {a=3,
b=1, c=1}

for ([Link]<String,Integer> e : [Link]


ySet())
[Link]([Link]() + "=" + [Link]());

Java Notes — handwritten style · pg. 23


PART 4 · COLLECTIONS 24 / 41

Queue, Deque & Stack


Queue (FIFO)
offer(e)/add(e) → insert at tail. poll()/remove() → remove
head. peek()/element() → look, don't remove.
offer/poll/peek return null/false on failure; avoid the old
add/remove/element THROW exceptions — pick based on [Link]
need. class — it's legacy
& synchronized
Deque (double-ended)
(slow); use
Can add/remove from BOTH ends: addFirst, addLast,
ArrayDeque
removeFirst, removeLast.
ArrayDeque is the modern replacement for both Stack
AND legacy Queue impls — faster than Stack/LinkedList.
Use as a Stack: push()=addFirst, pop()=removeFirst,
peek()=peekFirst.

PriorityQueue
Min-heap by default (smallest first) — O(log n)
insert/remove. Pass a Comparator for max-heap or custom
order.

// example
Deque<Integer> stack = new ArrayDeque<>();
[Link](1); [Link](2); [Link](3);
[Link]([Link]()); class="c-comment">// 3
(LIFO)

Queue<Integer> queue = new LinkedList<>();


[Link](1); [Link](2);
ment">// 1
[Link]([Link]()); class="c-com
(FIFO)

e<>
PriorityQueue<Integer> pq = new PriorityQueu
([Link]());
[Link]([Link](5,1,9,3));
[Link]([Link]()); class="c-comment">// 9
(max-heap here)

Java Notes — handwritten style · pg. 24


PART 4 · COLLECTIONS 25 / 41

Iterator, Comparable & Comparator


Iterator
Safe way to loop + remove while iterating: [Link](), [Link](), [Link]().
Never modify a collection with a for-each loop or [Link]() directly while
iterating → ConcurrentModificationException!
compareTo/compare
Comparable (natural order)
returns:
Implement inside the class itself: implements Comparable<T> { compareTo(T o) }
negative=less,
Only ONE natural ordering possible per class. 0=equal,
positive=greater
Comparator (custom order)
External, flexible — as many as you want, don't touch the original class.
Modern lambda style:
[Link](Person::getAge).thenComparing(Person::getName).reversed()

// example
List<Integer> nums = new ArrayList<>([Link](1,2,3,4,5));
Iterator<Integer> it = [Link]();
while ([Link]()) {
if ([Link]() % 2 == 0) [Link](); class="c-comment">// SAFE removal
}

class Person implements Comparable<Person> {


String name; int age;
public int compareTo(Person o) { return [Link] - [Link]; }
}

List<Person> people = getPeople();


[Link]([Link]((Person p) -> [Link]).reversed
());

Java Notes — handwritten style · pg. 25


PART 5 · GENERICS 26 / 41

Generics
Why generics
Compile-time type safety + no manual casting.
List<String> guarantees only Strings go in — caught at
compile time, not runtime.
generics are
Pre-generics (Java 1.4-) you'd store Object and cast ERASED at
everywhere → ClassCastException risk. runtime ("type
erasure") —
Generic classes & methods
List<String> and
class Box<T> { T value; } → T is a type placeholder,
List<Integer> are
replaced at usage: Box<String>
same .class!
Generic method: static <T> T firstElement(List<T> list) {
return [Link](0); }
Multiple params: class Pair<K, V> { K key; V value; }

Bounded types & wildcards


Bounded: <T extends Number> → T must be Number or
subclass.
? extends T (upper bound, "producer") → read-only, safe
to READ as T.
? super T (lower bound, "consumer") → safe to WRITE T
into it.
PECS mnemonic: Producer Extends, Consumer Super.

// example
class Box<T> {
private T value;
void set(T value) { [Link] = value; }
T get() { return value; }
}
Box<Integer> b = new Box<>();
[Link](42); class="c-comment">// only ints allowed
- compile time checked!

static double sumAll(List<? extends Number> list) {


class="c-comment">// producer
double sum = 0;
for (Number n : list) sum += [Link]();
return sum;
}

Java Notes — handwritten style · pg. 26


PART 6 · FUNCTIONAL JAVA (8+) 27 / 41

λLambda Expressions
Syntax
(parameters) -> expression OR (parameters) -> {
statements; return x; }
Only works where a functional interface (exactly ONE lambda body
abstract method) is expected. can't reassign a
Types are usually inferred; can omit parens for single variable from the
param: x -> x*2 enclosing scope!

Why
Replaces verbose anonymous classes for simple behavior-
passing — huge boilerplate reduction.
Enables treating behavior as data — pass functions as
arguments.
Lambdas can capture outer variables, but they must be
effectively final (never reassigned after).

// example
class="c-comment">// old way: anonymous
class
Comparator<String> byLength = new Compa
rator<String>() {
public int compare(String a, String b)
{ return
[Link]() - [Link](); }
};

class="c-comment">// new way: lambda


(same thing!)
Comparator<String> byLen = (a, b) ->
[Link]() -
[Link]();

Runnable r = () -> [Link]("


run!");
List<Integer> nums = [Link](1,2,3);
[Link](n -> [Link](n
* n));

Java Notes — handwritten style · pg. 27


PART 6 · FUNCTIONAL JAVA (8+) 28 / 41

Functional Interfaces
Built-in ones ([Link])
Function<T,R> → T apply(T)→R (transform)
Predicate<T> → boolean test(T) (filter/condition)
Consumer<T> → void accept(T) (do something, no method
return) reference forms:
Supplier<T> → T get() (produce a value, no input) Class::static,
obj::instance,
BiFunction<T,U,R>, UnaryOperator<T>,
Class::instance,
BinaryOperator<T> → specialized variants
Class::new
@FunctionalInterface
Annotation → marks an interface as having exactly 1
abstract method; compiler enforces this.
Method references are shorthand lambdas:
ClassName::methodName, instance::methodName,
ClassName::new

// example
Predicate<Integer> isEven = n -> n % 2 == 0;
[Link]([Link](4)); class="c-
comment">// true

Function<String, Integer> len = String::length; class="c-


comment">// method reference!
[Link]([Link]("hello")); class="c-
comment">// 5

Supplier<List<String>> listMaker = ArrayList::new;


class="c-comment">// constructor ref

@FunctionalInterface
interface Calculator { int calc(int a, int b); }
Calculator add = (a, b) -> a + b;

Java Notes — handwritten style · pg. 28


PART 6 · FUNCTIONAL JAVA (8+) 29 / 41

Streams API
Pipeline structure
Source (collection/array) → intermediate ops (lazy,
chainable) → terminal op (triggers execution).
Intermediate: filter(), map(), sorted(), distinct(), limit(), streams are
skip(), flatMap() declarative ("what")
Terminal: collect(), forEach(), count(), reduce(), not imperative
anyMatch(), min()/max(), toArray() ("how") — great for
Streams don't run until a terminal op is called (lazy readability
evaluation), and can only be consumed ONCE.

Collectors
[Link](), toSet(), toMap(k,v), joining(", "),
groupingBy(fn), counting(), summingInt(fn)

// example
List<String> names =
[Link]("Amit","Riya","Zoe","Aman","Bob");

List<String> result = [Link]()


.filter(n -> [Link]() > 3)
.map(String::toUpperCase)
.sorted()
.collect([Link]());
class="c-comment">// [AMAN, AMIT, RIYA]

Map<Character, List<String>> grouped = [Link]()


.collect([Link](n -> [Link](0)));

int total = [Link](1,2,3,4).stream()


.reduce(0, Integer::sum); class="c-
comment">// 10

Java Notes — handwritten style · pg. 29


PART 6 · FUNCTIONAL JAVA (8+) 30 / 41

❓Optional
Purpose
A container that may or may not hold a value — makes
"absence" explicit instead of returning null everywhere.
Goal: reduce NullPointerExceptions by forcing callers to don't call .get()
handle the empty case. without checking
isPresent() first —
Core methods
defeats the whole
[Link](x) (non-null), [Link](x) (may be
purpose!
null), [Link]()
isPresent()/isEmpty(), get() (risky, throws if empty!),
orElse(default), orElseGet(supplier), orElseThrow()
ifPresent(consumer), map(fn), filter(predicate) —
chainable, stream-like!

// example
Optional<String> findUser(int id) {
:
return id == 1 ? [Link]("Alice")
[Link]();
}

String name = findUser(2)


.map(String::toUpperCase)
.orElse("UNKNOWN"); class="c-comment">//
"UNKNOWN" (no exception!)

.println("Found: " +
findUser(1).ifPresent(n -> [Link]
n));

Java Notes — handwritten style · pg. 30


PART 7 · CONCURRENCY 31 / 41

Threads Basics
Creating threads
Extend Thread and override run() — OR implement
Runnable and pass to `new Thread(runnable)`
(preferred! keeps single inheritance free).
calling run()
Call .start() to actually spawn a new thread — calling instead of start() is
.run() directly just runs it on the current thread (common a top beginner
mistake!).
mistake — runs on
SAME thread!
Lifecycle
NEW → RUNNABLE → (BLOCKED/WAITING/TIMED_WAITING)
→ TERMINATED
join() → caller thread waits for this thread to finish.
sleep(ms) → pauses current thread, doesn't release locks
it holds.

// example
class MyTask implements Runnable {
public void run() { [Link]("Running
in: " +
[Link]().getName()); }
}

Thread t1 = new Thread(new MyTask());


[Link](); class="c-comment">// NOT [Link]()!
[Link](); class="c-comment">// main thread
waits here

class="c-comment">// lambda works too, since Runnabl


e is
functional interface
Thread t2 = new Thread(() -> [Link]("l
ambda
thread"));
[Link]();

Java Notes — handwritten style · pg. 31


PART 7 · CONCURRENCY 32 / 41

Synchronization & Locks


The race-condition problem
Two threads modifying shared state simultaneously →
unpredictable/wrong results ("race condition").
synchronized keyword → only ONE thread can hold the count++ looks
lock on an object/method at a time. atomic but is
actually READ-
Options modify-WRITE —
synchronized method → locks on `this`. synchronized(obj) 3 steps, NOT
block → locks on a specific object, finer control. thread-safe!
volatile → guarantees visibility of a variable's latest value
across threads (NOT atomicity though!).
[Link] → explicit lock()
/ unlock(), more flexible (tryLock, fairness).
[Link] classes (AtomicInteger etc)
→ lock-free, thread-safe counters.

// example
class Counter {
private int count = 0;
+; }
public synchronized void increment() { count+
class="c-comment">// atomic now
}
public synchronized int get() { return count;
}

class="c-comment">// or with explicit lock:


class Counter2 {
private int count = 0;
antLock();
private final ReentrantLock lock = new Reentr
void increment() {
[Link]();
try { count++; } finally { [Link](); }
y!
class="c-comment">// ALWAYS unlock in finall
}
}

Java Notes — handwritten style · pg. 32


PART 7 · CONCURRENCY 33 / 41

⚙Executor Framework
Why not raw threads
Creating a new Thread per task is expensive &
unmanaged. ExecutorService = a managed thread pool.
[Link](n), forgetting
newCachedThreadPool(), newSingleThreadExecutor(), [Link]() =
newScheduledThreadPool(n)
classic reason a
Java app "hangs"
Submitting work
and won't exit
execute(Runnable) → fire and forget. submit(Callable<T>)
→ returns a Future<T> you can .get() a result from
(blocks until done).
Callable is like Runnable but CAN return a value + throw
checked exceptions.
Always call shutdown() when done, or the pool's threads
keep the JVM alive forever!

// example
ExecutorService pool = [Link](4);

Future<Integer> future = [Link](() -> {


[Link](1000);
return 42;
});

for (int i = 0; i < 10; i++) {


int taskId = i;
[Link](() -> [Link]("task " +
taskId));
}

[Link]([Link]()); class="c-comment">//
blocks until 42 is ready
[Link](); class="c-comment">//
don't forget!

Java Notes — handwritten style · pg. 33


PART 8 · ADVANCED 34 / 41

File I/O & NIO


Classic [Link]
FileReader/FileWriter → char streams (text).
FileInputStream/FileOutputStream → byte streams
(binary).
prefer
Wrap in BufferedReader/BufferedWriter for [Link]
performance (reduces actual disk hits). for new code —
Always use try-with-resources so streams auto-close! fewer lines, better
exceptions
Modern [Link]
Path + Files utility class (Java 7+) → much cleaner API.
[Link](path), [Link](path, lines),
[Link](path), [Link](), [Link]()

// example
class="c-comment">// modern NIO way - very clean:
Path path = [Link]("[Link]");
List<String> lines = [Link](path);
[Link](path, "new content",
[Link]);

class="c-comment">// classic buffered reading:


try (BufferedReader br = new BufferedReader(new
FileReader("[Link]"))) {
String line;
while ((line = [Link]()) != null) {
[Link](line);
}
} catch (IOException e) { [Link](); }

Java Notes — handwritten style · pg. 34


PART 8 · ADVANCED 35 / 41

Serialization
Concept
Converting an object's state → byte stream (to save to
disk / send over network). Deserialization = reverse.
Class must implement the marker interface Serializable modern apps
(no methods to implement!). often use JSON
serialVersionUID → version ID; mismatch during (Jackson/Gson)
deserialize throws InvalidClassException. instead of native
transient keyword → field is SKIPPED during serialization Java serialization
(e.g. passwords, caches).

// example
class User implements Serializable {
private static final long serialVersionUI
D = 1L;
String name;
transient String password; class="c-comment">// NOT
serialized!
}

class="c-comment">// writing
try (ObjectOutputStream out = new Objec
tOutputStream(new
FileOutputStream("[Link]"))) {
[Link](new User());
}
class="c-comment">// reading
try (ObjectInputStream in = new Objec
tInputStream(new
FileInputStream("[Link]"))) {
User u = (User) [Link]();
}

Java Notes — handwritten style · pg. 35


PART 8 · ADVANCED 36 / 41

Reflection
What it does
Inspect/manipulate classes, methods, fields at runtime —
even private ones!
Powers frameworks like Spring, Hibernate, JUnit (e.g. reflection can
reading @annotations, calling methods dynamically). access private
Entry point: [Link](), or [Link], or members - powerful
[Link]("[Link]") but breaks
encapsulation, use
Caution carefully
Slower than direct calls, bypasses compile-time type
safety, can break encapsulation (setAccessible(true)).
Use sparingly in application code — great for
tools/frameworks, risky for everyday business logic.

// example
Class<?> cls = [Link]("[Link]");
[Link]([Link]()); class="c-
comment">// String

for (Method m : [Link]())


[Link]([Link]());

class="c-comment">// invoking a private field via


reflection:
Field f = [Link]("secret");
[Link](true); class="c-comment">// bypass
private!
Object value = [Link](myObjectInstance);

Java Notes — handwritten style · pg. 36


PART 8 · ADVANCED 37 / 41

Annotations
Built-in ones
@Override, @Deprecated,
@SuppressWarnings("unchecked"), @FunctionalInterface,
@SafeVarargs
this is exactly
Creating your own how JUnit's @Test
annotation works
@interface MyAnnotation { String value(); int priority()
default 1; }
under the hood!

Meta-annotations control behavior: @Retention


(SOURCE/CLASS/RUNTIME — RUNTIME needed if read via
reflection), @Target (what it can annotate: METHOD,
FIELD, TYPE...).
Annotations alone do NOTHING — some other code
(compiler, framework, your reflection code) must read &
act on them.

// example
@Retention([Link])
@Target([Link])
@interface Test {
String description() default "";
}

class Calc {
@Test(description = "checks addition")
void testAdd() { /* ... */ }
}

class="c-comment">// framework code reading it via


reflection:
for (Method m : [Link]()) {
if ([Link]([Link])) {
[Link]("Found test: " + [Link]());
}
}

Java Notes — handwritten style · pg. 37


PART 8 · ADVANCED 38 / 41

Memory Management & GC


Stack vs Heap
Stack → method calls, local vars, primitives — fast, LIFO,
auto-cleaned when method returns. Each thread has its
own.
memory leaks
Heap → all objects live here (shared across threads). CAN still happen
Managed by Garbage Collector. in Java — e.g.
objects stuck in a
Heap generations
static collection
Young Gen (Eden + 2 Survivor spaces) → new objects,
forever!
frequent "minor GC", fast.
Old Gen (Tenured) → long-lived objects promoted here
after surviving several minor GCs. "Major/Full GC" is
slower & rarer.
Metaspace (Java 8+, replaced PermGen) → class
metadata, method info.

GC basics
GC uses reachability: an object with no live references is
eligible for collection.
You cannot force GC — [Link]() is only a
*request/hint*, JVM may ignore it.
Common GC algorithms: Serial, Parallel, G1 (default
since Java 9), ZGC/Shenandoah (low-pause, for huge
heaps).

Java Notes — handwritten style · pg. 38


PART 8 · ADVANCED 39 / 41

JDBC Basics
Core objects
[Link](url, user, pass) →
opens a Connection to the DB.
Statement → run static SQL. PreparedStatement → "SELECT *
precompiled + parameterized, prevents SQL injection , FROM users
faster for repeated runs. WHERE id=" +
ResultSet → cursor over query results, .next() moves userInput -> classic
forward, .getString/getInt(col) reads values. SQL injection
vulnerability!
Best practice
ALWAYS use PreparedStatement with ? placeholders for
any user input — never string-concatenate SQL!
Use try-with-resources for
Connection/Statement/ResultSet — they all implement
AutoCloseable.

// example
String url = "jdbc:mysql:class="c-
comment">//localhost:3306/mydb";
try (Connection conn = [Link](
url,
"user", "pass");
PreparedStatement ps = [Link](
"SELECT name FROM users WHERE id = ?")) {
[Link](1, 42);
try (ResultSet rs = [Link]()) {
while ([Link]()) {
[Link]([Link]("name"));
}
}
} catch (SQLException e) { [Link]();
}

Java Notes — handwritten style · pg. 39


PART 8 · ADVANCED 40 / 41

Design Patterns
Singleton
Only ONE instance ever exists globally. Private constructor
+ static getInstance().
Best modern approach: enum singleton (thread-safe, GoF catalogued
serialization-safe, one line!). 23 patterns across
Creational /
Factory Structural /
Encapsulate object creation logic → caller doesn't need to Behavioral —
know the concrete class, just the interface.
these 4 come up
most
Builder
For objects with MANY optional params — avoids
telescoping constructors, chainable & readable.

Observer
One-to-many dependency: when subject changes state, all
registered observers get notified automatically (pub-sub
base).

// example
d-safe, best
class="c-comment">// Singleton via enum (threa
practice)
enum AppConfig {
INSTANCE;
private String setting = "default";
public String get() { return setting; }
}
[Link]();

class="c-comment">// Builder pattern


Pizza p = new [Link]()
.size("L")
.topping("cheese")
.topping("olives")
.build();

Java Notes — handwritten style · pg. 40


PART 8 · ADVANCED 41 / 41


Best Practices & Interview Cheat-Sheet
Code quality
Favor composition over inheritance when possible —
more flexible, less fragile.
Program to an interface, not implementation: List<T> l = you made it
new ArrayList<>(); through advanced
Make classes immutable where possible (final fields, no Java — now go
setters) — thread-safe by default, easier to reason about. build something!
Always override equals() + hashCode() together, and
toString() for debugging.

Common interview gotchas recap


== vs .equals() | String immutability + pool | ArrayList vs
LinkedList Big-O
checked vs unchecked exceptions | overloading vs
overriding | abstract class vs interface
HashMap vs TreeMap vs LinkedHashMap | PECS wildcards |
start() vs run() | final/finally/finalize (3 different things!)

final vs finally vs finalize


final → keyword, non-reassignable/non-overridable/non-
extendable.
finally → block that always executes after try/catch.
finalize() → deprecated GC hook method, called before
object is collected — don't rely on it!

Java Notes — handwritten style · pg. 41

You might also like