Title: Java Programming — LearnCoachAssist

Q: What is the JVM?
A: The Java Virtual Machine (JVM) is an abstract computing machine that executes Java bytecode. It provides platform independence by translating bytecode into native machine instructions at runtime, enabling the "Write Once, Run Anywhere" principle.

Q: What is the difference between JDK, JRE, and JVM?
A: JDK (Java Development Kit) includes tools for developing Java programs.
JRE (Java Runtime Environment) provides libraries and JVM to run Java applications.
JVM (Java Virtual Machine) executes the bytecode. Relationship: JDK ⊃ JRE ⊃ JVM.

Q: What are the four pillars of OOP in Java?
A: - Encapsulation – bundling data and methods, restricting direct access
- Inheritance – a class derives from another class
- Polymorphism – one interface, many implementations
- Abstraction – hiding complex details, showing only essentials

Q: What is encapsulation in Java?
A: Encapsulation is the practice of keeping fields private and providing access through public getter and setter methods. This protects the internal state of an object and enforces data hiding.
Example: private int age; public int getAge() { return age; }

Q: What is inheritance in Java?
A: Inheritance allows a class to acquire properties and methods of another class using the extends keyword. Java supports single inheritance for classes.
Example: class Dog extends Animal { }

Q: What is polymorphism in Java?
A: Polymorphism lets objects take many forms.
Compile-time (method overloading): same method name, different parameters.
Runtime (method overriding): subclass provides its own implementation of a parent method. Resolved via dynamic method dispatch.

Q: What is abstraction in Java?
A: Abstraction hides implementation details and shows only functionality. Achieved through:
- Abstract classes – declared with abstract keyword, can have both abstract and concrete methods
- Interfaces – define a contract of methods a class must implement

Q: What is the difference between an abstract class and an interface?
A: Abstract class: can have constructors, instance variables, and both abstract and concrete methods. Use extends.
Interface: all methods are implicitly public abstract (before Java 8). Supports multiple inheritance. Use implements. Since Java 8, interfaces can have default and static methods.

Q: What are access modifiers in Java?
A: Java has four access modifiers:
- public – accessible from everywhere
- protected – accessible within the same package and subclasses
- default (no keyword) – accessible only within the same package
- private – accessible only within the declaring class

Q: What is the purpose of the static keyword?
A: The static keyword means the member belongs to the class rather than an instance.
- static variables are shared across all instances
- static methods can be called without creating an object
- static blocks run once when the class is loaded
Example: Math.sqrt(25)

Q: What is the final keyword used for?
A: final has three uses:
- Variable: value cannot be changed (constant)
- Method: cannot be overridden by subclasses
- Class: cannot be extended
Example: final int MAX = 100;

Q: What is a constructor in Java?
A: A constructor is a special method called when an object is created. It has the same name as the class and no return type.
Java provides a default no-arg constructor if none is defined.
Example: public MyClass(int x) { this.x = x; }

Q: What is method overloading?
A: Method overloading is defining multiple methods with the same name but different parameter lists (number, type, or order). It is a form of compile-time polymorphism.
Example:
int add(int a, int b)
double add(double a, double b)

Q: What is method overriding?
A: Method overriding occurs when a subclass provides a specific implementation of a method already defined in its superclass. The method signature must be identical. Annotate with @Override. It is a form of runtime polymorphism.

Q: What is the this keyword in Java?
A: this refers to the current object instance. Common uses:
- Distinguish instance variables from parameters: this.name = name;
- Call another constructor: this(args);
- Pass the current object as an argument

Q: What is the super keyword in Java?
A: super refers to the parent class. It is used to:
- Call the parent constructor: super(args);
- Access parent methods: super.methodName();
- Access parent fields hidden by child fields

Q: What are Java packages?
A: A package is a namespace that organizes classes and interfaces. Declared with package com.example.myapp; at the top of a file.
Benefits:
- Avoids naming conflicts
- Controls access with package-private visibility
- Provides modular code structure

Q: What is the difference between == and equals() in Java?
A: == compares reference equality (whether two variables point to the same object in memory).
.equals() compares value equality (logical content).
Example: new String("hi") == new String("hi") is false, but new String("hi").equals(new String("hi")) is true.

Q: Why is the String class immutable in Java?
A: String is immutable for several reasons:
- Security – strings used in class loading, networking, and file paths cannot be altered
- Thread safety – immutable objects are inherently thread-safe
- String Pool – the JVM caches strings for memory efficiency
- Hashing – hashCode can be cached since the value never changes

Q: What is the String Pool in Java?
A: The String Pool (or intern pool) is a special memory area in the heap where the JVM stores string literals. When you write String s = "hello";, the JVM checks the pool first and reuses existing objects, saving memory. Use intern() to add a string to the pool explicitly.

Q: What is the difference between String, StringBuilder, and StringBuffer?
A: - String – immutable, thread-safe, stored in String Pool
- StringBuilder – mutable, not thread-safe, faster for single-threaded string manipulation
- StringBuffer – mutable, thread-safe (synchronized methods), slower than StringBuilder
Use StringBuilder when performance matters in a single thread.

Q: What are Java generics?
A: Generics enable types (classes, interfaces, methods) to operate on parameterized types, providing compile-time type safety.
Example: List<String> names = new ArrayList<>();
Benefits: eliminates casting, catches type errors at compile time, and enables reusable code.

Q: What are bounded type parameters in generics?
A: Bounded types restrict the types that can be used as generic arguments.
Upper bound: <T extends Number> – T must be Number or a subclass.
Lower bound (wildcards): <? super Integer> – accepts Integer or any superclass.
Unbounded wildcard: <?> – accepts any type.

Q: What is type erasure in Java generics?
A: Type erasure is the process by which the Java compiler removes all generic type information at compile time. Generic types are replaced with their bounds or Object. This means List<String> and List<Integer> become the same List class at runtime, ensuring backward compatibility with pre-generics code.

Q: What is the Java Collections Framework?
A: The Collections Framework provides a unified architecture for storing and manipulating groups of objects. Key interfaces:
- List – ordered, allows duplicates (ArrayList, LinkedList)
- Set – no duplicates (HashSet, TreeSet)
- Map – key-value pairs (HashMap, TreeMap)
- Queue – FIFO ordering (PriorityQueue, LinkedList)

Q: What is the difference between ArrayList and LinkedList?
A: ArrayList: backed by a dynamic array. Fast O(1) random access via index. Slow O(n) insertions/deletions in the middle.
LinkedList: backed by a doubly-linked list. Fast O(1) insertions/deletions at head/tail. Slow O(n) random access. Also implements Deque.

Q: What is the difference between HashMap and TreeMap?
A: HashMap: stores key-value pairs in a hash table. O(1) average lookup. No ordering of keys. Allows one null key.
TreeMap: stores entries in a red-black tree. O(log n) lookup. Keys are sorted in natural order or by a custom Comparator. Does not allow null keys.

Q: What is the difference between HashSet and TreeSet?
A: HashSet: uses a hash table internally. O(1) add/remove/contains. No ordering guarantee. Allows one null.
TreeSet: uses a red-black tree. O(log n) operations. Elements are stored in sorted order. Does not allow null. Implements NavigableSet.

Q: How does HashMap work internally?
A: HashMap uses an array of buckets. Each key's hashCode() determines the bucket index. Collisions are handled with linked lists (or balanced trees in Java 8+ when a bucket exceeds 8 entries).
Key operations: put(key, value), get(key), remove(key). Load factor (default 0.75) triggers resizing.

Q: What is the Iterator interface?
A: The Iterator interface provides a way to traverse a collection sequentially.
Methods:
- hasNext() – returns true if more elements exist
- next() – returns the next element
- remove() – removes the last returned element
Use it to safely remove elements during iteration, unlike a for-each loop.

Q: What are Java exceptions?
A: An exception is an event that disrupts normal program flow. Java's exception hierarchy:
Throwable → Exception (checked) and Error (unchecked, serious).
RuntimeException extends Exception and is unchecked.
Handle with try-catch-finally blocks.

Q: What is the difference between checked and unchecked exceptions?
A: Checked exceptions must be declared in the method signature with throws or caught with try-catch. Example: IOException.
Unchecked exceptions extend RuntimeException and do not need explicit handling. Example: NullPointerException, ArrayIndexOutOfBoundsException.

Q: What is the try-with-resources statement?
A: Try-with-resources (Java 7+) automatically closes resources that implement AutoCloseable.
Example:
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
  String line = br.readLine();
}
Resources are closed in reverse order of declaration, even if an exception occurs.

Q: How do you create a custom exception in Java?
A: Create a class that extends Exception (checked) or RuntimeException (unchecked):
public class InsufficientFundsException extends Exception {
  public InsufficientFundsException(String message) {
    super(message);
  }
}
Throw it with: throw new InsufficientFundsException("Balance too low");

Q: What are lambda expressions in Java?
A: Lambda expressions (Java 8+) provide a concise way to implement functional interfaces (interfaces with a single abstract method).
Syntax: (parameters) -> expression
Example: List.sort((a, b) -> a.compareTo(b));
They enable functional programming and are heavily used with the Streams API.

Q: What is a functional interface?
A: A functional interface has exactly one abstract method and can be annotated with @FunctionalInterface.
Common built-in examples:
- Predicate<T> – takes T, returns boolean
- Function<T,R> – takes T, returns R
- Consumer<T> – takes T, returns void
- Supplier<T> – takes nothing, returns T

Q: What is the Stream API in Java?
A: The Stream API (Java 8+) processes collections of objects in a functional style.
Key characteristics: lazy evaluation, supports parallel processing, does not modify the source.
Example:
list.stream()
  .filter(s -> s.length() > 3)
  .map(String::toUpperCase)
  .collect(Collectors.toList());

Q: What are intermediate and terminal operations in Streams?
A: Intermediate operations return a new Stream and are lazy (not executed until a terminal operation is called):
filter(), map(), sorted(), distinct(), limit()
Terminal operations produce a result and trigger execution:
collect(), forEach(), reduce(), count(), findFirst()

Q: What is the Optional class in Java?
A: Optional<T> (Java 8+) is a container that may or may not hold a non-null value. It helps avoid NullPointerException.
Key methods:
- Optional.of(value) – wraps a non-null value
- Optional.empty() – empty Optional
- isPresent(), ifPresent()
- orElse(default), orElseThrow()

Q: What is multithreading in Java?
A: Multithreading allows concurrent execution of two or more threads. Create threads by:
- Extending Thread class and overriding run()
- Implementing Runnable interface
- Using ExecutorService (preferred)
Start a thread with thread.start(), not thread.run().

Q: What is the synchronized keyword?
A: synchronized ensures that only one thread can access a block of code or method at a time, preventing race conditions.
Usage:
synchronized void increment() { count++; }
Or as a block: synchronized(lockObject) { // critical section }
It acquires an intrinsic lock (monitor) on the object.

Q: What is the volatile keyword?
A: volatile ensures that a variable's value is always read from and written to main memory, not a thread's local cache. It guarantees visibility across threads but does not guarantee atomicity.
Example: private volatile boolean running = true;
Use for simple flags shared between threads.

Q: What is the ExecutorService in Java?
A: ExecutorService is a higher-level replacement for manually managing threads. It manages a thread pool and handles task submission.
Example:
ExecutorService executor = Executors.newFixedThreadPool(4);
executor.submit(() -> doWork());
executor.shutdown();
Types: FixedThreadPool, CachedThreadPool, SingleThreadExecutor.

Q: What are annotations in Java?
A: Annotations provide metadata about the code. They do not directly affect execution but can be processed at compile time or runtime.
Common annotations:
- @Override – method overrides a superclass method
- @Deprecated – marks code as obsolete
- @SuppressWarnings – suppresses compiler warnings
- @FunctionalInterface – validates single abstract method

Q: What is the Comparable interface?
A: Comparable<T> defines the natural ordering of objects by implementing compareTo(T o).
Returns: negative (less than), zero (equal), positive (greater than).
Example:
public class Student implements Comparable<Student> {
  public int compareTo(Student s) { return this.name.compareTo(s.name); }
}

Q: What is the Comparator interface?
A: Comparator<T> defines a custom ordering separate from the object's class.
Example: Comparator<Student> byAge = (a, b) -> Integer.compare(a.getAge(), b.getAge());
Useful when you need multiple sorting strategies or cannot modify the class. Chain comparators with .thenComparing().

Q: What are the main Java I/O stream classes?
A: Java I/O is organized into:
Byte streams: InputStream / OutputStream – for binary data.
Character streams: Reader / Writer – for text data.
Common classes:
- FileInputStream / FileOutputStream
- BufferedReader / BufferedWriter
- PrintWriter
Always close streams or use try-with-resources.

Q: What is the difference between an interface default method and an abstract method?
A: A default method (Java 8+) in an interface has a body and provides a default implementation: default void greet() { System.out.println("Hello"); }
An abstract method has no body and must be implemented by the class. Default methods allow adding new methods to interfaces without breaking existing implementations.

Q: What is garbage collection in Java?
A: Garbage Collection (GC) is the JVM's automatic process of reclaiming memory occupied by objects that are no longer reachable.
Key points:
- You cannot force GC; System.gc() is only a suggestion
- Common collectors: G1, ZGC, Parallel GC
- Objects become eligible when no live references point to them

Q: What is the difference between Runnable and Callable?
A: Runnable: has void run() method. Cannot return a result or throw checked exceptions.
Callable<V>: has V call() method. Can return a value and throw exceptions.
Use Callable with ExecutorService.submit() which returns a Future<V> to retrieve the result asynchronously.

Q: What is the lifecycle of a Java object on the heap?
A: A Java object goes through these stages:
1. Created via new (memory allocated in Eden).
2. In use with at least one live reference.
3. Unreachable when no reference points to it (GC-eligible).
4. Finalized (only if it overrides finalize(), called once before collection — deprecated in Java 9+).
5. Collected by the garbage collector and its memory reclaimed.

Q: What are the eight primitive data types in Java?
A: Java has 8 primitives:
byte (8-bit), short (16-bit), int (32-bit), long (64-bit), float (32-bit), double (64-bit), char (16-bit Unicode), boolean (1-bit).
Primitives are stored on the stack (when local) and have no methods or identity. They are not objects; use wrapper classes (e.g. Integer) when object behavior is required.

Q: What is autoboxing and unboxing in Java?
A: Autoboxing is the automatic conversion of a primitive to its wrapper object: Integer i = 42;
Unboxing is the reverse: int n = i;
The compiler inserts Integer.valueOf() and intValue() calls. Caveat: unboxing a null wrapper throws NullPointerException. For values in the IntegerCache range (-128..127) boxing returns the same cached instance.

Q: What is the equals()/hashCode() contract in Java?
A: The contract requires:
1. Reflexive: x.equals(x) is true.
2. Symmetric: x.equals(y) iff y.equals(x).
3. Transitive: if x.equals(y) and y.equals(z), then x.equals(z).
4. Consistent: repeated calls return the same value until state changes.
5. Non-null: x.equals(null) is false.
Critical: if equals() returns true, hashCode() MUST be equal. Violating this breaks HashMap, HashSet, and HashTable.

Q: What is the contract between compareTo() and equals()?
A: The contract for Comparable.compareTo() recommends (not requires) that (x.compareTo(y) == 0) == x.equals(y). If violated, sorted sets/maps behave inconsistently: a TreeSet will not accept "equal" objects that are .equals() true, and a BigDecimal-like "different scale, equal value" case can produce duplicates in a TreeSet while HashSet deduplicates them.

Q: What is the difference between deep copy and shallow copy?
A: A shallow copy duplicates the top-level object but shares references to nested mutable objects: modifying the inner list of the copy also affects the original.
A deep copy recursively duplicates all referenced objects, so the copy and the original share nothing.
Java's Object.clone() is shallow by default; deep copy usually requires a copy constructor, serialization round-trip, or manual recursion.

Q: What is the Marker interface pattern in Java?
A: A marker interface is an empty interface (no methods) used to tag classes with metadata the runtime can check via instanceof. Classic examples: java.io.Serializable, java.lang.Cloneable, java.util.RandomAccess.
Since Java 5, the same role is often served by marker annotations (e.g. @Deprecated, @Override) processed via reflection.

Q: What is the Singleton design pattern and how to implement it thread-safely?
A: Singleton ensures a class has exactly one instance and provides a global access point.
Thread-safe lazy version with double-checked locking:
public class Singleton {
  private static volatile Singleton instance;
  private Singleton() {}
  public static Singleton getInstance() {
    if (instance == null) {
      synchronized (Singleton.class) {
        if (instance == null) instance = new Singleton();
      }
    }
    return instance;
  }
}
Alternatives: enum singleton (best — serialization-safe and reflection-safe) or eager init.

Q: What is the Factory Method design pattern in Java?
A: Factory Method defines an interface for creating objects but lets subclasses decide which class to instantiate.
Example shape:
interface AnimalFactory { Animal create(); }
class DogFactory implements AnimalFactory { public Animal create() { return new Dog(); } }
Benefits: decouples client code from concrete classes, centralizes construction logic, easier to extend with new product types without modifying existing code.

Q: What is the Builder pattern and when is it useful in Java?
A: Builder separates the construction of a complex object from its representation, allowing step-by-step configuration. Useful when an object has many optional parameters or telescoping constructors become unreadable.
Example: new User.Builder("Alice").age(30).email("a@b.com").admin(true).build();
Lombok's @Builder and Java 16+ records reduce boilerplate. Effective Java Item 2 strongly recommends builders over constructors for many-parameter classes.

Q: What is the Dependency Injection principle in Java?
A: Dependency Injection (DI) is a technique where an object's dependencies are provided externally rather than the object creating them itself.
Types:
• Constructor injection (preferred — promotes immutability and testability).
• Setter injection.
• Field injection (avoid in production — hard to test).
Frameworks: Spring, Guice, CDI. DI enables loose coupling, easier mocking in unit tests, and the Dependency Inversion SOLID principle.

Q: What are the SOLID principles in Java OOP?
A: SOLID = five design principles:
• Single Responsibility: a class has one reason to change.
• Open/Closed: open for extension, closed for modification.
• Liskov Substitution: subtypes must be substitutable for their base types.
• Interface Segregation: prefer many small interfaces over one fat interface.
• Dependency Inversion: depend on abstractions, not concretions.
They guide maintainable, testable object-oriented design.

Q: What is the Liskov Substitution Principle (LSP) and a Java violation example?
A: LSP states that objects of a superclass must be replaceable with objects of a subclass without breaking behavior.
Classic violation: Rectangle superclass with setWidth/setHeight; Square subclass overrides both to keep sides equal, so a method expecting a Rectangle that calls both setters gets an unexpected area. Fix: prefer composition, or design immutable value types, or split roles into separate abstractions.

Q: What is composition over inheritance in Java?
A: Composition over inheritance favors "has-a" relationships over "is-a" by embedding an instance of another class and delegating to it.
Benefits: avoids tight coupling to a parent's fragile public API, prevents the fragile base class problem, and lets you swap implementations at runtime via interfaces.
Rule of thumb: extend only when the subclass truly is a specialization and you control both classes.

Q: What is the difference between an interface and an abstract class in Java 8+?
A: Both can have default and static methods since Java 8. Differences:
• A class can implements many interfaces but only extends one class.
• Interfaces have no instance state (only static final constants) — abstract classes can have instance fields.
• Interfaces cannot have constructors; abstract classes can.
• Interfaces are typically used for capabilities (e.g. Serializable); abstract classes for shared base state and behavior.

Q: What is a functional interface with a default method — does it remain functional?
A: Yes. A functional interface must have exactly one abstract method (SAM). Default and static methods, and methods inherited from Object, do not count toward the SAM count.
Example: Predicate<T> has one abstract method test(T) plus a default and(...) and a static isEqual(...), yet it is still a functional interface.

Q: What is method reference in Java 8 and what are its four kinds?
A: A method reference is a shorthand for a lambda that calls an existing method. Four kinds:
1. Static: Math::sqrt → x -> Math.sqrt(x).
2. Instance on a particular object: System.out::println → x -> System.out.println(x).
3. Instance on an arbitrary object of a given type: String::length → s -> s.length().
4. Constructor: ArrayList::new → () -> new ArrayList().

Q: What is the difference between findFirst() and findAny() in Java Streams?
A: findFirst() returns an Optional describing the first element in encounter order; useful when order matters.
findAny() returns any element of the stream — the docs allow it to return any. On a sequential stream it usually returns the first, but on a parallel stream it can return whichever element the runtime finds first, which is faster and non-deterministic.
Use findFirst() when you need determinism; findAny() when you just need "some" element and want parallelism.

Q: What is the difference between map() and flatMap() in Java Streams?
A: map() applies a Function<T,R> to each element and returns a stream of R — one-to-one transformation.
flatMap() applies a Function<T, Stream<R>> and flattens the resulting streams into one — useful for one-to-many (e.g. splitting strings into words, optional unwrapping, nested collections).
Example: words.stream().flatMap(line -> Arrays.stream(line.split(" "))).

Q: What is the difference between Collection.stream() and Collection.parallelStream()?
A: stream() processes elements sequentially on the calling thread; results stay in encounter order and behavior is deterministic.
parallelStream() splits the work across multiple threads using the common ForkJoinPool; performance gains depend on data size, CPU cores, and the cost per element. Side effects in operations become unsafe, and ordering is not guaranteed for unordered sources. Use parallel only for CPU-bound work on large datasets with stateless operations.

Q: What is a Spliterator in Java?
A: Spliterator ("split iterator") is the internal traversal mechanism behind Streams. It can partition a source into sub-sources, supports parallel processing, and reports characteristics via characteristics() (e.g. ORDERED, DISTINCT, SORTED, SIZED, IMMUTABLE).
Most developers never implement one directly, but understanding it explains why some streams parallelize well and others do not.

Q: What is the Collectors.toMap() gotcha when values are duplicate?
A: If the stream produces duplicate keys, Collectors.toMap(keyMapper, valueMapper) throws IllegalStateException: Duplicate key.
Fix: provide a merge function:
stream().collect(Collectors.toMap(
  Person::getId,
  Person::getName,
  (a, b) -> a + "," + b));
For order preservation use Collectors.toMap(..., LinkedHashMap::new); to allow null values use Collectors.toUnmodifiableMap variants carefully — some throw on null keys/values.

Q: What is the difference between peek() and forEach() in Streams?
A: peek(Consumer) is an intermediate operation intended for debugging; it executes only when a downstream terminal operation runs, and its behavior is undefined in parallel streams if the action mutates shared state.
forEach(Consumer) is a terminal operation that processes every element and is guaranteed to run.
Rule: do not use peek() for side effects in production code; use it only in debug logs. Use forEach() for side effects.

Q: What is the Java Memory Model (JMM)?
A: The Java Memory Model (JSR-133) defines the rules by which threads interact through memory. It specifies:
• Atomicity of reads/writes (long/double are not atomic without volatile).
• Visibility — when writes by one thread are seen by another (volatile, synchronized, final for properly constructed objects).
• Ordering — happens-before relationships that prevent reordering breaking concurrent code.
Without JMM, compiler/CPU reordering can cause infinite loops, stale values, and lost updates even in apparently correct code.

Q: What is the happens-before relationship in Java?
A: Happens-before is the JMM's core ordering guarantee: if action A happens-before action B, then the result of A is visible to B and A appears to execute before B.
Built-in sources of happens-before:
• A thread's actions happen-before its Thread.join() returns.
• A write to a volatile happens-before any subsequent read of the same variable.
• An unlock on a monitor happens-before any subsequent lock of the same monitor.
• Actions in a thread happen-before any other thread that returns from Thread.start() on that thread.

Q: What is the difference between intrinsic lock and explicit Lock in Java concurrency?
A: Intrinsic lock (every Java object has one) is acquired/released with the synchronized keyword — basic, reentrant, automatically released on exception.
Explicit lock (java.util.concurrent.locks.Lock via ReentrantLock) offers: tryLock(), timed lock acquisition, multiple Condition variables, and fair-vs-unfair policies. ReentrantLock must be released in a finally block.
Prefer synchronized unless you need its advanced features.

Q: What is ReentrantReadWriteLock and when should it be used?
A: ReentrantReadWriteLock holds two locks: a read lock (shared) and a write lock (exclusive). Multiple readers can hold the read lock simultaneously, but a writer excludes everyone.
Use it when reads significantly outnumber writes (e.g. an in-memory cache). The readLock() and writeLock() methods return Lock instances used in try/finally.
Caveat: writer starvation is possible under heavy read load; consider StampedLock for optimistic reads.

Q: What is StampedLock in Java 8?
A: StampedLock (in java.util.concurrent.locks) supports three modes:
• Write lock — exclusive.
• Read lock — shared.
• Optimistic read — a non-blocking stamp; later validate the stamp with validate(stamp) and re-read if invalid.
Optimistic reads are extremely fast for read-mostly data because they avoid CAS. StampedLock is not reentrant and has no notion of ownership; the same thread reentering a write lock it already holds can deadlock.

Q: What is a CountDownLatch in Java?
A: CountDownLatch lets one or more threads wait until a set of operations performed in other threads completes. It is initialized with a count; each countDown() decrements it; await() blocks until the count reaches zero (or times out).
Use it to wait for N services to initialize, or for N tasks to finish before aggregating. The latch is single-use; once the count hits zero it cannot be reset — for cyclic reuse use CyclicBarrier or Phaser.

Q: What is the difference between CountDownLatch, CyclicBarrier, and Phaser?
A: • CountDownLatch: N threads count down, 1+ threads wait; single use.
• CyclicBarrier: N threads wait for each other at a barrier, then all proceed; reusable; supports a barrier action.
• Phaser: a flexible phased barrier with dynamic party count; parties register/deregister across phases; useful for staged parallel algorithms where participants come and go.
Choose the simplest that fits: CountDownLatch for one-shot coordination, CyclicBarrier for fixed-size teams, Phaser for evolving groups.

Q: What is BlockingQueue and what are common implementations?
A: BlockingQueue is a thread-safe queue whose put()/take() block when the queue is full/empty. It is the standard building block for the producer-consumer pattern.
Common implementations:
• ArrayBlockingQueue — bounded, array-backed.
• LinkedBlockingQueue — optionally bounded, linked nodes.
• PriorityBlockingQueue — unbounded, ordered.
• SynchronousQueue — zero capacity, hands off directly between threads.
• DelayQueue — elements become available after a delay.

Q: What is a Semaphore in Java concurrency?
A: Semaphore maintains a set of permits. acquire() blocks until a permit is available, release() returns one. It is useful for resource pooling (e.g. limiting concurrent connections to 10) or for mutual exclusion (initialized with 1 permit, similar to a lock).
Unlike ReentrantLock, a semaphore does not have ownership — one thread can release a permit acquired by another. Available constructors: new Semaphore(permits, true) enables fair ordering.

Q: What is the Producer-Consumer pattern in Java and how is it typically implemented?
A: Producer-consumer decouples threads that produce items from threads that consume them via a shared bounded queue. The producer blocks when the queue is full; the consumer blocks when empty.
Canonical Java implementation:
BlockingQueue<Task> q = new ArrayBlockingQueue<>(100);
// producer: q.put(task);
// consumer: Task t = q.take();
This pattern naturally provides backpressure and is the basis for Executor work queues.

Q: What is the Fork/Join framework in Java?
A: Fork/Join (Java 7+) is a framework for parallelizing divide-and-conquer tasks. It uses a work-stealing ForkJoinPool: idle threads steal tasks from busy threads' deques, balancing load.
Tasks extend RecursiveTask<V> (returns a result) or RecursiveAction (no result) and implement compute(), calling fork() on subtasks and join() to collect results. parallelStream() uses the common pool by default; configure parallelism with System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "N").

Q: What is CompletableFuture in Java 8?
A: CompletableFuture<T> implements Future<T> and CompletionStage<T>, supporting composable, asynchronous pipelines.
Key methods:
• supplyAsync(() -> value) / runAsync(runnable).
• thenApply(), thenAccept(), thenRun() — chain transformations.
• thenCompose() — flatten a nested future (like flatMap).
• thenCombine() — combine two independent futures.
• exceptionally() / handle() — recover from errors.
• allOf() / anyOf() — aggregate multiple futures.
By default uses the common ForkJoinPool; pass a custom Executor for control.

Q: What is the difference between thenApply() and thenCompose() in CompletableFuture?
A: thenApply() takes a Function<T, U> and returns CompletableFuture<U> — transforms a value when the future completes (analogous to map()).
thenCompose() takes a Function<T, CompletionStage<U>> and flattens the result, avoiding CompletableFuture<CompletableFuture<U>> (analogous to flatMap()).
Use thenCompose() whenever the next step itself returns a future (chaining async calls). Use thenApply() for synchronous value transformations.

Q: What is a ConcurrentHashMap and how does it differ from Hashtable?
A: ConcurrentHashMap is a thread-safe map with fine-grained locking / lock-free reads (Java 8+ uses CAS + synchronized per bucket, no global lock). It allows concurrent reads and a high degree of concurrent writes, and weakly consistent iterators that do not throw ConcurrentModificationException.
Hashtable locks the entire map on every operation → poor scalability under contention. ConcurrentHashMap forbids null keys and values; Hashtable also forbids nulls. Prefer ConcurrentHashMap for concurrent maps.

Q: What is the atomicity gap on long and double in Java?
A: Writes and reads of long and double (64-bit) are not guaranteed atomic by the JMM without a modifier — they can be split into two 32-bit operations, allowing a thread to read a torn value.
To guarantee atomicity, declare them volatile or access them under a synchronized block. int, short, byte, float, and references are always atomic.
For compound operations (e.g. counter++) even volatile is insufficient — use AtomicInteger / AtomicLong or proper locking.

Q: What is the AtomicInteger / AtomicLong in Java?
A: Classes in java.util.concurrent.atomic providing lock-free, thread-safe operations on single variables using low-level CAS (compare-and-swap) CPU instructions.
Key methods: incrementAndGet(), getAndIncrement(), compareAndSet(expected, new), updateAndGet(), and Java 8+ accumulateAndGet() / getAndAccumulate().
Use them for counters and flags; they outperform synchronized for low-to-moderate contention. Combine with AtomicReference for non-blocking stacks and queues.

Q: What is the difference between wait() / notify() and the higher-level concurrency utilities?
A: wait()/notify() are low-level primitives on every object's monitor — they require the calling thread to own the object's monitor, are easy to misuse (lost wakeups, illegal monitor state), and offer no fairness.
Higher-level utilities in java.util.concurrent — BlockingQueue, CountDownLatch, CyclicBarrier, Semaphore, Phaser, ConcurrentMap, CompletableFuture — are safer, clearer, and more performant.
Rule of thumb: avoid wait()/notify() in new code; use the higher-level utilities instead.

Q: What is the difference between the Executor and ExecutorService interfaces?
A: Executor is a single-method interface: void execute(Runnable command) — fire-and-forget task submission.
ExecutorService extends Executor with lifecycle management (shutdown(), awaitTermination()), task results via submit() returning Future, batch invocation (invokeAll(), invokeAny()), and ScheduledExecutorService for delayed/periodic tasks.
Most production code uses ExecutorService, created via the Executors factory or directly via new ThreadPoolExecutor(...).

Q: What is ThreadPoolExecutor's key configuration in Java?
A: ThreadPoolExecutor is configured with 7 core parameters:
• corePoolSize — minimum threads kept alive.
• maximumPoolSize — upper limit.
• keepAliveTime / unit — idle time before surplus threads die.
• workQueue — holds tasks waiting for a thread.
• threadFactory — creates threads (set names!).
• handler — RejectedExecutionHandler for when the pool and queue are full (e.g. AbortPolicy, CallerRunsPolicy).
Prefer ThreadPoolExecutor over the Executors shortcuts so you can name threads and choose the queue safely.

Q: What is the ExecutorService shutdown protocol?
A: Always:
1. executor.shutdown(); — stop accepting new tasks; finish in-flight ones.
2. executor.awaitTermination(timeout, unit); — wait up to the timeout.
3. executor.shutdownNow(); — interrupt running tasks and return queued ones if not done.
4. executor.awaitTermination(...); again to confirm.
Skipping shutdown() prevents the JVM from exiting (non-daemon threads keep the process alive) and leaks resources.

Q: What is the difference between Callable, Supplier, and Runnable in functional terms?
A: • Runnable — no args, no return, can throw any Exception. void run().
• Supplier<T> — no args, returns T, no checked exceptions. T get().
• Callable<V> — no args, returns V, can throw checked Exception. V call().
Use Runnable for fire-and-forget tasks; Supplier for lazy value generation; Callable for asynchronous tasks that return a result or throw checked exceptions.

Q: What is enum in Java and what advanced features does it support?
A: A Java enum is a special class representing a fixed set of constants. It can have:
• Fields and a constructor (always private).
• Methods (including abstract ones — each constant can override).
• Implement interfaces.
• Per-constant behavior via abstract methods.
Example: enum Operation { ADD { int apply(int a,int b){return a+b;} }, SUB { int apply(int a,int b){return a-b;} }; abstract int apply(int,int); }
Enums are implicitly final, extend java.lang.Enum, and are safely usable in switch statements and EnumSet/EnumMap.

Q: What is EnumSet and EnumMap and why are they efficient?
A: EnumSet is a Set optimized for enum keys, backed by a single long bitmask (or multiple for large enums) — O(1) add/remove/contains with tiny memory footprint.
EnumMap is a Map optimized for enum keys, backed by an array indexed by ordinal.
Both are far faster than HashSet/HashMap for enum keys. They are not thread-safe; wrap with Collections.synchronizedSet or Set.of(...) for immutability. Use them whenever keys are enums.

Q: What is a Java record (since Java 16)?
A: A record is a compact, immutable data class. The compiler generates the constructor, accessors (name(), not getName()), equals(), hashCode(), and toString() from its components.
Example: public record Point(int x, int y) {}
Records cannot extend other classes (implicitly extend Record), but can implement interfaces. They are ideal for DTOs, value objects, and tuples. Use compact constructors for validation and additional explicit methods/components as needed.

Q: What is a sealed class in Java (since Java 17)?
A: A sealed class restricts which other classes may extend it. The compiler enforces the closed hierarchy, enabling exhaustive pattern matching in switch.
Example:
public sealed class Shape permits Circle, Square, Triangle { }
public final class Circle extends Shape { }
public final class Square extends Shape { }
public non-sealed class Triangle extends Shape { }
Permitted subclasses must be in the same module/package and declared final, sealed, or non-sealed. Replaces ad-hoc "package-private" or "check by string" hierarchies.

Q: What is the var keyword (local variable type inference) in Java 10+?
A: var lets the compiler infer the type of a local variable from its initializer: var list = new ArrayList<String>();
Restrictions:
• Only for local variables (with initializer), for loop indices, and lambda parameters (Java 11+).
• Not allowed for fields, method parameters, or return types.
• Type is fixed at compile time; reassignment must be to compatible types.
• Avoid var when the right-hand side is non-obvious (e.g. var x = method();) — readability suffers.

Q: What is a text block in Java 15+?
A: A text block is a multi-line string literal delimited by """:
String json = """
  {
    "name": "Alice",
    "age": 30
  }
  """;
Indentation and incidental whitespace are normalized. String.stripIndent() / translateEscapes() are also available on the literal. Text blocks don't contain \r; embedded \ at line end joins lines.

Q: What is the switch expression feature in Java 14+?
A: Switch expressions use arrow syntax and return a value:
String label = switch (day) {
  case MONDAY, FRIDAY, SUNDAY -> "fun";
  case TUESDAY -> "meh";
  default -> "other";
};
Exhaustiveness is checked for enum switches — if all cases are covered, no default is required. Combined with sealed types, the compiler can prove all cases handled. Replaces verbose break;-laden switch statements.

Q: What is pattern matching for instanceof in Java 16+?
A: You can bind the matched object to a variable directly:
if (obj instanceof String s) {
  System.out.println(s.length());
}
The variable s is in scope only where the type is known to be String. Eliminates the traditional cast + temporary variable pattern. Combined with switch patterns (Java 21+), it enables exhaustive type-based dispatch on sealed hierarchies.

Q: What is the module system (JPMS) introduced in Java 9?
A: The Java Platform Module System (Project Jigsaw) groups packages into modules declared in module-info.java:
module com.example.app {
  requires java.sql;
  exports com.example.api;
  opens com.example.model to com.fasterxml.jackson;
}
requires = dependency; exports = public API; opens = reflective access for libraries like Spring/JPA. requires transitive re-exports a dependency. Modules enable reliable configuration, strong encapsulation, and a smaller custom runtime image via jlink.

Q: What is the difference between exports and opens in JPMS?
A: exports makes a package's public types and members accessible at compile and run time to other modules — the standard public API.
opens allows reflective access to all types in the package (including private members) at run time — needed by frameworks that use reflection (e.g. Hibernate, Jackson, Spring).
Open a package narrowly: opens com.example.model to com.fasterxml.jackson; avoids opening it to everyone. opens com.example.model; opens it to all modules (still restricted to compile time by not exporting).

Q: What is ServiceLoader in Java?
A: ServiceLoader implements the Service Provider Interface (SPI) pattern using META-INF/services/ files or module provides ... with ... directives. It lazily discovers and instantiates implementations of an interface at runtime.
Example:
ServiceLoader<Driver> sl = ServiceLoader.load(Driver.class);
for (Driver d : sl) d.connect(...);
JDBC, LoggerFinder, ImageIO, and many Java SE subsystems use ServiceLoader for pluggable implementations.

Q: What is reflection in Java?
A: Reflection lets a program inspect and manipulate classes, fields, methods, and constructors at run time via java.lang.reflect.
Capabilities:
• Load classes by name: Class.forName("com.x.Y").
• Instantiate: ctor.newInstance().
• Read/write fields (including private): field.set(obj, value).
• Invoke methods: method.invoke(target, args).
Drawbacks: slower, breaks encapsulation, fragile across refactors, security restrictions under module system. Use frameworks like Spring/Hibernate, but prefer compile-time solutions (generics, sealed types) when possible.

Q: What are Class objects and how are they obtained?
A: Every loaded class has a Class<?> object. Obtained via:
• String.class — compile-time literal.
• obj.getClass() — runtime instance.
• Class.forName("com.x.Y") — fully qualified name (throws ClassNotFoundException).
• ClassLoader.loadClass("...").
The Class object exposes metadata: name, modifiers, fields, constructors, methods, annotations, generic supertype, and the classloader. It's the entry point for reflection and dynamic proxy creation.

Q: What is a dynamic proxy in Java?
A: A dynamic proxy is an instance of a runtime-generated class that implements one or more interfaces. Created with Proxy.newProxyInstance(classLoader, interfaces, InvocationHandler); every method call is dispatched to InvocationHandler.invoke().
Used by: java.lang.reflect.Proxy, Spring AOP, Mockito, RPC stubs, and JDK module adapters.
Limitation: only interfaces can be proxied. For class proxies without interfaces, use ByteBuddy, CGLIB, or JDK 21+ MethodHandles.Lookup.defineHiddenClass.

Q: What are the three built-in classloaders in Java?
A: The JVM uses a parent-delegation hierarchy:
1. Bootstrap class loader — loads core JDK classes (rt.jar / jrt-fs); implemented in native code; returns null as parent.
2. Platform (extension) class loader — loads Java SE platform extensions.
3. Application (system) class loader — loads classes from the classpath.
Each loader first delegates to its parent before trying to load itself, preventing custom classes from spoofing core types. Custom classloaders are common in app servers, OSGi, and plugin systems.

Q: What is the parent-delegation model and how can it be broken?
A: In the parent-delegation model, a classloader asks its parent to load a class first; only if the parent fails does it try itself. This prevents the application classloader from overriding java.lang.Object, for example.
It can be broken (overridden loadClass()) but doing so is dangerous and discouraged. Tomcat historically used a child-first (servlet-first) delegation for hot-reload; OSGi uses a complex graph with multiple parents.

Q: What is the bytecode and how does the JVM execute it?
A: Bytecode is the platform-neutral instruction set stored in .class files, produced by javac (or other front-ends like Kotlin, Scala, Groovy). The JVM interprets it and uses the JIT (Just-In-Time) compiler (C1/C2 in HotSpot, Graal in newer versions) to compile hot methods into native machine code for performance.
Common bytecode instructions: aload, astore, invokevirtual, invokestatic, invokeinterface, new, dup. Inspect with javap -c -p MyClass.

Q: What is the difference between interpreted, JIT-compiled, and AOT-compiled Java?
A: Interpreted — the JVM walks bytecode one instruction at a time; fast to start, slow steady-state.
JIT-compiled (C1/C2/Graal) — the HotSpot compiler profiles hot methods and compiles them to native code; slow startup, fast steady-state. Tiered compilation blends both.
AOT (Ahead-of-Time) (GraalVM Native Image) — compiles to a native binary before run time; near-instant startup, low memory, slower peak CPU, less reflection friendly.
Modern JVMs use a mix: C1 + C2 tiered compilation, with AOT via Graal for cloud/serverless.

Q: What is the structure of a Java .class file?
A: A .class file is a binary stream with this layout:
1. Magic number 0xCAFEBABE.
2. Minor/Major version.
3. Constant pool — UTF-8 literals, class/method/field references, names, descriptors.
4. Access flags (public, final, abstract…).
5. This class and super class.
6. Interfaces, fields, methods, attributes (code, line numbers, generics signatures, annotations).
Inspect with javap -v Class; manipulate with ASM, ByteBuddy, or Javassist.

Q: What is the difference between the stack and heap memory in the JVM?
A: Stack — per-thread LIFO of stack frames for active method calls. Stores local variables (primitives + references), operand stack, return address. Allocation is fast and automatic; freed on method return. Size: -Xss.
Heap — shared region for all objects and class metadata. Garbage-collected, sized via -Xms/-Xmx. Generational: Eden + Survivor + Old (Tenured). Large arrays or objects may go directly to Old.

Q: What are the JVM memory regions beyond heap and stack?
A: HotSpot also uses:
• Metaspace (Java 8+) — class metadata, method data, constant pool; native memory, sized by -XX:MaxMetaspaceSize.
• Code cache — JIT-compiled native code.
• Direct memory — off-heap, used by NIO ByteBuffer.allocateDirect and Netty; sized by -XX:MaxDirectMemorySize.
• Thread stacks (per-thread).
• Compressed oops — 32-bit object references in 64-bit JVMs (heap < 32 GB by default).

Q: What is the difference between the G1, ZGC, and Parallel garbage collectors?
A: • Parallel GC — throughput-oriented, stop-the-world, full GC pauses; good for batch workloads.
• G1 GC (default since Java 9) — regional, mostly-concurrent, balances throughput and latency; target -XX:MaxGCPauseMillis.
• ZGC (Java 11+) — sub-millisecond pauses regardless of heap size, concurrent; great for very large heaps (multi-GB to TB) and latency-sensitive services.
• Shenandoah — similar to ZGC, low-pause concurrent collector.
Pick by SLA: ZGC/Shenandoah for <10 ms pauses, G1 for balanced, Parallel for batch.

Q: What is the Object.finalize() method and is it still recommended?
A: Object.finalize() is called by the GC at most once on an object before it is reclaimed, but the timing is not guaranteed and in practice many objects may never be finalized.
Drawbacks: unpredictable, can resurrect the object by assigning a new reference, slow, and can cause GC pauses. Deprecated for removal since Java 9.
Use try-with-resources + AutoCloseable for deterministic cleanup, or java.lang.ref.Cleaner (Java 9+) for off-heap resource cleanup.

Q: What is the Cleaner API in Java 9+?
A: Cleaner (in java.lang.ref) provides a more predictable, thread-safe alternative to finalize() for cleaning up off-heap resources (e.g. direct buffers, native handles).
Example:
Cleaner cleaner = Cleaner.create();
cleaner.register(obj, new CleaningAction());
When obj becomes unreachable, a daemon thread runs the registered Runnable action. The action must not reference the original object (avoid retaining it).

Q: What are the four types of references in Java?
A: Strong — normal Object o = new Object(); prevents GC as long as reachable.
Soft — SoftReference; cleared by GC only when memory is needed (heap-pressure sensitive). Used for memory-sensitive caches.
Weak — WeakReference; cleared at the next GC cycle. Used by WeakHashMap to auto-evict entries when keys are no longer strongly reachable.
Phantom — PhantomReference; enqueued after the object is finalized, before its memory is reclaimed. Used to schedule post-mortem cleanup actions.
Only soft and weak are typically used directly; phantoms are advanced.

Q: What is WeakHashMap and when is it appropriate?
A: WeakHashMap is a Map whose keys are held weakly — when a key has no strong references elsewhere, the entry is removed automatically at the next GC. Useful for caches where cache lifetime should match the lifetime of the key object.
Caveats:
• Size-based eviction is unreliable; entries can vanish at unpredictable times.
• Iteration is weakly consistent; it may produce stale entries briefly.
• The value holds a strong reference to the value object — be careful not to store a strong reference to the value back to the key, or the entry never dies.

Q: What is serialization in Java and what is its main pitfall?
A: Serialization converts an object to a byte stream via ObjectOutputStream / ObjectInputStream. A class opts in by implementing Serializable (a marker). On deserialization the JVM bypasses constructors — fields are populated reflectively. serialVersionUID controls version compatibility.
Pitfalls:
• Constructor bypass breaks invariants.
• Hidden security risks: deserialization gadget chains can execute attacker code (Java 8+ validates ObjectInputFilter to mitigate).
• Brittle to refactoring.
Prefer JSON/Protobuf for cross-system exchange; reserve Serializable for short-lived caches.

Q: What is the Externalizable interface in Java?
A: Externalizable extends Serializable and gives the class full control over its serialization by implementing writeExternal(ObjectOutput) and readExternal(ObjectInput).
Differences from Serializable:
• A public no-arg constructor is required (used during deserialization).
• You decide exactly which fields to write and how to encode them — can be more efficient and version-tolerant.
• Default constructor can run custom initialization before fields are restored.

Q: What are the transient and volatile keywords difference?
A: transient (storage modifier): a field marked transient is excluded from default Serializable serialization. Useful for non-serializable state, derived values, or sensitive data.
volatile (visibility modifier): a field marked volatile is read/written directly to main memory, providing cross-thread visibility.
They are unrelated — transient concerns persistence; volatile concerns concurrency. Both are applied to fields, not methods.

Q: What is the difference between BufferedInputStream and BufferedReader?
A: BufferedInputStream is a byte stream wrapper around an InputStream; reads raw bytes and supports mark/reset.
BufferedReader is a character stream wrapper around a Reader; reads chars/lines (e.g. readLine()) and decodes bytes using the default or specified charset.
Use BufferedInputStream for binary data (images, network protocols), BufferedReader for text. Always specify a charset explicitly (e.g. StandardCharsets.UTF_8) when reading text to avoid platform-dependent bugs.

Q: What is the difference between File, Path, and Files in Java NIO.2?
A: File (java.io, legacy) — encapsulates a path; many operations throw SecurityException and have limited error info.
Path (java.nio.file, NIO.2) — the modern, immutable representation of a file location.
Files (java.nio.file) — static utility methods for stream-based I/O: Files.readAllBytes(path), Files.write(...), Files.lines(path), Files.copy(...), Files.walk(...), etc.
Prefer Path + Files in new code: better exception handling, charset support, symbolic-link awareness, and watch service integration.

Q: What is the Files.walk() vs Files.walkFileTree() in Java NIO.2?
A: Files.walk(path, depth) returns a Stream<Path> lazily traversed; simple to use with Streams (filter, map, collect). Default depth is Integer.MAX_VALUE; use a depth limit on large trees to avoid stack overflow.
Files.walkFileTree(path, SimpleFileVisitor) drives a FileVisitor with preVisitDirectory, visitFile, visitFileFailed, postVisitDirectory. Better for non-trivial work (deleting trees, copying with attributes, following links).
Prefer walkFileTree for anything beyond a flat filter.

Q: What is the NIO ByteBuffer and what are heap vs direct buffers?
A: ByteBuffer (java.nio) is a fixed-size container for binary data. Two allocation modes:
• Heap: ByteBuffer.allocate(n) — backed by a byte[] on the JVM heap; GC-managed, fast to allocate, but requires a copy when passed to native channels.
• Direct: ByteBuffer.allocateDirect(n) — allocated outside the heap; native code (channels, sockets) can read/write without copying, ideal for high-throughput I/O. Direct buffers are more expensive to allocate and freed via Cleaner, not GC.

Q: What is the Selector in Java NIO and how does it enable non-blocking I/O?
A: Selector allows a single thread to multiplex many non-blocking SelectableChannel instances (sockets, pipes). Steps:
1. Open selector: Selector.open().
2. Register channels: channel.register(selector, SelectionKey.OP_READ).
3. Loop: selector.select() returns the count of channels with events.
4. Iterate selectedKeys(), handle READ/WRITE/ACCEPT/CONNECT.
It is the foundation of high-performance servers (Netty, Jetty, gRPC Java).

Q: What is the difference between BIO, NIO, and NIO.2 in Java?
A: BIO (blocking I/O) — classic java.io; one thread per socket; simple but scales poorly beyond a few hundred connections.
NIO (java.nio, Java 1.4+) — non-blocking channels, buffers, selectors; one thread can handle many connections; the basis of high-performance servers.
NIO.2 (Java 7+) — asynchronous I/O with AsynchronousChannel, AsynchronousFileChannel, callback-based or Future-based completion; plus the modern Path/Files API. NIO.2 is for true async, distinct from NIO's non-blocking polling.

Q: What is the try-with-resources resource ordering rule?
A: Resources are closed in the reverse order of their declaration, in a finally-equivalent block. Each resource is closed even if an exception occurs while opening or closing another.
Suppressed exceptions from earlier failures are attached to the primary exception via Throwable.addSuppressed(); retrieve them with e.getSuppressed().
Resources must implement AutoCloseable (most I/O classes do).

Q: What are checked exceptions in Java and how do they interact with lambdas?
A: Checked exceptions (subclasses of Exception other than RuntimeException) must be either caught or declared with throws.
Functional interfaces in java.util.function like Function, Consumer, Supplier, Predicate do not declare checked exceptions, so a lambda body that throws a checked exception (e.g. IOException from Files.lines) will not compile directly.
Workarounds: catch and wrap in a RuntimeException, use a custom functional interface with throws Exception, or use a helper like Lombok's @SneakyThrows.

Q: What is the difference between Error and Exception in Java?
A: Error (e.g. OutOfMemoryError, StackOverflowError, LinkageError) represents fatal, unrecoverable conditions the application should not try to catch. The JVM is usually in an unstable state.
Exception represents conditions applications can reasonably handle (I/O failures, validation, business rules).
Catching Throwable or Error is a code smell — let the JVM report and die, or recover at a coarser level (e.g. restart the worker).

Q: What is a stack trace and what does the order of "Caused by" mean?
A: A stack trace is a snapshot of the call stack at the moment an exception is thrown, listing class, method, file, and line. "Caused by" chains show the underlying root cause of a wrapped exception. The last "Caused by" is the original root cause; the first exception is the one the application actually saw.
Use Throwable.getCause(), getSuppressed(), and initCause() programmatically. Tools: Arrays.asList(e.getStackTrace()) or libraries like grep over logs.

Q: What is the difference between throw and throws in Java?
A: throw is a statement used to actually raise an exception: throw new IllegalArgumentException("bad");.
throws is a method-signature clause declaring that the method may propagate one or more exception types: void read() throws IOException { ... }.
You can throw any Throwable; throws is required only for checked exceptions (and Error subclasses are allowed but unnecessary).

Q: What is the difference between final, finally, and finalize?
A: final — keyword that makes a variable, method, or class unchangeable (no reassign, no override, no extend).
finally — block that runs after try/catch, executed whether or not an exception was thrown; used for cleanup. Skipped only if the JVM exits (System.exit) or the thread is killed.
finalize — legacy Object method called by GC before collection; deprecated for removal. Use try-with-resources or Cleaner instead.

Q: What is the difference between the Date, Calendar, and java.time APIs?
A: java.util.Date — legacy mutable timestamp, broken month indexing in some toString outputs, no time zones; largely superseded.
java.util.Calendar — mutable, complex, has month-off-by-one bugs.
java.time (Java 8+) — JSR-310, immutable, fluent API: LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Instant, Duration, Period. Built on java.time.format.DateTimeFormatter (thread-safe) and ChronoUnit.
Prefer java.time in all new code; avoid Date/Calendar.

Q: What is Instant vs LocalDateTime in Java time API?
A: Instant — a point on the UTC timeline, machine-friendly, nanosecond precision; obtained from Instant.now(). Used for timestamps and calculations across zones.
LocalDateTime — a date and time without a time zone; "what the clock on the wall reads" in some unspecified place. Cannot represent an exact moment.
Use ZonedDateTime or OffsetDateTime when you need a time zone/offset; convert Instant ↔ ZonedDateTime via ZoneId.systemDefault() or a specific zone.

Q: What is a StringJoiner / String.join()?
A: String.join(delim, elements) concatenates an iterable/array of strings with the given delimiter: String.join(", ", "a", "b", "c"); → "a, b, c".
StringJoiner is a mutable builder for the same idea with prefix/suffix: new StringJoiner(", ", "[", "]").
Both are convenient for one-shot joins. For repeated building use StringBuilder or Collectors.joining(...) in Streams.

Q: What is Collectors.joining() in Java Streams?
A: Collectors.joining(delimiter, prefix, suffix) produces a single string from a stream:
list.stream().map(Object::toString).collect(Collectors.joining(", ", "[", "]"));
It uses an internal StringJoiner — efficient and thread-confined. If the stream is empty, the result is just prefix + suffix. Combine with grouping/partitioning for nested joins.

Q: What is the difference between Comparable.sort() and Stream.sorted()?
A: Both produce elements in natural order, but:
• List.sort(Comparator) modifies the list in place (throws UnsupportedOperationException on unmodifiable lists); supports a custom comparator and is generally faster (no stream pipeline overhead).
• stream.sorted() returns a new stream; the original collection is unchanged; lazier and works inside larger pipelines. Requires the stream to be consumed by a terminal op before order is realized.

Q: What is the difference between HashMap, LinkedHashMap, and TreeMap?
A: All implement Map:
• HashMap — hash table; O(1) ops; no order.
• LinkedHashMap — hash table + doubly-linked list; O(1) ops; preserves insertion order; supports access-order mode (new LinkedHashMap(cap, 0.75f, true)) — used to build LRU caches by overriding removeEldestEntry.
• TreeMap — red-black tree; O(log n) ops; keys kept in natural or comparator order; supports range queries via subMap, headMap, tailMap, navigableKeySet.

Q: What is the difference between fail-fast and fail-safe iterators?
A: Fail-fast iterators (default for most java.util collections like ArrayList, HashMap) detect concurrent modification via a modCount check and throw ConcurrentModificationException on next iteration after structural change. They are weakly consistent only on the surface — strictly not safe for concurrent mutation.
Fail-safe / weakly consistent iterators (concurrent collections like ConcurrentHashMap, CopyOnWriteArrayList) operate on a snapshot or with weak ordering; they do not throw on concurrent modification and never see modified elements retroactively.

Q: What is CopyOnWriteArrayList and when is it appropriate?
A: CopyOnWriteArrayList is a thread-safe variant of ArrayList where every mutation (add/set/remove) creates a fresh underlying array copy. Readers iterate the immutable snapshot, so they are wait-free and never see torn data.
Use for read-mostly, write-rarely lists (listeners, configuration).
Don't use for general concurrent lists — writes are O(n) and memory-churning. For general concurrent lists use Collections.synchronizedList or specialized queues.

Q: What is the difference between synchronizedList, CopyOnWriteArrayList, and a ConcurrentLinkedQueue?
A: • Collections.synchronizedList — wraps any List with coarse-grained locking; simple, slow under contention.
• CopyOnWriteArrayList — wait-free reads, expensive copies on writes; great for listener registries.
• ConcurrentLinkedQueue — non-blocking MPMC queue; best for producer/consumer throughput.
• ArrayDeque + BlockingQueue variants when bounded capacity is needed.
Pick based on read/write ratio and access pattern, not a single rule.

Q: What is the difference between ArrayDeque and LinkedList when used as a stack/queue?
A: Both implement Deque and can act as stack or queue.
• ArrayDeque — resizable circular array; no node overhead; faster for stack/queue use; lower memory. Cannot store null.
• LinkedList — allocates a node per element; higher memory; slower due to pointer chasing; useful when you also need List semantics or arbitrary-index access. Also allows null.
Prefer ArrayDeque over Stack (legacy, synchronized) for stack use.

Q: What is the diamond operator in Java generics?
A: The diamond operator (<>) lets the compiler infer the generic type arguments from the context:
List<String> list = new ArrayList<>(); (Java 7+)
Without it, you'd write new ArrayList<String>() redundantly. From Java 9, the diamond is also allowed in anonymous inner class creation: new <> Comparator<String>() { ... } (if inferred from context).

Q: What is the PECS rule (Producer Extends Consumer Super) in Java generics?
A: PECS — when writing a method that consumes or produces generic values:
• If you only read from a collection (it's a producer), use ? extends T.
• If you only write into a collection (it's a consumer), use ? super T.
Example: void copy(List<? extends T> src, List<? super T> dest) — read from src, write to dest.
Mnemonic: "Get and Put" = extends in the "get" (producer) position, super in the "put" (consumer) position.

Q: What is the difference between List and List?
A: List<? extends T> — a list of some unknown subtype of T. Safe to read as T, but you cannot add (except null) because the actual type is unknown.
List<? super T> — a list of some unknown supertype of T. Safe to add T (and its subtypes), but reading returns Object.
Together they support variance: covariance for producers, contravariance for consumers.

Q: What is a raw type in Java and why is it discouraged?
A: A raw type is a generic class/interface used without type arguments: List list = new ArrayList(); instead of List<String> list = new ArrayList<>();.
Raw types are kept only for backward compatibility with pre-generics code. They disable generic type checking, produce unchecked warnings, and can cause ClassCastException at run time.
Always use parameterized types. The only legitimate use of a raw type is the class literal List.class (no parameters allowed).

Q: What is the difference between @SuppressWarnings("unchecked") and @SafeVarargs?
A: @SuppressWarnings("unchecked") silences a specific compiler warning at a particular site (variable, method, class). Use sparingly and add a comment explaining why it's safe.
@SafeVarargs (Java 7+) asserts that a method's varargs parameterized array is not exposed to heap pollution — i.e. it does not store the array or return it for outside modification. The compiler trusts the assertion and skips heap-pollution checks.
Apply only to static methods, final methods, or private methods (Java 9+ allows private instance methods).

Q: What is heap pollution in Java generics?
A: Heap pollution occurs when a variable of a parameterized type refers to an object that is not of that type. It typically happens when a non-reifiable varargs array (e.g. T...) is stored, returned, or iterated unsafely, mixing the array's runtime component (Object[]) with the compile-time type.
Consequences: ArrayStoreException or ClassCastException at a distant point in the code. Mitigations: @SafeVarargs on well-behaved methods; otherwise, copy the varargs into a typed List before storing.

Q: What is the difference between int[] and Integer[]?
A: int[] — a primitive array; stores raw 32-bit ints contiguously; no autoboxing overhead; cannot be used where Object[] is required.
Integer[] — an array of object references to boxed Integers; each element is a heap-allocated object; works with generics (List<Integer>) and reflection.
For numeric processing, primitive arrays (or libraries like Trove, Eclipse Collections, IntStream) avoid boxing. For collections of arbitrary size in generic code, use wrappers.

Q: What is the diamond problem in Java and how is it handled?
A: The diamond problem arises in multiple inheritance: two super-interfaces declare default methods with the same signature, and a class implementing both does not know which to inherit.
Java's rule: the implementing class must override the conflicting method (the compiler refuses to pick one). Inside the override, you can call a specific super's version with InterfaceA.super.method();.
This is different from C++ where the same class can suffer data-member ambiguities.

Q: What is the strategy pattern in Java?
A: Strategy defines a family of algorithms, encapsulates each one, and makes them interchangeable. Each strategy is a class implementing a common interface; the context object delegates work to the strategy instance.
Example: interface PricingStrategy { double price(double base); } with RegularPricing, DiscountPricing, HolidayPricing implementations. Combined with lambdas, the pattern reduces to passing a function — but the explicit interface remains useful when state/configuration is involved.

Q: What is the Observer pattern in Java?
A: Observer defines a one-to-many dependency so that when one object (the subject) changes state, all its dependents (observers) are notified.
Pre-9: java.util.Observer / Observable — deprecated.
Modern Java: implement your own interface (addListener, removeListener, fireEvent), or use PropertyChangeListener / PropertyChangeSupport (in java.beans), or use Flow API (Reactive Streams) for backpressure-aware streams.
Libraries: RxJava, Reactor, Guava EventBus.

Q: What is the Decorator pattern in Java?
A: Decorator attaches additional responsibilities to an object dynamically by wrapping it with another object of the same interface. Java I/O is the canonical example: new BufferedReader(new FileReader(file)) decorates a byte-by-byte reader with a buffering layer.
Implementation requires the same interface (or abstract class) for the component and decorator. Decorator differs from subclassing: it is a composition approach, not inheritance — fewer class explosions and easier runtime change.

Q: What is the DAO pattern in Java?
A: Data Access Object (DAO) abstracts persistence by providing an interface of domain operations (findById, save, delete) and one or more implementations (JDBC, JPA, in-memory for tests).
Benefits: business logic is independent of storage technology, easier to unit test (inject a mock DAO), and the same business code can switch between SQL, NoSQL, or a cache by swapping the DAO.
DAOs typically map 1:1 to a domain entity and return plain domain objects, not DTOs or rows.

Q: What is the difference between ArrayList.subList() and a new ArrayList(Collection)?
A: subList(from, to) returns a view onto the original list — modifications to either list are reflected in the other, and changes to the size of the original (outside the view) make the view undefined.
new ArrayList<>(collection) creates an independent copy.
Prefer an explicit copy when you want a snapshot to outlive structural changes to the original; prefer subList() for in-place range operations (e.g. removing a slice).

Q: What is the difference between Arrays.asList() and List.of()?
A: • Arrays.asList(array) — a fixed-size, mutable-element view backed by the original array. set() works; add()/remove() throw UnsupportedOperationException. Mutations to the array are visible in the list and vice versa.
• List.of(elements) (Java 9+) — truly immutable list; any structural change throws UnsupportedOperationException, and you cannot null elements. Multiple calls with the same elements may return the same instance (jvm optimization).
Prefer List.of / Set.of / Map.of for constants and DTOs.

Q: What is the difference between Collection.removeIf() and Stream.filter().collect()?
A: Both remove unwanted elements, but:
• collection.removeIf(predicate) mutates the collection in place and is generally faster (no intermediate list allocation, can stop early). Throws ConcurrentModificationException on iterators that don't allow it; works fine on the standard collections.
• stream.filter(p).collect(Collectors.toList()) returns a new list; the original is untouched. Use when you need the filtered result separate from the source, or when chaining further operations.

Q: What is the difference between Predicate.not() and explicit negation?
A: Java 11 added Predicate.not(p) as a static factory equivalent to p.negate() but more useful with method references:
list.removeIf(Predicate.not(String::isBlank)); is clearer than list.removeIf(s -> !s.isBlank());.
It is also a workaround for overload resolution ambiguity: passing a method reference to an IntPredicate/Predicate overload that is otherwise ambiguous.

Q: What is the Map.compute() / computeIfAbsent() / computeIfPresent() family?
A: Modern Map defaults for atomic, conditional updates:
• map.compute(key, (k,v) -> newValue) — always set (or remove) based on key+old value.
• map.computeIfAbsent(key, k -> newValue) — only set if absent; atomic on ConcurrentHashMap; common for memoization and Map<String, List<X>> caches.
• map.computeIfPresent(key, (k,v) -> transformed) — only update if key present.
• map.merge(key, value, remappingFunction) — combine old + new (e.g. add counts).
Prefer these over get-then-put to avoid race conditions and null-handling bugs.

Q: What is the difference between Map.getOrDefault() and Map.putIfAbsent()?
A: getOrDefault(key, default) — returns the value if present, else the supplied default; does not modify the map.
putIfAbsent(key, value) — sets and returns the value if absent; if present, returns the existing value and does not modify. Atomic on ConcurrentHashMap.
Use getOrDefault for read-only fallback; use putIfAbsent for cache fill. For "compute the value only if missing", use computeIfAbsent — it accepts a lambda to avoid unnecessary computation.

Q: What is the @FunctionalInterface annotation and is it required?
A: @FunctionalInterface is an informative annotation that tells the compiler to verify the interface has exactly one abstract method. It is not required for an interface to be usable as a lambda target — the compiler treats any SAM type as a functional interface regardless of the annotation.
Always add it to your functional interfaces: it documents intent and prevents future maintainers from accidentally breaking the contract by adding a second abstract method.

Q: What is the difference between mapToInt() and map() in Java Streams?
A: map(Function) — returns Stream<R> for any R; no special numeric operations.
mapToInt(ToIntFunction) — returns IntStream, an autoboxing-free primitive stream. Provides sum(), average(), summaryStatistics(), range() factories, and is generally faster for numeric pipelines.
Similar mapToLong / mapToDouble variants exist. Convert back to object stream with boxed().

Q: What is the IntStream.range() vs IntStream.rangeClosed()?
A: IntStream.range(start, end) — exclusive end: produces start, start+1, ..., end-1.
IntStream.rangeClosed(start, end) — inclusive end: produces start, start+1, ..., end.
For large known sequences prefer iterate(seed, hasNext, next) or generate(supplier) for arbitrary streams; use range* for integer ranges, e.g. IntStream.rangeClosed(1, 10).sum() → 55.

Q: What is the difference between reduce() and collect() in Streams?
A: reduce(identity, accumulator) — folds the stream into a single value using an associative, ideally stateless binary operator. Example: sum = list.stream().reduce(0, Integer::sum);.
collect(supplier, accumulator, combiner) — accumulates into a mutable container (e.g. StringBuilder, List). The combiner merges partial results, enabling efficient parallel collection.
Use collect when you build a collection; reduce for scalar folds. The 1-arg reduce returning Optional has weaker guarantees in parallel.

Q: What is a Checked Supplier / Function in functional Java?
A: Built-in functional interfaces (Supplier, Function, Consumer, Predicate) do not allow checked exceptions. To wrap an I/O throwing method as a lambda, you can:
1. Define your own functional interface with throws Exception.
2. Catch and rethrow as RuntimeException inside the lambda.
3. Use @FunctionalInterface with E extends Throwable generics on a custom interface.
4. Use Lombok's @SneakyThrows (controversial, but concise).

Q: What is the difference between an anonymous inner class and a lambda?
A: A lambda is a concise body that targets a functional interface; the compiler turns it into an invokedynamic call site (Java 7+), often without allocating an extra class file.
An anonymous inner class (e.g. new Runnable() { public void run() {...} }) creates a fresh named subclass at compile time (a MyClass$1.class file), can extend a class or implement an interface, and can have state, multiple methods, or non-final captured variables (within the same rules).
Use lambdas for SAM types; anonymous classes when you need more than one method or a non-functional type.

Q: What is the difference between inner class, nested static class, and local class?
A: • Nested static class (e.g. Outer.Nested) — declared static inside another class; does not capture the outer instance; behaves like a top-level class; can be instantiated without an outer object.
• Inner (non-static) class — each instance holds an implicit reference to its enclosing instance; can access all enclosing members; instantiable as outer.new Inner().
• Local class — declared inside a method; can access effectively final locals; rarely needed in modern Java (use lambdas instead).
• Anonymous class — local class with no name; see previous card.