The advanced curriculum — generics and their wildcards, the exception hierarchy, Java 8 streams and lambdas, the design patterns Xperi adores, multithreading, and the final rapid-fire revision.
Begin Reading →The cornerstone of modern Java. Generics, type erasure, bounded types, and the PECS rule — questions that distinguish the prepared from the fortunate.
Generics Java 5 mein aaye — yeh compile-time type safety provide karte hain. Bina generics ke aap Object type ka collection bana sakte the, par runtime pe ClassCastException aata tha.
// BEFORE Generics (Java 1.4) List list = new ArrayList(); list.add("hello"); Integer n = (Integer) list.get(0); // ClassCastException at runtime // AFTER Generics (Java 5+) List<String> list = new ArrayList<>(); list.add("hello"); Integer n = (Integer) list.get(0); // COMPILE ERROR — type safety
Type Erasure ek process hai jismein compiler generic type information ko hata deta hai aur raw types / bridges mein convert kar deta hai. JVM ko generics ka pata hi nahi hota — yeh sirf compiler-level feature hai.
// Source code List<String> strings = new ArrayList<String>(); strings.add("hi"); String s = strings.get(0); // After type erasure (what JVM sees) List strings = new ArrayList(); strings.add("hi"); String s = (String) strings.get(0); // compiler inserts cast
new T(), new T[], instanceof T generic types ke saath allowed NAHI hain — kyunki runtime pe T ka existence nahi hota.<? extends T> vs <? super T> mein kya difference hai?PECS Rule — Producer Extends, Consumer Super.
| Wildcard | Meaning | Use when |
|---|---|---|
<? extends T> | Upper bound — T ya uska subtype | Sirf read kar rahe ho (Producer) |
<? super T> | Lower bound — T ya uska supertype | Sirf write kar rahe ho (Consumer) |
<?> | Unbounded — kuch bhi | Read + write dono nahi, sirf null daal sakte ho |
// PECS example public static void process(List<? extends Number> producer) { // PRODUCER — read only for (Number n : producer) System.out.println(n); } public static void fill(List<? super Integer> consumer) { // CONSUMER — write only consumer.add(10); consumer.add(20); }
extends = "mere paas sirf produce karke dene wale hain" (read), super = "mere paas dal ke khaane wale hain" (write).T, E, K, V, ? ka convention kya hai?| Symbol | Stands for | Used in |
|---|---|---|
E | Element | Collections — List<E>, Set<E> |
K | Key | Maps — Map<K, V> |
V | Value | Maps — Map<K, V> |
N | Number | Numeric types |
T | Type | Generic classes/methods |
S, U, V | 2nd, 3rd, 4th Type | Multi-type parameters |
? | Wildcard (unknown) | Read-only contexts |
new T[10] kyun illegal hai?Type erasure ke kaaran runtime pe generic type ka existence nahi hota. Arrays ke saath problem yeh hai ki arrays reified hain (runtime pe type check karte hain), jabki generics erased hain.
List<String>[] stringLists = new ArrayList<String>[10]; // COMPILE ERROR // Workaround: raw type ya wildcard use karo List[] stringLists = new ArrayList[10]; // OK but unchecked warning List<?>[] stringLists = new ArrayList<?>[10]; // OK
List<Object> ya List<?> use karo jab multiple types store karne ho.The exception hierarchy, checked versus unchecked, try-with-resources — the foundations of production-grade Java.
Java mein sab kuch Throwable se inherit hota hai, jismein 2 branches hain:
Throwable ├── Error (unchecked, JVM level — OutOfMemoryError, StackOverflowError) └── Exception ├── IOException (checked — file, network failures) ├── SQLException (checked) ├── ClassNotFoundException (checked) └── RuntimeException (unchecked) ├── NullPointerException ├── ArrayIndexOutOfBoundsException ├── ArithmeticException └── IllegalArgumentException
| Aspect | Checked | Unchecked |
|---|---|---|
| Extends | Exception (not RuntimeException) | RuntimeException |
| Compile check | YES — handle or declare | NO — optional |
| Cause | External resources (file, DB, network) | Programming bugs (NPE, bad index) |
| Recovery | Expected, can recover | Usually fatal logic errors |
Flow rules:
catch block run hota haifinally block HAMESHA execute hota hai — exception aaye ya na aaye (except System.exit() ya JVM crash)AutoCloseable resources automatically close hote hain// Old way FileReader fr = null; try { fr = new FileReader("file.txt"); // read file } catch (IOException e) { e.printStackTrace(); } finally { if (fr != null) try { fr.close(); } catch (IOException e) {} } // NEW way (Java 7+) — cleaner try (FileReader fr = new FileReader("file.txt")) { // read file — fr auto-closed } catch (IOException e) { e.printStackTrace(); }
final variable bhi try-with mein use kar sakte ho.throw vs throws mein kya difference hai?| Aspect | throw | throws |
|---|---|---|
| Type | Keyword used to actually throw an exception | Keyword used in method signature to declare |
| Usage | Inside method body | Method signature mein |
| Count | Single exception object throw karta hai | Multiple exception types declare kar sakta hai |
| Example | throw new IOException(); | void read() throws IOException |
public void withdraw(double amount) throws InsufficientFundsException { // DECLARE if (amount > balance) throw new InsufficientFundsException("Not enough money"); // ACTUALLY THROW balance -= amount; }
Jab business logic mein specific error represent karna ho. Best practice: checked extend karo agar caller ko recover karna hai, unchecked agar programming bug hai.
// Checked — caller ko handle karna chahiye public class InsufficientFundsException extends Exception { public InsufficientFundsException(String msg) { super(msg); } } // Unchecked — programming error public class InvalidAgeException extends RuntimeException { public InvalidAgeException(String msg) { super(msg); } }
Exception lagao aur past tense mein describe karo — UserNotFoundException, InvalidInputException.// Multi-catch (Java 7+) try { Class.<String>forName("com.foo.Bar").newInstance(); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { logger.error("Failed to load class", e); } // Multi-resource try-with-resources try (FileReader fr = new FileReader("in.txt"); FileWriter fw = new FileWriter("out.txt")) { fw.write(fr.readAllBytes()); } // both auto-closed in REVERSE order
AutoCloseable implement karna padta hai.Functional programming in Java — guaranteed in every coding round.
Stream ek sequence of elements hai jo functional operations support karta hai. Yeh data structure nahi hai — yeh sirf data pe operations perform karne ka tarika hai.
| Aspect | Collection | Stream |
|---|---|---|
| Purpose | Data store karna | Data pe computation karna |
| Modification | Add/remove elements | Immutable — original change nahi hota |
| Iteration | External (for/while) | Internal (stream khud iterate karta hai) |
| Lazy | No | Yes — terminal op tak kuch nahi hota |
| Reusable | Yes | No — ek baar consume hoke khatam |
Stream pipeline ke 3 steps hote hain:
list.stream(), Stream.of(...), Arrays.stream(...)filter(), map(), sorted(), distinct() — lazy, naya stream return karte haincollect(), forEach(), count(), reduce() — result produce karta hai, stream khatamList<String> names = Arrays.asList("Alice", "Bob", "Anna", "Charlie"); List<String> result = names.stream() // 1. Source .filter(n -> n.startsWith("A")) // 2a. Intermediate (lazy) .map(String::toUpperCase) // 2b. Intermediate (lazy) .sorted() // 2c. Intermediate (lazy) .collect(Collectors.toList()); // 3. Terminal (triggers execution) // Result: [ALICE, ANNA]
:: lambda ka shorthand hai. String::toUpperCase ≡ s -> s.toUpperCase()map() vs flatMap() mein kya difference hai?map() — har element ko 1:1 transform karta hai.
flatMap() — har element ko multiple elements mein "flatten" karta hai (1:N transformation).
List<List<Integer>> nested = Arrays.asList( Arrays.asList(1, 2), Arrays.asList(3, 4, 5), Arrays.asList(6) ); // map() — flat nahi karta List<Stream<Integer>> mapped = nested.stream().map(l -> l.stream()).collect(Collectors.toList()); // flatMap() — flatten karta hai List<Integer> flat = nested.stream() .flatMap(Collection::stream) .collect(Collectors.toList()); // Result: [1, 2, 3, 4, 5, 6]
map = "wrap", flatMap = "unwrap and concat". Optional ke saath flatMap bahut powerful hai.Lambda = anonymous function. Yeh Functional Interface (single abstract method wala) ka concise implementation hai.
// Anonymous class — old, verbose Runnable r1 = new Runnable() { public void run() { System.out.println("old"); } }; // Lambda — new, concise Runnable r2 = () -> System.out.println("new"); // With parameters Comparator<Integer> cmp = (a, b) -> a.compareTo(b); // Multi-line body Function<Integer, Integer> square = x -> { return x * x; };
Predicate<T> (T→boolean), Function<T,R> (T→R), Consumer<T> (T→void), Supplier<T> (()→T).Optional<T> ek container hai jo hold kar sakta hai ya nahi bhi kar sakta. Yeh null references ke alternative ke roop mein aaya Java 8 mein.
// Old way — null check required String name = user.getName(); if (name != null) System.out.println(name.toUpperCase()); // New way — Optional Optional<String> opt = user.getNameOptional(); String result = opt .map(String::toUpperCase) .orElse("DEFAULT"); // safe default // Common methods opt.isPresent() // true if has value opt.ifPresent(System.out::println) // run lambda only if present opt.orElseThrow(() -> new NotFoundException("missing")) // throw if empty
Singleton, Observer, Factory, Builder, Strategy — every interview expects 2-3 of these written from memory.
Singleton ensures ek class ka sirf ek instance ho. Use case: DB connection pool, Logger, Config manager.
// 1. Eager initialization (simple but wastes memory) public class EagerSingleton { private static final EagerSingleton INSTANCE = new EagerSingleton(); private EagerSingleton() {} public static EagerSingleton getInstance() { return INSTANCE; } } // 2. Bill Pugh Singleton (BEST — lazy + thread-safe, no synchronization) public class BillPughSingleton { private BillPughSingleton() {} private static class Helper { private static final BillPughSingleton INSTANCE = new BillPughSingleton(); } public static BillPughSingleton getInstance() { return Helper.INSTANCE; } } // 3. Enum Singleton (best against reflection + serialization) public enum EnumSingleton { INSTANCE; public void doWork() { /* ... */ } }
Factory pattern object creation ko encapsulate karta hai. Client ko sirf interface pata hota hai, actual class nahi.
interface Shape { void draw(); } class Circle implements Shape { public void draw() { System.out.println("○"); } } class Square implements Shape { public void draw() { System.out.println("□"); } } public class ShapeFactory { public static Shape create(String type) { return switch (type) { case "circle" -> new Circle(); case "square" -> new Square(); default -> throw new IllegalArgumentException("Unknown: " + type); }; } } // Client code Shape s = ShapeFactory.create("circle"); // no new keyword! s.draw();
Jab class mein bahut zyada optional fields hon aur telescoping constructors ugly ho jaayein, tab Builder use karo.
public class User { private final String name; // required private final String email; // required private final int age; // optional private final String phone; // optional private final String address; // optional private User(Builder b) { this.name = b.name; this.email = b.email; this.age = b.age; this.phone = b.phone; this.address = b.address; } public static class Builder { private final String name, email; private int age; private String phone, address; public Builder(String name, String email) { this.name = name; this.email = email; } public Builder age(int v) { this.age = v; return this; } public Builder phone(String v) { this.phone = v; return this; } public Builder address(String v) { this.address = v; return this; } public User build() { return new User(this); } } } // Usage User u = new User.Builder("Pritam", "p@x.com").age(25).phone("999").build();
Observer = publish-subscribe. Jab ek object (Subject) ki state change ho, sab registered listeners (Observers) automatically notify ho jaate hain.
interface Observer { void update(String msg); } class Subject { private final List<Observer> observers = new ArrayList<>(); public void subscribe(Observer o) { observers.add(o); } public void notifyAll(String msg) { observers.forEach(o -> o.update(msg)); } } // Real-world: YouTube channel Subject channel = new Subject(); channel.subscribe(msg -> System.out.println("Email: " + msg)); channel.subscribe(msg -> System.out.println("Push: " + msg)); channel.notifyAll("New video uploaded!"); // both notified
java.util.Observer (deprecated), PropertyChangeListener, aur Reactive Streams (RxJava, Project Reactor).Strategy pattern ek family of algorithms ko define karta hai, unhe encapsulate karta hai, aur runtime pe interchange karne deta hai.
interface PaymentStrategy { void pay(int amount); } class CreditCard implements PaymentStrategy { public void pay(int amount) { System.out.println("Paid " + amount + " via CC"); } } class UPI implements PaymentStrategy { public void pay(int amount) { System.out.println("Paid " + amount + " via UPI"); } } class ShoppingCart { private PaymentStrategy strategy; public void setStrategy(PaymentStrategy s) { this.strategy = s; } public void checkout(int amount) { strategy.pay(amount); } } // Runtime pe strategy change ShoppingCart cart = new ShoppingCart(); cart.setStrategy(new CreditCard()); cart.checkout(500); cart.setStrategy(new UPI()); cart.checkout(500);
Thread lifecycle, synchronization, deadlock prevention, volatile — must-know for production systems.
| Aspect | Process | Thread |
|---|---|---|
| Definition | Independent program in execution | Lightweight sub-unit of a process |
| Memory | Separate memory space | Shared memory with other threads |
| Communication | IPC (pipes, sockets) — slow | Direct shared variables — fast |
| Creation cost | High | Low |
| Crash impact | Isolated | Can crash whole process |
// 1. Extend Thread class class MyThread extends Thread { public void run() { System.out.println("Thread running"); } } new MyThread().start(); // 2. Implement Runnable (PREFERRED — Java doesn't support multiple inheritance) Runnable r = () -> System.out.println("Runnable running"); new Thread(r).start(); // Modern: ExecutorService (BEST for production) ExecutorService pool = Executors.newFixedThreadPool(4); pool.submit(() -> System.out.println("From pool")); pool.shutdown();
Thread avoid karo — ExecutorService use karo. Thread pool automatically manage hota hai, resource leaks se bachata hai.synchronized aur volatile mein kya difference hai?| Aspect | synchronized | volatile |
|---|---|---|
| Purpose | Mutual exclusion (atomicity) | Visibility guarantee |
| Scope | Method ya block | Only variable |
| Performance | Slower (lock acquire/release) | Lightweight |
| Compound ops | Safe (e.g. i++) | NOT safe for compound ops |
| Reentrancy | Yes (same thread can re-enter) | N/A |
private volatile boolean running = true; // visible to all threads public void run() { while (running) { /* work */ } } public void stop() { running = false; } // all threads see this
Deadlock = 2 ya zyada threads ek doosre ke lock release karne ka wait kar rahe hain. App hang ho jaata hai.
4 Coffman Conditions (sab true honi chahiye):
tryLock with timeout use karo, ya higher-level java.util.concurrent utilities (Semaphore, ReentrantLock) use karo.| Aspect | wait() | sleep() |
|---|---|---|
| Class | Object | Thread |
| Lock release? | Yes | No |
| Wake up | notify() / notifyAll() | After timeout |
| Where called | Inside synchronized block | Anywhere |
| Use case | Inter-thread communication | Pausing execution |
wait/notify avoid karo. java.util.concurrent package ka use karo: BlockingQueue, CountDownLatch, CyclicBarrier, CompletableFuture.Five quick-fire questions to test your readiness — answers crisp, examples precise.
== compares references (memory address) for objects, values for primitives. .equals() compares logical content (override karna padta hai).
String a = new String("hi"); String b = new String("hi"); a == b // false (different objects) a.equals(b) // true (same content)
| Class | Mutable | Thread-safe | Use when |
|---|---|---|---|
String | No (immutable) | Yes | Constant text, no change |
StringBuilder | Yes | No | Single-thread string manipulation (FASTEST) |
StringBuffer | Yes | Yes (synchronized) | Multi-thread string manipulation |
Default capacity = 16, Default load factor = 0.75. Rehash hota hai jab size > 16 × 0.75 = 12 ho jaaye.
new HashMap<>(expectedSize / 0.75 + 1) — rehash se bachne ke liye.| Aspect | Array | ArrayList |
|---|---|---|
| Size | Fixed at creation | Dynamic (auto-grow) |
| Type | Primitives + objects | Objects only (boxed primitives) |
| Features | None extra | add, remove, contains, iterator |
Immutability = object ki state creation ke baad change nahi hoti. Steps:
final declare karo (subclassing nahi hogi)private final banaopublic final class ImmutablePoint { private final int x, y; public ImmutablePoint(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public int getY() { return y; } }