Skip to content
Java interview 8 min read

Interview Questions

A focused set of the questions that actually come up in Java interviews, grouped by topic. Answers are short on purpose — enough to demonstrate understanding without rambling. If you can explain each of these clearly out loud, you’re in good shape.

The best interview answers state the rule, then add the “why” or a caveat. Memorizing the headline isn’t enough; interviewers probe for the reasoning underneath.

Core Java

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

== compares references for objects (identity) and raw values for primitives. equals() compares logical equality and can be overridden — String, the wrapper classes, and most JDK types override it to compare content. So new String("a") == new String("a") is false, but .equals() returns true.

Why is String immutable in Java?

Once created, a String’s value never changes. This enables safe sharing across threads without synchronization, allows the string pool to cache and reuse literals, makes strings safe to use as HashMap keys (the hash is stable), and improves security (a path or hostname can’t be mutated after a check). Operations like concat return a new String.

What is the difference between final, finally, and finalize?

They are unrelated despite the similar names. final is a modifier: a final variable can’t be reassigned, a final method can’t be overridden, a final class can’t be subclassed. finally is a block that always runs after try/catch, used for cleanup. finalize() was a deprecated Object method called before garbage collection — avoid it entirely; use try-with-resources or Cleaner instead.

What is the difference between checked and unchecked exceptions?

Checked exceptions (subclasses of Exception but not RuntimeException) must be declared or caught — the compiler enforces handling, e.g. IOException. Unchecked exceptions (RuntimeException and its subclasses, like NullPointerException) signal programming errors and don’t require declaration. Error (e.g. OutOfMemoryError) represents unrecoverable JVM problems.

What is autoboxing and what’s its main pitfall?

Autoboxing automatically converts between primitives and their wrapper types (intInteger). The pitfall is silent performance cost in loops and the caching trap: Integer caches values in -128..127, so == may appear to work for small values but fail for larger ones. Always use .equals() to compare wrappers.

What is the difference between an interface and an abstract class?

An abstract class can hold state (fields), constructors, and a mix of concrete and abstract methods; a class extends only one. An interface defines a contract, supports multiple inheritance of type, and (since Java 8) allows default and static methods. Reach for an interface to model capability, an abstract class to share implementation among closely related types.

OOP

What are the four pillars of OOP?

Encapsulation (hiding internal state behind a public API), Inheritance (deriving types to reuse and specialize behavior), Polymorphism (one interface, many implementations — resolved at runtime via dynamic dispatch), and Abstraction (modeling the essential and hiding the incidental). Together they manage complexity in large systems.

What is the difference between method overloading and overriding?

Overloading means multiple methods with the same name but different parameter lists in the same class — resolved at compile time (static binding). Overriding means a subclass replaces a superclass method with the same signature — resolved at runtime (dynamic binding). Overriding is the mechanism behind polymorphism.

What is the contract between equals() and hashCode()?

If two objects are equals(), they must return the same hashCode(). The reverse isn’t required (collisions are allowed). Breaking this contract corrupts hash-based collections: equal objects could land in different buckets and “disappear” from a HashMap or HashSet. Always override both together.

What is composition over inheritance?

A design principle favoring building behavior by holding references to other objects (has-a) over deep inheritance hierarchies (is-a). Composition is more flexible, avoids fragile base-class coupling, and lets you swap collaborators at runtime. Inheritance is best reserved for true subtype relationships.

Collections

How does a HashMap work internally?

A HashMap is an array of buckets. A key’s hashCode() is spread (mixed) and masked to an index. Entries with the same index form a chain — a linked list that converts to a balanced red-black tree once it exceeds 8 nodes (Java 8+) to bound worst-case lookups at O(log n). On get, it finds the bucket, then uses equals() to locate the exact key. The table resizes (doubles) when size exceeds capacity × load factor (default 0.75).

What is the difference between ArrayList and LinkedList?

ArrayList is backed by a resizable array: O(1) random access and cache-friendly iteration, but O(n) inserts/removes in the middle. LinkedList is a doubly-linked list: O(1) insert/remove at the ends given a node, but O(n) indexing and poor cache locality. In practice ArrayList wins for almost all workloads.

What is the difference between fail-fast and fail-safe iterators?

Fail-fast iterators (on ArrayList, HashMap) throw ConcurrentModificationException if the collection is structurally modified during iteration, detected via a modCount check. Fail-safe iterators (on CopyOnWriteArrayList, ConcurrentHashMap) operate over a snapshot or tolerate concurrent changes without throwing, at the cost of possibly not reflecting the latest state.

What is the difference between HashMap, LinkedHashMap, and TreeMap?

HashMap gives O(1) average access with no ordering. LinkedHashMap maintains insertion (or access) order via a linked list at slightly higher cost. TreeMap is a red-black tree keeping keys sorted, with O(log n) operations and navigation methods like floorKey/ceilingKey.

How do you make a collection thread-safe?

Options, from worst to best: Collections.synchronizedList(...) wraps with coarse locking (you still must synchronize manually during iteration); the legacy Vector/Hashtable (avoid); or purpose-built concurrent collections like ConcurrentHashMap, CopyOnWriteArrayList, and ConcurrentLinkedQueue, which scale far better under contention.

Concurrency

What is the difference between volatile and synchronized?

volatile guarantees visibility — reads/writes go to main memory, not a thread-local cache — and prevents reordering, but provides no atomicity for compound actions like i++. synchronized provides both mutual exclusion (atomicity) and visibility by acquiring a monitor lock. Use volatile for a simple flag; use synchronized (or java.util.concurrent locks/atomics) when operations must be atomic.

What is the difference between Runnable and Callable?

Runnable.run() returns nothing and can’t throw checked exceptions. Callable.call() returns a value and may throw checked exceptions. You submit a Callable to an ExecutorService and get back a Future to retrieve the result or propagated exception.

What is a deadlock and how do you prevent it?

A deadlock occurs when two or more threads each hold a lock the other needs, so none can proceed. Prevent it by acquiring locks in a consistent global order, using tryLock with timeouts, holding locks for the shortest time possible, and preferring higher-level concurrency utilities over manual locking.

What does the ExecutorService give you over raw threads?

It decouples task submission from thread management: a managed thread pool reuses threads (avoiding creation cost), bounds concurrency, queues work, and returns Futures for results and cancellation. Creating a new Thread() per task doesn’t scale and gives you no lifecycle control.

What is the difference between wait()/notify() and a BlockingQueue?

wait()/notify() are low-level primitives on Object that require holding the monitor and careful guarding against spurious wakeups in a loop. A BlockingQueue (e.g. ArrayBlockingQueue) encapsulates the producer-consumer handoff with put/take that block automatically — almost always the correct, less error-prone choice.

What is the happens-before relationship?

It’s the JVM memory-model guarantee that defines when one thread’s writes are visible to another. For example, unlocking a monitor happens-before any later lock of the same monitor, and a write to a volatile field happens-before any subsequent read of it. Correct concurrent code relies on establishing these ordering edges.

Streams & Java 8+

What is the difference between intermediate and terminal stream operations?

Intermediate operations (map, filter, sorted) are lazy — they return a new stream and do no work until a terminal operation runs. Terminal operations (collect, forEach, reduce, count) trigger execution and produce a result or side effect. A stream can be consumed only once.

What is the difference between map and flatMap?

map applies a one-to-one transform, producing one output per input. flatMap applies a one-to-many transform that returns a stream per element, then flattens all of them into a single stream — useful for un-nesting, e.g. turning a Stream<List<T>> into a Stream<T>.

What is the difference between Optional.orElse and orElseGet?

orElse(value) always evaluates its argument, even when the Optional is present. orElseGet(supplier) evaluates the supplier lazily only when the value is absent. Use orElseGet when the fallback is expensive or has side effects to avoid wasted computation.

What is a functional interface?

An interface with exactly one abstract method, optionally annotated @FunctionalInterface. It’s the target type for lambdas and method references. Examples include Function, Predicate, Supplier, Consumer, and Runnable.

When should you use parallel streams?

Only for large datasets with CPU-bound, stateless, side-effect-free operations and a cheaply splittable source (arrays, ArrayList). They use the common ForkJoinPool, so they can starve other parallel work and often perform worse than sequential streams for small or I/O-bound tasks. Always measure before adopting.

JVM / Memory

What is the difference between heap and stack memory?

The stack stores method frames: local variables, parameters, and references — it’s thread-local, fast, and automatically reclaimed when a method returns. The heap stores all objects and is shared across threads and managed by the garbage collector. A StackOverflowError comes from deep recursion; an OutOfMemoryError from heap exhaustion.

How does garbage collection work in Java?

The GC reclaims objects no longer reachable from GC roots (stack references, statics, etc.). Modern collectors are generational: most objects die young, so a fast minor GC sweeps the young generation, and survivors are promoted to the old generation collected less often. Collectors like G1 (default) and ZGC aim to minimize pause times.

What is a memory leak in Java, given there’s a garbage collector?

A leak happens when objects remain reachable but are never used again, so the GC can’t collect them. Common causes: unbounded caches or collections that grow forever, listeners/callbacks never unregistered, ThreadLocals not cleared in pooled threads, and lingering references in static fields.

What is the difference between strong, soft, weak, and phantom references?

A strong reference (the default) prevents collection. A SoftReference is collected only when memory is low — ideal for caches. A WeakReference is collected at the next GC once no strong references remain — used by WeakHashMap. A PhantomReference is enqueued after collection for managing post-finalization cleanup via a ReferenceQueue.

What is the role of the JIT compiler?

The JVM starts by interpreting bytecode, then the Just-In-Time compiler profiles execution and compiles hot methods to optimized native code at runtime. Optimizations include inlining, loop unrolling, and escape analysis. This is why a Java method gets faster after its first thousands of invocations warm up.

What is class loading and the role of class loaders?

Class loaders load .class bytecode into the JVM on demand, following a parent-delegation model: the bootstrap loader handles core JDK classes, the platform/extension loader handles JDK modules, and the application loader handles your classpath. Each delegates upward first, which prevents core classes from being overridden by user code.

Last updated June 1, 2026
Was this helpful?