Skip to content

Java Advanced Interview Questions

Advanced concepts: patterns, performance, frameworks, concurrency, and enterprise design.


1. What's the difference between volatile, synchronized, and Atomic classes?

View Answer

Comparison Table

Property volatile synchronized Atomic
Purpose Visibility guarantee Mutual exclusion (lock) Atomic operations (lock-free)
Lock None Monitor lock Compare-and-swap (CAS)
Thread-safe Read/write visibility only Full exclusion Atomic operations only
Performance Fast (no locking) Slow (locks) Fast (lock-free, hardware)
Use case Flag variables, stop signals Shared state, compound operations Counters, simple updates

volatile: Visibility Only

volatile boolean shutdown = false;

// Thread 1
while (!shutdown) {  // Sees latest value
    process();
}

// Thread 2
shutdown = true;    // Visible immediately to Thread 1

✅ Fast, but only guarantees visibility.

synchronized: Mutual Exclusion

int count = 0;

synchronized void increment() {
    count++;  // Atomic: read-modify-write
}

✅ Full thread-safety, but slower (lock contention).

Atomic: Lock-Free

AtomicInteger counter = new AtomicInteger(0);

counter.incrementAndGet();  // Hardware CAS operation

✅ Fast and atomic, best for simple operations.

📚 Learn more: Concurrency


2. What's the difference between Runnable and Callable?

View Answer

Quick Comparison

Feature Runnable Callable
Return value void (no return) Returns result (<T>)
Exception Cannot throw checked Can throw checked
Method run() call()
Executor execute() submit()

Runnable: No Return

Runnable task = () -> {
    System.out.println("Task running");
    // No return value
};

executor.execute(task);  // Fire-and-forget

Callable: With Return

Callable<Integer> task = () -> {
    return 42;  // Returns result
};

Future<Integer> future = executor.submit(task);
Integer result = future.get();  // Block and get result

📚 Learn more: Threading


3. Explain Future vs CompletableFuture

View Answer

Future: Blocking

Represents result of async computation, but getting result blocks.

ExecutorService executor = Executors.newFixedThreadPool(1);
Future<String> future = executor.submit(() -> {
    Thread.sleep(1000);
    return "Result";
});

String result = future.get();  // ❌ Blocks here until ready

Problems: Blocking, limited composition, no callbacks

CompletableFuture: Non-Blocking

Non-blocking chains and callbacks.

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    return "Step 1";
})
.thenApply(s -> s + " → Step 2")      // Chain operations
.thenAccept(s -> System.out.println(s));  // Callback (non-blocking)

// Main thread continues, doesn't wait

Comparison Table

Feature Future CompletableFuture
Blocking Yes (.get()) Optional
Chaining Limited Rich (.thenApply, .thenCombine)
Callbacks No Yes (.thenAccept, .whenComplete)
Error handling Exception on .get() .exceptionally, .handle
Combining Manual Built-in (.thenCombine, .anyOf, .allOf)

CompletableFuture Methods

  • .thenApply(): Transform result
  • .thenAccept(): Consume result (no return)
  • .thenCombine(): Combine two futures
  • .exceptionally(): Handle errors
  • .allOf(): Wait for all futures
  • .anyOf(): Wait for first future

📚 Learn more: Concurrency & Futures


4. What are design patterns and when to use them?

View Answer

Categories of Design Patterns

Creational: Object Creation

  • Singleton: One instance per application
  • Factory: Create objects without specifying exact classes
  • Builder: Construct complex objects step-by-step

Structural: Object Composition

  • Decorator: Add behavior to objects dynamically
  • Adapter: Make incompatible interfaces compatible
  • Proxy: Provide placeholder for another object

Behavioral: Object Collaboration

  • Observer: Notify multiple objects of state changes
  • Strategy: Encapsulate interchangeable algorithms
  • Template Method: Define algorithm skeleton, subclasses fill details

When to Use

Problem Pattern Example
Need single instance Singleton Logger, Database connection
Create families of objects Factory Database drivers (MySQL, PostgreSQL)
Complex object construction Builder HTTP request with many options
Add behavior at runtime Decorator Compression, encryption wrappers
Make incompatible things work Adapter Legacy API integration
Notify multiple observers Observer Event handling, MVC

📚 Learn more: Design Patterns


5. How does garbage collection work?

View Answer

GC Process

  1. Mark: Identify reachable objects from GC roots
  2. Sweep: Free unreachable objects
  3. Compact: Defragment heap (some algorithms)

GC Algorithms

Algorithm Latency Throughput Best For
Serial GC High pauses Good Single-core, small heaps
Parallel GC Medium pauses Best Batch processing
G1GC Low pauses Good Large heaps, low latency
ZGC Very low (<10ms) Good Ultra-low latency apps

Generational Hypothesis

Most objects die young; old objects rarely become garbage.

Young Generation (Frequent GC)
├─ Eden
├─ Survivor 1
└─ Survivor 2

Old Generation (Infrequent GC)

✅ Optimize for young generation garbage collection.

📚 Learn more: JVM & Garbage Collection


6. What's the difference between type erasure and generics?

View Answer

Generics: Compile-Time Type Safety

List<String> list = new ArrayList<>();
list.add("Hello");
String s = list.get(0);  // No cast needed, type-safe

Type Erasure: Runtime Behavior

Generics info ERASED at runtime.

// At compile-time (generic)
List<String> stringList = new ArrayList<>();

// At runtime (erased)
List stringList = new ArrayList();  // Type info gone!

Implications

// ❌ INVALID: Runtime doesn't know generic type
List<String> list = new ArrayList<>();
if (list instanceof List<String>) { }  // Compile error

// ✅ Valid: Can check raw type
if (list instanceof List) { }

Why Type Erasure?

  • Backward compatibility with pre-Java 5 code
  • Reduce runtime memory (no type info stored)

PECS Principle: Producer Extends, Consumer Super

// List<String> producer
List<? extends String> producer = list;

// List accepts String consumer
List<? super String> consumer = list;

📚 Learn more: Java Generics


7. How do you identify and fix performance bottlenecks?

View Answer

Identification Steps

  1. Profile: Measure CPU, memory, I/O usage
  2. Find hotspot: Identify slowest method/component
  3. Measure baseline: Before/after comparison
  4. Iterate: Fix one issue at a time

Profiling Tools

  • JProfiler: CPU/memory profiling
  • YourKit: Advanced performance analysis
  • Async Profiler: Low-overhead sampling
  • JMH: Microbenchmarking

Common Bottlenecks & Fixes

Issue Symptoms Fix
GC pauses High latency spikes Tune heap, use low-latency GC (ZGC)
Lock contention High thread wait time Use ConcurrentHashMap, lock-free structures
Database queries Slow response time Add indexes, optimize SQL, cache
N+1 problem Many queries, slow Use JOIN FETCH, entity graphs
Memory leaks Growing heap, OOM Profile, fix reference leaks

📚 Learn more: Performance Optimization


8. Explain the decorator pattern with examples

View Answer

Decorator Pattern: Add Behavior Dynamically

Wrap objects to add behavior without modifying original class.

Example: Coffee Shop

// Base interface
interface Coffee {
    double cost();
    String description();
}

// Concrete implementation
class SimpleCoffee implements Coffee {
    public double cost() { return 2.0; }
    public String description() { return "Simple Coffee"; }
}

// Decorator
abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee;
    public CoffeeDecorator(Coffee coffee) { this.coffee = coffee; }
}

// Concrete decorators
class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) { super(coffee); }
    public double cost() { return coffee.cost() + 0.5; }
    public String description() { return coffee.description() + ", Milk"; }
}

class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) { super(coffee); }
    public double cost() { return coffee.cost() + 0.2; }
    public String description() { return coffee.description() + ", Sugar"; }
}

Usage

Coffee coffee = new SimpleCoffee();  // $2.0
coffee = new MilkDecorator(coffee);   // $2.5
coffee = new SugarDecorator(coffee);  // $2.7

System.out.println(coffee.description());  // Simple Coffee, Milk, Sugar
System.out.println(coffee.cost());         // 2.7

Real-World Examples

  • Java I/O: InputStream → BufferedInputStream → DataInputStream
  • Spring: @Transactional decorator on methods
  • Compression: FileOutputStream → GZIPOutputStream

Decorator vs Inheritance

Decorator: Combine at runtime, flexible, multiple decorators

Inheritance: Fixed at compile-time, rigid class hierarchy

📚 Learn more: Design Patterns


See also: Design Patterns, Concurrency, Interview Questions Overview