Java & Concurrent Programming — Complete Notes
Java & Concurrent Programming
Complete Study Notes — Basic to Advanced
Classes • OOP • Threads • Lambda • Generics • Collections • Streams
Page 1
Java & Concurrent Programming — Complete Notes
SECTION 1 — Java Fundamentals
1. JVM, JRE & JDK
Understanding the Java platform is the first step.
JVM — Java Virtual Machine
The JVM is the engine that executes Java bytecode. It is platform-specific (Windows/Linux/Mac
each have their own JVM) but the bytecode it runs is platform-independent.
Source (.java) → javac compiler → Bytecode (.class) → JVM → OS/Hardware
JRE — Java Runtime Environment
JRE = JVM + standard libraries ([Link], [Link], etc). Needed to RUN Java programs. End-
users install JRE.
JDK — Java Development Kit
JDK = JRE + javac compiler + javadoc + debugger + tools. Needed to WRITE and COMPILE Java
programs. Developers install JDK.
📝 NOTE: Memory trick: JDK ⊃ JRE ⊃ JVM. Each is inside the previous.
2. Data Types
Primitive Types (8 total)
byte b = 100; // 8-bit, -128 to 127
short s = 30000; // 16-bit
int i = 1000000; // 32-bit ← most common
long l = 9999999999L; // 64-bit (needs L suffix)
float f = 3.14f; // 32-bit decimal (needs f)
double d = 3.14159; // 64-bit decimal ← most common
char c = 'A'; // 16-bit Unicode character
boolean flag = true; // true or false only
Reference Types
Everything else — String, arrays, objects. They store a memory address (reference), not the actual
value.
String name = "Alice"; // reference to String object
int[] nums = {1, 2, 3}; // reference to array
💡 TIP: Use double for money calculations (or better: BigDecimal). Never use float for financial
data — it has rounding errors.
Wrapper Classes
Each primitive has an Object version (used in Collections):
int → Integer double → Double
char → Character boolean → Boolean
Integer x = 5; // autoboxing: int → Integer
int y = x; // unboxing: Integer → int
Page 2
Java & Concurrent Programming — Complete Notes
SECTION 2 — Object-Oriented Programming
3. Classes & Objects
A class is a blueprint. An object is a real instance of that blueprint.
class Car {
// Fields (state)
String brand;
int speed;
// Constructor
Car(String brand, int speed) {
[Link] = brand; // 'this' = current object
[Link] = speed;
}
// Method (behaviour)
void drive() {
[Link](brand + " going " + speed + " km/h");
}
}
// Creating objects
Car myCar = new Car("Toyota", 120);
[Link](); // Output: Toyota going 120 km/h
Constructors — Key Rules
• Same name as class, no return type
• Called automatically when 'new' is used
• If you don't write one, Java provides a default empty constructor
• You can have multiple constructors (constructor overloading)
class Box {
int width, height;
Box() { width = 10; height = 10; } // default
Box(int w, int h) { width = w; height = h; } // parameterized
}
this keyword
'this' refers to the current object. Used to distinguish fields from parameters, or to call another
constructor.
class Person {
String name;
Person(String name) {
[Link] = name; // [Link] = field, name = parameter
}
Person() {
this("Unknown"); // calls Person(String) constructor
}
}
Page 3
Java & Concurrent Programming — Complete Notes
4. Access Modifiers
Control who can see your class members:
public → accessible everywhere
private → only inside the same class
protected → same class + subclasses + same package
(default) → only within the same package
class BankAccount {
private double balance; // nobody outside can touch directly
public void deposit(double amount) { // public method = safe gateway
if (amount > 0) balance += amount;
}
public double getBalance() { return balance; }
}
💡 TIP: Always make fields private and provide public getters/setters — this is Encapsulation.
5. Inheritance (extends)
Inheritance lets a child class reuse fields and methods of a parent class. Java supports single
inheritance (one parent only).
class Animal {
String name;
void eat() { [Link](name + " is eating"); }
}
class Dog extends Animal { // Dog inherits from Animal
void bark() { [Link](name + " says Woof!"); }
}
Dog d = new Dog();
[Link] = "Rex";
[Link](); // inherited from Animal
[Link](); // Dog's own method
super keyword
'super' calls parent class methods or constructors.
class Vehicle {
Vehicle(String type) { [Link]("Vehicle: " + type); }
void info() { [Link]("I am a vehicle"); }
}
class Bike extends Vehicle {
Bike() {
super("Bike"); // calls Vehicle(String) constructor FIRST
}
void info() {
[Link](); // calls parent method
[Link]("I am a Bike");
}
}
📝 NOTE: super() must be the FIRST statement inside a constructor if used.
Page 4
Java & Concurrent Programming — Complete Notes
6. Overloading vs Overriding
Method Overloading — Same name, different parameters (same class)
class Calculator {
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; } // overloaded
int add(int a, int b, int c) { return a + b + c;} // overloaded
}
// Java picks the right one based on arguments at COMPILE TIME
Method Overriding — Redefine parent method in child class
class Shape {
void draw() { [Link]("Drawing a shape"); }
}
class Circle extends Shape {
@Override
void draw() { [Link]("Drawing a circle"); } // overrides
}
Shape s = new Circle();
[Link](); // Output: Drawing a circle ← RUNTIME decides (polymorphism)
💡 TIP: Always use @Override annotation — it makes the compiler verify you're actually
overriding, not creating a new method.
Quick Comparison:
Feature | Overloading | Overriding
----------------|----------------------|---------------------------
Where | Same class | Parent → Child
Parameters | Must differ | Must be same
Return type | Can differ | Must be same (or covariant)
Resolved at | Compile time | Runtime
@Override | Not needed | Recommended
7. Abstract Class
An abstract class cannot be instantiated. It can have abstract methods (no body — child MUST
implement) and concrete methods (with body).
abstract class Shape {
String color;
abstract double area(); // no body — subclass must implement
void printColor() { // concrete method — shared by all
[Link]("Color: " + color);
}
}
class Circle extends Shape {
double radius;
Circle(double r) { [Link] = r; }
Page 5
Java & Concurrent Programming — Complete Notes
@Override
double area() { return [Link] * radius * radius; } // must implement
}
// Shape s = new Shape(); // ERROR — cannot instantiate abstract class
Shape c = new Circle(5);
[Link]([Link]()); // 78.53...
8. Interface
An interface is a pure contract — all methods are abstract by default (before Java 8). A class can
implement multiple interfaces.
interface Flyable {
void fly(); // implicitly public abstract
default void land() { // default method (Java 8+)
[Link]("Landing...");
}
}
interface Swimmable {
void swim();
}
class Duck implements Flyable, Swimmable { // multiple interfaces OK
public void fly() { [Link]("Duck flying"); }
public void swim() { [Link]("Duck swimming"); }
}
Abstract Class vs Interface:
Feature | Abstract Class | Interface
-------------------|-------------------------|---------------------------
Instantiate | No | No
Constructor | Yes | No
Fields | Any type | public static final only
Methods | Abstract + concrete | Abstract + default (J8+)
Multiple inherit | No (one class only) | Yes (many interfaces)
Use when | 'IS-A' shared base | 'CAN-DO' capability
📝 NOTE: Rule of thumb: Use interface for behaviours (Runnable, Comparable). Use abstract
class for a common base with shared code.
9. Static Keyword
Static members belong to the CLASS, not to any object. Shared across all instances.
class Counter {
static int count = 0; // shared by ALL Counter objects
int id;
Counter() {
count++; // increments the class-level counter
id = count;
}
Page 6
Java & Concurrent Programming — Complete Notes
static void showCount() { // static method — no 'this'
[Link]("Total: " + count);
}
}
Counter a = new Counter(); // count = 1
Counter b = new Counter(); // count = 2
[Link](); // Output: Total: 2
💡 TIP: Static methods cannot access instance (non-static) fields. If you try, you get a compile
error.
10. final Keyword
// final variable = constant (cannot reassign)
final double PI = 3.14159;
// PI = 3.0; // COMPILE ERROR
// final method = cannot be overridden
class Parent {
final void show() { [Link]("Cannot override this"); }
}
// final class = cannot be extended (e.g., String is final)
final class Immutable { }
// class Child extends Immutable { } // COMPILE ERROR
11. Polymorphism
Poly = many, morph = forms. One reference, many behaviours. Java resolves which method to call
at RUNTIME based on the actual object type.
class Animal {
void sound() { [Link]("Some sound"); }
}
class Cat extends Animal {
void sound() { [Link]("Meow"); }
}
class Dog extends Animal {
void sound() { [Link]("Woof"); }
}
Animal[] animals = { new Cat(), new Dog(), new Cat() };
for (Animal a : animals) {
[Link](); // Meow, Woof, Meow — same call, different outputs
}
instanceof — check type at runtime
Animal a = new Dog();
if (a instanceof Dog) {
Dog d = (Dog) a; // safe cast
[Link]();
}
Page 7
Java & Concurrent Programming — Complete Notes
// Java 16+ pattern matching (cleaner)
if (a instanceof Dog d) {
[Link](); // d is already cast
}
SECTION 3 — Exception Handling
12. Exceptions
An exception is an event that disrupts normal program flow. Java uses try-catch-finally to handle
them.
try {
int result = 10 / 0; // ArithmeticException
String s = null;
[Link](); // NullPointerException
} catch (ArithmeticException e) {
[Link]("Math error: " + [Link]());
} catch (NullPointerException e) {
[Link]("Null ref: " + [Link]());
} catch (Exception e) { // catch-all (must be last)
[Link]("Unexpected: " + [Link]());
} finally {
[Link]("Always runs — cleanup here");
}
Checked vs Unchecked Exceptions
Checked (must handle or declare with throws):
IOException, SQLException, ClassNotFoundException
Unchecked (RuntimeException — optional to handle):
NullPointerException, ArrayIndexOutOfBoundsException,
ClassCastException, ArithmeticException, NumberFormatException
Custom Exception
class InsufficientFundsException extends Exception {
InsufficientFundsException(String msg) { super(msg); }
}
void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance)
throw new InsufficientFundsException("Balance too low!");
balance -= amount;
}
try-with-resources (Java 7+)
Automatically closes resources (files, connections) — no need for finally.
try (FileReader fr = new FileReader("[Link]");
BufferedReader br = new BufferedReader(fr)) {
String line = [Link]();
} catch (IOException e) {
[Link]();
}
// fr and br are automatically closed after try block
Page 8
Java & Concurrent Programming — Complete Notes
SECTION 4 — Generics & Collections
13. Generics
Generics allow you to write code that works with any type, while being type-safe at compile time.
// Generic class
class Box<T> {
T value;
Box(T value) { [Link] = value; }
T get() { return value; }
}
Box<Integer> intBox = new Box<>(42);
Box<String> strBox = new Box<>("Hello");
[Link]([Link]()); // 42
// Generic method
static <T> void printArray(T[] array) {
for (T item : array) [Link](item + " ");
}
Bounded Type Parameters
<T extends Number> // T must be Number or its subclass
<T extends Comparable> // T must implement Comparable
static <T extends Number> double sum(T a, T b) {
return [Link]() + [Link]();
}
14. Collections Framework
List — ordered, allows duplicates
List<String> list = new ArrayList<>();
[Link]("A"); [Link]("B"); [Link]("A");
[Link](0); // "A"
[Link](); // 3
[Link]("A"); // removes first occurrence
LinkedList<Integer> ll = new LinkedList<>(); // doubly-linked, better for
insertions
Set — no duplicates, no index
Set<String> set = new HashSet<>(); // unordered
Set<String> sorted = new TreeSet<>(); // sorted alphabetically
Set<String> linked = new LinkedHashSet<>(); // insertion order preserved
[Link]("x"); [Link]("y"); [Link]("x");
[Link]([Link]()); // 2 (no duplicate 'x')
Page 9
Java & Concurrent Programming — Complete Notes
Map — key-value pairs
Map<String, Integer> map = new HashMap<>();
[Link]("Alice", 90);
[Link]("Bob", 85);
[Link]("Alice"); // 90
[Link]("Bob"); // true
[Link]("X", 0); // 0 (key not found)
for ([Link]<String, Integer> e : [Link]()) {
[Link]([Link]() + " → " + [Link]());
}
Queue & Deque
Queue<Integer> q = new LinkedList<>();
[Link](1); [Link](2); [Link](3);
[Link](); // removes & returns 1 (FIFO)
[Link](); // returns 2 without removing
Deque<Integer> dq = new ArrayDeque<>();
[Link](1); // add to front
[Link](2); // add to back
💡 TIP: ArrayList → fast random access. LinkedList → fast insert/delete at ends. HashMap →
fastest lookup. TreeMap → sorted keys.
SECTION 5 — Lambda & Functional Programming
15. Lambda Expressions (Java 8+)
A lambda is an anonymous function — a compact way to pass behaviour as data. Lambdas
implement functional interfaces (interfaces with exactly 1 abstract method).
// Traditional anonymous class
Runnable r1 = new Runnable() {
public void run() { [Link]("Running"); }
};
// Lambda equivalent (much shorter!)
Runnable r2 = () -> [Link]("Running");
// Syntax: (parameters) -> { body }
// If single expression, no braces needed
(int a, int b) -> a + b
x -> x * x
() -> [Link]("hello")
Common Functional Interfaces ([Link])
// Predicate<T> — takes T, returns boolean
Predicate<Integer> isEven = n -> n % 2 == 0;
[Link](4); // true
// Function<T, R> — takes T, returns R
Function<String, Integer> len = s -> [Link]();
[Link]("Hello"); // 5
// Consumer<T> — takes T, returns nothing
Page 10
Java & Concurrent Programming — Complete Notes
Consumer<String> print = s -> [Link](s);
[Link]("Hi");
// Supplier<T> — takes nothing, returns T
Supplier<Double> random = () -> [Link]();
[Link](); // 0.342...
// BiFunction<T, U, R> — takes T and U, returns R
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
[Link](3, 4); // 7
Method References
// Lambda: s -> [Link](s)
// Method ref: [Link]::println
List<String> names = [Link]("Bob", "Alice", "Charlie");
[Link]([Link]::println); // instance method of arg
[Link](String::compareToIgnoreCase); // static method
// Constructor reference
Supplier<ArrayList> newList = ArrayList::new;
ArrayList list = [Link]();
16. Stream API (Java 8+)
Streams let you process collections in a declarative, pipeline style — like SQL for Java objects.
List<Integer> nums = [Link](1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Find sum of even numbers squared
int result = [Link]()
.filter(n -> n % 2 == 0) // keep evens: [2,4,6,8,10]
.map(n -> n * n) // square each: [4,16,36,64,100]
.reduce(0, Integer::sum); // sum all: 220
[Link](result); // 220
Key Stream Operations
// INTERMEDIATE (lazy, return Stream)
.filter(predicate) // keep elements that match
.map(function) // transform each element
.flatMap(function) // flatten nested streams
.sorted() // sort (natural or with Comparator)
.distinct() // remove duplicates
.limit(n) // take first n
.skip(n) // skip first n
// TERMINAL (eager, trigger processing)
.collect([Link]()) // gather into List
.collect([Link](...)) // gather into Map
.forEach(consumer) // perform action
.count() // number of elements
.findFirst() // Optional<T>
.anyMatch(predicate) // true if any matches
.allMatch(predicate) // true if all match
Page 11
Java & Concurrent Programming — Complete Notes
.min() / .max() // Optional<T>
// Group students by grade
Map<String, List<Student>> byGrade = [Link]()
.collect([Link](Student::getGrade));
// Names as comma-separated string
String result = [Link]()
.filter(n -> [Link]("A"))
.collect([Link](", "));
SECTION 6 — Concurrent Programming & Threads
17. Threads — Basics
A thread is the smallest unit of execution. Java supports multithreading — running multiple threads
in parallel.
Way 1: Extend Thread class
class MyThread extends Thread {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
[Link](getName() + " → " + i);
try { [Link](100); } catch (InterruptedException e) {}
}
}
}
MyThread t1 = new MyThread();
[Link]("Thread-A");
[Link](); // starts new thread (DON'T call run() directly!)
Way 2: Implement Runnable (preferred)
class Task implements Runnable {
public void run() {
[Link]([Link]().getName() + " working");
}
}
Thread t = new Thread(new Task());
[Link]();
// Or with lambda (cleanest)
Thread t2 = new Thread(() -> [Link]("Lambda thread"));
[Link]();
💡 TIP: Prefer Runnable over extending Thread. It separates task logic from threading
mechanism, and your class can still extend another class.
Thread Lifecycle
NEW → RUNNABLE → RUNNING → BLOCKED/WAITING/TIMED_WAITING → TERMINATED
new Thread(r) // NEW
[Link]() // RUNNABLE (ready to run)
thread runs // RUNNING (OS scheduler picks it)
Page 12
Java & Concurrent Programming — Complete Notes
[Link](ms) // TIMED_WAITING
[Link]() // WAITING (needs notify)
synchronized block // BLOCKED (waiting for lock)
run() finishes // TERMINATED
Important Thread Methods
[Link]() // start the thread
[Link]() // wait for thread to finish
[Link](1000) // wait max 1000ms
[Link](500) // pause current thread 500ms
[Link]() // request thread to stop
[Link]() // is thread still running?
[Link](1-10) // 1=min, 5=normal, 10=max
[Link]() // get reference to current thread
18. Synchronization — Race Conditions
When multiple threads access shared data simultaneously, you get race conditions.
Synchronization ensures only one thread accesses critical code at a time.
The Problem (Race Condition)
class Counter {
int count = 0;
void increment() { count++; } // NOT thread-safe! count++ is 3 ops
}
// Two threads incrementing simultaneously can lose updates
Solution 1: synchronized method
class SafeCounter {
int count = 0;
synchronized void increment() { // only one thread at a time
count++;
}
synchronized int getCount() { return count; }
}
Solution 2: synchronized block (finer-grained)
class SafeCounter {
int count = 0;
Object lock = new Object();
void increment() {
synchronized (lock) { // only lock this section
count++;
}
// rest of method runs without lock
}
}
Solution 3: Atomic Variables (fastest)
import [Link].*;
AtomicInteger count = new AtomicInteger(0);
Page 13
Java & Concurrent Programming — Complete Notes
[Link](); // thread-safe, no locks
[Link](5);
[Link](); // read
19. wait() / notify() / notifyAll()
Used for thread communication — one thread waits for a condition, another signals when it's ready.
class SharedBuffer {
List<Integer> buffer = new ArrayList<>();
int maxSize = 5;
synchronized void produce(int item) throws InterruptedException {
while ([Link]() == maxSize) {
wait(); // buffer full — release lock and wait
}
[Link](item);
[Link]("Produced: " + item);
notifyAll(); // wake up waiting consumers
}
synchronized int consume() throws InterruptedException {
while ([Link]()) {
wait(); // nothing to consume — wait
}
int item = [Link](0);
[Link]("Consumed: " + item);
notifyAll(); // wake up waiting producers
return item;
}
}
📝 NOTE: wait() and notify() MUST be called inside a synchronized block/method, otherwise you
get IllegalMonitorStateException.
20. volatile Keyword
volatile ensures that a variable's value is read from main memory, not a thread's local cache. Useful
for flags.
class StopFlag {
volatile boolean stop = false; // 'volatile' = always read from main memory
void stopTask() { stop = true; }
void doWork() {
while (!stop) { // without volatile, might never see the update
// doing work...
}
[Link]("Stopped!");
}
}
💡 TIP: volatile is NOT a replacement for synchronized. It ensures visibility but not atomicity. Use
AtomicInteger for compound operations.
Page 14
Java & Concurrent Programming — Complete Notes
21. Executor Framework (Java 5+)
Creating raw threads for every task is expensive. ExecutorService manages a pool of reusable
threads.
import [Link].*;
// Fixed pool of 4 threads
ExecutorService exec = [Link](4);
for (int i = 0; i < 10; i++) {
final int taskId = i;
[Link](() -> {
[Link]("Task " + taskId + " by " +
[Link]().getName());
});
}
[Link](); // no more tasks accepted
[Link](5, [Link]); // wait for completion
Callable & Future — get results from threads
Callable<Integer> task = () -> {
[Link](1000);
return 42;
};
ExecutorService exec = [Link]();
Future<Integer> future = [Link](task);
// Do other work here...
Integer result = [Link](); // blocks until task completes
[Link]("Result: " + result); // 42
[Link]();
Types of Executor Pools
[Link](n) // fixed n threads
[Link]() // grows/shrinks as needed
[Link]() // exactly 1 thread, tasks queue
[Link](n) // scheduled / periodic tasks
22. ReentrantLock (Advanced Locking)
[Link] provides more flexible locking than synchronized.
import [Link].*;
class SafeAccount {
private double balance;
private final Lock lock = new ReentrantLock();
void deposit(double amount) {
Page 15
Java & Concurrent Programming — Complete Notes
[Link]();
try {
balance += amount;
} finally {
[Link](); // ALWAYS unlock in finally!
}
}
boolean tryTransfer(double amount) {
if ([Link]()) { // non-blocking attempt
try { balance -= amount; return true; }
finally { [Link](); }
}
return false; // lock was busy
}
}
ReadWriteLock — multiple readers OR one writer
ReadWriteLock rwLock = new ReentrantReadWriteLock();
// Multiple threads can read simultaneously
[Link]().lock();
try { /* read data */ } finally { [Link]().unlock(); }
// Only one thread can write
[Link]().lock();
try { /* modify data */ } finally { [Link]().unlock(); }
23. Deadlock
Deadlock happens when two threads each hold a lock and wait for the other's lock — forever.
// Thread 1 holds Lock A, waits for Lock B
// Thread 2 holds Lock B, waits for Lock A
// → both stuck forever!
// Prevention: always acquire locks in the SAME ORDER
// Thread 1: lock A then B
// Thread 2: lock A then B ← consistent order prevents deadlock
💡 TIP: Use tryLock() with timeout to detect and recover from deadlocks. Or use higher-level
constructs like concurrent collections.
24. CountDownLatch & CyclicBarrier
CountDownLatch — wait for N tasks to finish
CountDownLatch latch = new CountDownLatch(3); // count = 3
// 3 worker threads
for (int i = 0; i < 3; i++) {
new Thread(() -> {
doWork();
[Link](); // decrement count
Page 16
Java & Concurrent Programming — Complete Notes
}).start();
}
[Link](); // main thread waits until count reaches 0
[Link]("All workers done!");
CyclicBarrier — all threads wait for each other at a point
CyclicBarrier barrier = new CyclicBarrier(3,
() -> [Link]("All reached barrier!")); // runs when all arrive
for (int i = 0; i < 3; i++) {
new Thread(() -> {
phase1Work();
[Link](); // wait for all threads
phase2Work(); // starts only when all are ready
}).start();
}
25. Concurrent Collections
Regular collections (ArrayList, HashMap) are NOT thread-safe. Use these instead:
// Thread-safe List
List<String> list = new CopyOnWriteArrayList<>();
// Thread-safe HashMap
Map<String, Integer> map = new ConcurrentHashMap<>();
[Link]("key", 1);
[Link]("key", k -> expensiveCompute(k));
// Thread-safe Queue (for producer-consumer)
BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(10);
[Link](1); // blocks if full
int item = [Link](); // blocks if empty
📝 NOTE: ConcurrentHashMap is much faster than [Link]() because it
uses segment-level locking, not a single lock for the whole map.
SECTION 7 — Advanced Java Concepts
26. Inner Classes & Anonymous Classes
Anonymous Class — one-time implementation
// Instead of creating a named class just for one use
Comparator<String> comp = new Comparator<String>() {
public int compare(String a, String b) {
return [Link]() - [Link](); // sort by length
}
};
// Same with lambda (Java 8+)
Comparator<String> comp2 = (a, b) -> [Link]() - [Link]();
Page 17
Java & Concurrent Programming — Complete Notes
Static Nested Class
class Outer {
static class StaticNested {
void show() { [Link]("Static nested"); }
}
}
[Link] obj = new [Link](); // no Outer instance needed
27. Enum
Enums are special classes representing a fixed set of constants.
enum Day {
MON, TUE, WED, THU, FRI, SAT, SUN;
public boolean isWeekend() {
return this == SAT || this == SUN;
}
}
Day today = [Link];
[Link]([Link]()); // false
switch (today) {
case MON: [Link]("Monday blues"); break;
case FRI: [Link]("TGIF!"); break;
}
28. Optional (Java 8+)
Optional wraps a value that might be null, forcing you to handle the absence explicitly — avoids
NullPointerException.
Optional<String> opt1 = [Link]("Hello");
Optional<String> opt2 = [Link]();
Optional<String> opt3 = [Link](null); // safe for null
[Link](); // true
[Link](); // "Hello" (throws if empty)
[Link]("default"); // "default"
[Link](() -> compute()); // lazy default
[Link](String::toUpperCase); // Optional<"HELLO">
[Link](s -> [Link]() > 3); // Optional<"Hello">
// Typical use
Optional<User> user = findUserById(id);
[Link](u -> [Link]([Link]()));
29. String — Important Methods
String s = "Hello World";
Page 18
Java & Concurrent Programming — Complete Notes
[Link]() // 11
[Link](0) // 'H'
[Link]('o') // 4
[Link](6) // "World"
[Link](0, 5) // "Hello"
[Link]() // "hello world"
[Link]() // "HELLO WORLD"
[Link]() // remove leading/trailing spaces
[Link]("World","Java") // "Hello Java"
[Link](" ") // ["Hello", "World"]
[Link]("World") // true
[Link]("He") // true
[Link]("Hello World") // true (use equals, NOT ==)
[Link]("HELLO WORLD") // true
[Link](42) // "42"
[Link]("42") // 42
StringBuilder — mutable String (efficient concatenation)
StringBuilder sb = new StringBuilder();
[Link]("Hello");
[Link](" ");
[Link]("World");
[Link](5, ",");
[Link]();
String result = [Link](); // convert back to String
// StringBuilder is NOT thread-safe
// StringBuffer is thread-safe (but slower)
SECTION 8 — Quick Reference & Cheatsheet
30. OOP Pillars Summary
ENCAPSULATION → Private fields + public getters/setters
Protects internal state
ABSTRACTION → Abstract classes & interfaces
Hide complexity, show only what's needed
INHERITANCE → extends keyword
Child reuses parent code; 'IS-A' relationship
POLYMORPHISM → Override + upcasting
One reference, multiple forms; resolved at runtime
31. Thread Safety Techniques Summary
1. synchronized method/block → simple mutual exclusion
2. volatile keyword → visibility guarantee
3. AtomicInteger/Long/Reference → lock-free atomic ops
4. ReentrantLock → advanced locking (tryLock, timeout)
5. ConcurrentHashMap/CopyOnWriteArrayList → thread-safe collections
6. BlockingQueue → producer-consumer pattern
7. CountDownLatch/CyclicBarrier → thread coordination
Page 19
Java & Concurrent Programming — Complete Notes
8. ExecutorService → manage thread pools
32. Common Java Pitfalls
• Using == to compare Strings (use .equals() instead)
• Calling run() instead of start() — run() executes in current thread!
• Not closing resources — use try-with-resources
• ConcurrentModificationException — don't modify a list while iterating it
• Integer overflow — int maxes at 2,147,483,647; use long for big numbers
• Deadlock — always acquire locks in consistent order
• Not handling InterruptedException — always restore interrupt flag
• Using float/double for exact currency — use BigDecimal
• Mutable objects in Set/Map keys — can break lookup after mutation
— End of Notes —
Java & Concurrent Programming — CT074-3-2
Page 20