Interviews

🎯 Interview Guides 12 guides · updated 2026

Real questions and structured answers for data, cloud, and AI engineering interviews β€” including the system-design and GenAI rounds now showing up everywhere.

Core Java Interview Questions and Answers

These questions span Java fundamentals through modern Java 21 features β€” what hiring teams actually test for Java developer, backend engineer, and data engineer positions.


Object-Oriented Programming

Q1. Explain the four pillars of OOP with Java examples.

Encapsulation β€” bundle data and methods, restrict direct access:

public class BankAccount {
private double balance; // Hidden state
public void deposit(double amount) {
if (amount > 0) balance += amount;
}
public double getBalance() { return balance; }
}

Inheritance β€” subclass inherits and extends parent behavior:

abstract class Shape {
abstract double area();
}
class Circle extends Shape {
double radius;
Circle(double r) { this.radius = r; }
@Override
double area() { return Math.PI * radius * radius; }
}

Polymorphism β€” same interface, different behavior:

Shape s1 = new Circle(5);
Shape s2 = new Rectangle(4, 6);
System.out.println(s1.area()); // 78.54 β€” runtime dispatch
System.out.println(s2.area()); // 24.0

Abstraction β€” expose what, hide how (via abstract classes or interfaces).


Q2. What is the difference between an abstract class and an interface in Java?

// Abstract class β€” partial implementation, single inheritance
abstract class Animal {
String name;
Animal(String name) { this.name = name; } // Can have constructor
abstract void makeSound();
void breathe() { System.out.println("breathing"); } // Concrete method
}
// Interface β€” contract, multiple implementation (Java 8+: default methods)
interface Flyable {
int MAX_ALTITUDE = 10000; // Implicitly public static final
void fly(); // Implicitly public abstract
default void land() { // Default method (Java 8+)
System.out.println("landing");
}
static Flyable createBird() { return new Bird(); } // Static method (Java 8+)
}

Key differences:


Q3. What is the difference between == and .equals() in Java?

String a = new String("hello");
String b = new String("hello");
System.out.println(a == b); // false β€” different objects
System.out.println(a.equals(b)); // true β€” same content
Integer x = 127;
Integer y = 127;
System.out.println(x == y); // true β€” Integer cache (-128 to 127)
Integer p = 200;
Integer q = 200;
System.out.println(p == q); // false β€” outside cache range

Always use .equals() for object comparison. The Integer cache gotcha is a classic interview trap.


Collections & Generics

Q4. Explain the Java Collections Framework hierarchy.

Iterable
└── Collection
β”œβ”€β”€ List (ordered, allows duplicates)
β”‚ β”œβ”€β”€ ArrayList β€” O(1) get, O(n) insert/delete in middle
β”‚ β”œβ”€β”€ LinkedList β€” O(n) get, O(1) insert/delete at known node
β”‚ └── Vector β€” synchronized (prefer CopyOnWriteArrayList)
β”œβ”€β”€ Set (no duplicates)
β”‚ β”œβ”€β”€ HashSet β€” O(1) add/contains, no order
β”‚ β”œβ”€β”€ LinkedHashSet β€” insertion order, O(1)
β”‚ └── TreeSet β€” sorted order, O(log n)
└── Queue
β”œβ”€β”€ PriorityQueue β€” heap-based, O(log n) insert/poll
└── ArrayDeque β€” double-ended queue, O(1) amortized
Map (key-value, not Collection)
β”œβ”€β”€ HashMap β€” O(1) avg, no order
β”œβ”€β”€ LinkedHashMap β€” insertion order
β”œβ”€β”€ TreeMap β€” sorted by key, O(log n)
└── ConcurrentHashMap β€” thread-safe, no full locking

Q5. What happens internally in a HashMap when you put an element?

map.put(key, value);
  1. Compute key.hashCode() and apply hash spread: hash = (h = key.hashCode()) ^ (h >>> 16)
  2. Index into the bucket array: index = hash & (capacity - 1)
  3. If bucket is empty: create a new Node<K,V> and place it
  4. If bucket has entries: traverse the linked list (or tree if long):
    • If key.equals(existingKey): replace value
    • Otherwise: append new node
  5. If the list in a bucket exceeds 8 entries AND capacity β‰₯ 64: convert to red-black tree (O(log n))
  6. If load factor exceeded (default 0.75): resize and rehash (O(n) operation)

Key insight: equals() and hashCode() must be overridden consistently. Two equal objects must have the same hashCode; two objects with the same hashCode need not be equal (hash collision).


Multithreading & Concurrency

Q6. What is the difference between synchronized, ReentrantLock, and volatile?

synchronized β€” intrinsic lock on an object or class. Simplest; non-interruptible; no timeout.

synchronized (this) { /* critical section */ }
public synchronized void increment() { count++; }

ReentrantLock β€” explicit lock with more control:

Lock lock = new ReentrantLock();
if (lock.tryLock(200, TimeUnit.MILLISECONDS)) {
try { /* critical section */ }
finally { lock.unlock(); }
}

Advantages: tryLock() with timeout, interruptible locking, fair ordering option, multiple condition variables.

volatile β€” guarantees visibility (reads/writes go to main memory, not CPU cache). Does NOT guarantee atomicity for compound operations.

volatile boolean running = true; // Thread-safe flag β€” one thread stops another
// But: volatile int count; count++; NOT thread-safe (read-modify-write)

Q7. What are virtual threads in Java 21 and why are they significant?

Virtual threads (Project Loom, JDK 21 stable) are lightweight threads managed by the JVM, not the OS. Millions can be created without memory exhaustion:

// Platform thread (OS-backed, ~1MB stack, thousands max)
Thread.ofPlatform().start(() -> { ... });
// Virtual thread (JVM-managed, tiny footprint, millions possible)
Thread.ofVirtual().start(() -> { ... });
// Or with executor
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 1_000_000; i++) {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1)); // Doesn't block OS thread!
return processRequest();
});
}
}

Why significant:


Q8. What is the Java Memory Model (JMM) and what is the happens-before relationship?

The JMM defines how threads interact through memory and what values a thread is guaranteed to see when reading a field written by another thread.

Happens-before is a guarantee that memory writes by one thread are visible to reads by another:

Without happens-before, threads may read stale cached values. Proper synchronization establishes happens-before chains.


Functional & Streams

Q9. Explain Java Streams and the difference between intermediate and terminal operations.

Streams process collections declaratively and lazily:

List<Employee> employees = getEmployees();
// Processing pipeline
Map<String, Double> avgSalaryByDept = employees.stream()
.filter(e -> e.getSalary() > 50_000) // Intermediate β€” lazy
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.averagingDouble(Employee::getSalary) // Terminal β€” triggers execution
));

Intermediate operations (lazy, return a Stream): filter, map, flatMap, distinct, sorted, limit, skip, peek

Terminal operations (trigger execution, return a result): collect, forEach, count, findFirst, anyMatch, reduce, toList (Java 16+)

Streams don’t modify the source and are not reusable β€” a terminal operation β€œconsumes” the stream.


Q10. What are the functional interfaces in the java.util.function package?

InterfaceSignatureUse case
Predicate<T>T β†’ booleanFilter, test conditions
Function<T,R>T β†’ RTransform/map values
Consumer<T>T β†’ voidSide effects (forEach)
Supplier<T>() β†’ TLazy value creation
BiFunction<T,U,R>(T,U) β†’ RTwo-argument transform
UnaryOperator<T>T β†’ TTransform same type
BinaryOperator<T>(T,T) β†’ TReduce two same-type values
Predicate<String> isLong = s -> s.length() > 10;
Function<String, Integer> length = String::length; // Method reference
Consumer<String> print = System.out::println;
Supplier<List<String>> newList = ArrayList::new;
// Composition
Predicate<String> isShort = isLong.negate();
Function<String, String> upperThenTrim = ((Function<String,String>)String::toUpperCase).andThen(String::trim);

JVM & Modern Java

Q11. Explain the JVM’s garbage collection and the difference between G1 and ZGC.

The JVM heap is divided into generations (young, old) for most GCs:

GCPauseThroughputBest for
G1 GCLow (region-based)HighDefault since Java 9; most apps
ZGCUltra-low (< 1ms)ModerateLatency-critical, huge heaps (TB)
ShenandoahUltra-lowModerateSimilar to ZGC, Red Hat
Serial GCHighLowSingle-core, embedded

ZGC (now production-ready since Java 15, improved in 21) performs most GC work concurrently without stopping application threads, achieving sub-millisecond pause times regardless of heap size.


Q12. What are records in Java and when would you use them?

Records (Java 16+) are immutable data carriers with automatic boilerplate:

// Traditional class β€” 30+ lines for the same thing
record Point(double x, double y) {}
// Auto-generated: constructor, getters, equals, hashCode, toString
Point p = new Point(3.0, 4.0);
System.out.println(p.x()); // 3.0
System.out.println(p); // Point[x=3.0, y=4.0]
// Custom compact constructor for validation
record Temperature(double celsius) {
Temperature {
if (celsius < -273.15) throw new IllegalArgumentException("Below absolute zero");
}
double toFahrenheit() { return celsius * 9/5 + 32; }
}

Use records for DTOs, command objects, value objects β€” anywhere you want immutable, transparent data containers. They cannot extend other classes and fields are implicitly final.


Q13. What are sealed classes in Java and what problem do they solve?

Sealed classes (Java 17+) restrict which classes can extend or implement them:

sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
record Triangle(double base, double height) implements Shape {}
// Pattern matching with switch (Java 21 β€” stable)
double area = switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
case Triangle t -> 0.5 * t.base() * t.height();
};
// Compiler verifies exhaustiveness β€” no default needed!

Sealed classes enable exhaustive pattern matching β€” the compiler guarantees all cases are handled, making refactoring safer.


Q14. Explain Java’s Optional and when to use (and not use) it.

Optional<T> wraps a value that might be null, making the null case explicit in the type system:

// GOOD: return type from methods that might not return a value
public Optional<User> findById(Long id) {
return userRepository.findById(id);
}
// Consuming Optional
findById(42L)
.map(User::getEmail)
.filter(email -> email.endsWith("@company.com"))
.ifPresent(email -> sendWelcomeEmail(email));
// With default value
String name = findById(42L)
.map(User::getName)
.orElse("Guest");

Don’t use Optional for:


Q15. What is the difference between Comparable and Comparator?

// Comparable β€” natural ordering defined in the class itself
class Employee implements Comparable<Employee> {
String name;
int salary;
@Override
public int compareTo(Employee other) {
return Integer.compare(this.salary, other.salary); // Natural: by salary
}
}
// Comparator β€” external, flexible ordering
Comparator<Employee> byName = Comparator.comparing(Employee::getName);
Comparator<Employee> bySalaryDesc = Comparator.comparingInt(Employee::getSalary).reversed();
Comparator<Employee> compound = byName.thenComparingInt(Employee::getSalary);
List<Employee> employees = getEmployees();
employees.sort(bySalaryDesc);
TreeMap<Employee, String> sorted = new TreeMap<>(byName); // Use as key comparator

Use Comparable for the default natural ordering (makes sense as a property of the class). Use Comparator when you need multiple ordering strategies or ordering on classes you don’t own.