These Java interview questions for experienced engineers go deeper than the basics: collections internals, concurrency and multithreading scenarios, Java 8 stream coding, version-specific features, and question sets in the style of service companies and FAANG. For tester-focused basics, see the Java interview questions for testers.

Core Java

What is the difference between == and equals()?

== compares references (are they the same object in memory?). equals() compares logical content (defined by the class). String, Integer, and other classes override equals() for value comparison. Always override both equals() AND hashCode() together.

What is a NullPointerException and how to prevent it?

NPE is thrown when you call a method or access a field on a null reference. Prevention: use Optional<T>, null checks, Objects.requireNonNull(), @NotNull annotations, initialize fields in constructor, use String.equals() on literals ("hello".equals(str) vs str.equals("hello")).

What is the difference between checked and unchecked exceptions?

Checked: must be caught or declared in throws clause (IOException, SQLException). Compiler enforces this. Unchecked (RuntimeException subclasses): optional to handle (NullPointerException, IllegalArgumentException). Use checked for recoverable conditions, unchecked for programming bugs.

What is String.format() vs printf() vs concatenation?

String.format() creates a formatted string. System.out.printf() formats and prints (like C's printf). Concatenation (+) creates new String objects. For multiple formats: String.format() or StringBuilder. For simple messages: concatenation or modern text blocks.

Advertisement

OOP

Explain SOLID principles.

S: Single Responsibility — class does one thing. O: Open/Closed — open for extension, closed for modification. L: Liskov Substitution — subclass can replace superclass without breaking code. I: Interface Segregation — small focused interfaces. D: Dependency Inversion — depend on abstractions, not concretions.

What is the difference between composition and inheritance?

Inheritance: 'IS-A' relationship (Dog is an Animal). Composition: 'HAS-A' relationship (Car has an Engine). Prefer composition — more flexible, avoids fragile base class problem, supports runtime behavior change. Inheritance creates tight coupling between parent and child.

Collections

Difference between Hashtable and HashMap?

Hashtable: synchronized (thread-safe), doesn't allow null keys/values, legacy class. HashMap: not synchronized, allows one null key and multiple null values, faster. Use ConcurrentHashMap instead of Hashtable for modern thread-safe maps — better performance.

What is ConcurrentHashMap?

Thread-safe HashMap that uses segment-level locking (Java 7) or CAS + synchronized on buckets (Java 8+). Multiple threads can read simultaneously. Write operations lock only the affected bucket (not the whole map). Much more performant than synchronized HashMap for concurrent access.

Difference between Iterator and ListIterator?

Iterator: traverse List, Set, Map. forward-only, can remove. ListIterator: extends Iterator, List only, bidirectional (hasPrevious/previous), can add/set elements. Both allow safe removal during iteration (unlike for-each which throws ConcurrentModificationException).

Concurrency

What is the happens-before relationship?

Happens-before is a JMM (Java Memory Model) guarantee that one action's effects are visible to another. Rules: program order, monitor unlock happens-before next lock, volatile write happens-before subsequent read, thread start/join. Without happens-before, threads may see stale cached values.

What is a ThreadLocal?

ThreadLocal provides thread-local variables — each thread has its own independent copy. Example: storing user session info per request in a web server. Get/set with get() and set(). Must call remove() to prevent memory leaks in thread pools where threads are reused.

What is CountDownLatch vs CyclicBarrier?

CountDownLatch: one-time use, threads wait until count reaches 0 (main thread waits for N workers). CyclicBarrier: reusable, N threads wait for each other at a barrier point, then continue together. Use CountDownLatch for start/end signals, CyclicBarrier for phase synchronization.

Advanced

What is CompletableFuture?

Java 8 async programming tool. Unlike Future, supports non-blocking chaining: thenApply() (transform), thenAccept() (consume), thenCombine() (merge two), thenCompose() (flat-map), exceptionally() (error handle), allOf() (wait for all), anyOf() (wait for first). Enables reactive-style pipelines without blocking threads.

What is the difference between Comparable and Comparator?

Comparable: natural ordering via compareTo() in the class itself (single ordering). Comparator: external ordering, multiple orderings, used as parameter. sort(list, comparator). Java 8: Comparator.comparing(), thenComparing(), reversed() for readable chaining.

What is a memory leak in Java?

A memory leak is when objects are kept referenced (not eligible for GC) even when no longer needed. Causes: static collections growing forever, unclosed streams/connections, listener registrations not removed, ThreadLocal not cleared in thread pools. Tools: JProfiler, VisualVM, heap dump analysis.

Java Version Questions

Java 8 FeatureJava 11+ Enhancement
Lambdas & Streamsvar in lambda params (Java 11)
Optional<T>Optional.isEmpty() (Java 11)
default in interfacesprivate methods in interfaces (Java 9)
CompletableFutureCompletableFuture.delayedExecutor (Java 9)
Date-Time APIHttpClient built-in (Java 11)
StringJoinerString.isBlank(), lines(), strip() (Java 11)
Method referencesRecords (Java 16)
Stream APIStream.toList() (Java 16)
🎉

You now know Java from Zero to Interview-Ready!

Core Java → OOP → Exception Handling → Collections → Concurrency → Streams → Generics → Design Patterns → JVM Internals → Modern Java

FAANG / Tier-1 Level

Explain the Java Memory Model and happens-before guarantees.

JMM defines rules for visibility of memory writes across threads. Happens-before: (1) Program order within a thread, (2) Monitor unlock → subsequent lock, (3) Volatile write → subsequent read, (4) Thread.start() → any action in that thread, (5) Thread.join() → returns to joining thread. Without H-B, threads may see stale values from CPU caches.

How does ConcurrentHashMap work internally in Java 8+?

Java 8 ConcurrentHashMap uses an array of Node buckets. Reads are lock-free (volatile reads). Writes use synchronized on the specific bucket head (not the whole map). When a bucket's linked list grows beyond 8 nodes, it's converted to a Red-Black Tree. Size counting uses a distributed counter (LongAdder) to avoid contention. No global lock — extremely high concurrency.

What is the difference between ReentrantLock and synchronized?

synchronized: JVM-managed, no fairness, no timeout, no interruptible lock. ReentrantLock: explicit lock/unlock, optional fairness (FIFO order), tryLock() with timeout, lockInterruptibly(), multiple Condition objects per lock (like multiple wait sets). Use synchronized for simple cases; ReentrantLock for advanced control.

Design a thread-safe Singleton without double-checked locking.

Best approach: Enum Singleton (public enum Singleton { INSTANCE; }). It's thread-safe by JVM class loading, serialization-safe, and reflection-safe. Alternative: Initialization-on-demand holder idiom (private static class Holder { static final S INSTANCE = new S(); }). Both avoid synchronization overhead entirely.

What is a WeakReference and when would you use it?

WeakReference allows the referenced object to be GC'd if no strong references exist. Used for: caches (WeakHashMap — entries auto-removed when keys GC'd), listeners that shouldn't prevent GC of their targets, canonicalization maps. SoftReference is GC'd only when memory is low — good for image caches.

Mid-Level (TCS, Infosys, Wipro, Cognizant)

What is the difference between ArrayList and Vector?

Vector is synchronized (thread-safe, slower). ArrayList is not synchronized (faster). Both use dynamic arrays growing by different factors (Vector: 2x, ArrayList: 1.5x). Vector is legacy; use ArrayList + Collections.synchronizedList() or CopyOnWriteArrayList for thread-safe needs.

What happens when we store a duplicate key in HashMap?

The new value OVERWRITES the old value for that key. The key count stays the same. HashMap.put() returns the OLD value (or null if new key). The key object's hashCode() and equals() are used to identify duplicates.

What is method hiding vs method overriding?

Overriding: instance methods, @Override annotation, runtime polymorphism — JVM calls based on OBJECT type. Method hiding: static methods, same signature in parent+child, compile-time resolution — JVM calls based on REFERENCE type. You can't truly override static methods.

What is the purpose of the transient keyword?

transient marks a field to be excluded from Java serialization. When the object is serialized (written to bytes), transient fields are skipped and get their default values on deserialization (null for objects, 0 for primitives). Use for: passwords, computed fields, non-serializable references.

Explain try-with-resources and what interface it requires.

try-with-resources automatically closes resources after the block. Resources must implement AutoCloseable (single method: close()). Closeable extends AutoCloseable for I/O. Multiple resources allowed. Resources are closed in REVERSE order of declaration. close() is called even if an exception occurs.

Java 8 Streams — Coding Questions

import java.util.*;
import java.util.stream.*;

List<Integer> numbers = List.of(1,2,3,4,5,6,7,8,9,10);
List<String> words = List.of("apple","banana","cherry","date","elderberry");
List<Employee> employees = getEmployees(); // with name, dept, salary fields

// Q1: Find sum of all even numbers
int sumEven = numbers.stream()
    .filter(n -> n % 2 == 0)
    .mapToInt(Integer::intValue).sum(); // 30

// Q2: Find all words with length > 5, uppercase, sorted
List<String> result = words.stream()
    .filter(w -> w.length() > 5)
    .map(String::toUpperCase)
    .sorted()
    .collect(Collectors.toList());
// [BANANA, CHERRY, ELDERBERRY]

// Q3: Group employees by department
Map<String, List<Employee>> byDept = employees.stream()
    .collect(Collectors.groupingBy(Employee::getDepartment));

// Q4: Average salary per department
Map<String, Double> avgSalary = employees.stream()
    .collect(Collectors.groupingBy(
        Employee::getDepartment,
        Collectors.averagingDouble(Employee::getSalary)));

// Q5: Highest paid employee per department
Map<String, Optional<Employee>> topEarner = employees.stream()
    .collect(Collectors.groupingBy(
        Employee::getDepartment,
        Collectors.maxBy(Comparator.comparingDouble(Employee::getSalary))));

// Q6: Count employees per department
Map<String, Long> countByDept = employees.stream()
    .collect(Collectors.groupingBy(Employee::getDepartment, Collectors.counting()));

// Q7: Get names of all employees earning > 50000
List<String> highEarners = employees.stream()
    .filter(e -> e.getSalary() > 50000)
    .map(Employee::getName)
    .sorted()
    .collect(Collectors.toList());

// Q8: Find second highest salary
Optional<Double> secondHighest = employees.stream()
    .map(Employee::getSalary)
    .distinct()
    .sorted(Comparator.reverseOrder())
    .skip(1)
    .findFirst();

// Q9: Partition employees — senior (salary>60k) vs junior
Map<Boolean, List<Employee>> partition = employees.stream()
    .collect(Collectors.partitioningBy(e -> e.getSalary() > 60000));
List<Employee> senior = partition.get(true);
List<Employee> junior = partition.get(false);

// Q10: Concatenate all names with comma
String allNames = employees.stream()
    .map(Employee::getName)
    .collect(Collectors.joining(", "));

// Q11: FlatMap — list of lists to single list
List<List<Integer>> nested = List.of(List.of(1,2), List.of(3,4), List.of(5,6));
List<Integer> flat = nested.stream()
    .flatMap(Collection::stream)
    .collect(Collectors.toList()); // [1,2,3,4,5,6]

// Q12: Find if any employee is from IT dept
boolean hasIT = employees.stream()
    .anyMatch(e -> "IT".equals(e.getDepartment()));

Multithreading Interview Scenarios

// SCENARIO 1: Print odd/even numbers alternately using 2 threads
public class OddEvenPrinter {
    private int count = 1;
    private static final int MAX = 10;
    private final Object lock = new Object();

    public void printOdd() {
        synchronized (lock) {
            while (count <= MAX) {
                while (count % 2 == 0) { try { lock.wait(); } catch (InterruptedException e) {} }
                if (count <= MAX) System.out.print(count + " ");
                count++;
                lock.notify();
            }
        }
    }

    public void printEven() {
        synchronized (lock) {
            while (count <= MAX) {
                while (count % 2 != 0) { try { lock.wait(); } catch (InterruptedException e) {} }
                if (count <= MAX) System.out.print(count + " ");
                count++;
                lock.notify();
            }
        }
    }
}

// SCENARIO 2: Producer-Consumer with BlockingQueue
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5);

Thread producer = new Thread(() -> {
    for (int i = 1; i <= 10; i++) {
        try {
            queue.put(i); // blocks if full
            System.out.println("Produced: " + i);
        } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
    }
});

Thread consumer = new Thread(() -> {
    for (int i = 0; i < 10; i++) {
        try {
            int item = queue.take(); // blocks if empty
            System.out.println("Consumed: " + item);
            Thread.sleep(100);
        } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
    }
});

producer.start(); consumer.start();
producer.join(); consumer.join();

FAQs

What Java topics are asked at 3–5 years experience?

HashMap and ConcurrentHashMap internals, equals/hashCode, immutability, multithreading and the java.util.concurrent toolkit, streams and lambdas, JVM memory and garbage collection, and design patterns.