Experienced Java interviews in 2026 test four things: how collections and concurrency work under the hood, fluent Java 8 streams and lambdas, the newer features from Java 17, 21 and 25, and how you debug real production problems. Below are 35 questions in that order, each with a short answer you can say out loud.
What changed in Java interviews
Java ships a long-term support (LTS) release every two years. The ones interviewers care about are Java 8 (March 2014), 11 (September 2018), 17 (September 2021), 21 (September 2023) and 25 (16 September 2025, per the OpenJDK announcement).
Many service companies and banks still run Java 8 or 11, so streams and lambdas remain standard questions. Product companies and newer projects run 17 or 21, so expect records, sealed classes and virtual threads too. Check the job description for the version. If it says "Java 17+", prepare sections 4 and 5 below with extra care.
1. Core Java and OOP
Why is String immutable in Java?
Four reasons. String literals live in a shared pool, so one change would affect every reference to it. Strings carry security-sensitive values such as class names, file paths and connection URLs, which must not change after a check. Immutable objects are thread-safe without locks. And String caches its hashCode, which makes it a fast, reliable HashMap key.
What is the equals and hashCode contract, and what breaks if you ignore it?
If two objects are equal by equals(), they must return the same hashCode(). The reverse need not hold. If you override equals() but not hashCode(), two equal objects land in different HashMap buckets: map.get() returns null for a key you just put in, and a HashSet keeps "duplicates". Always override both, from the same fields, or use a record.
How do you design an immutable class?
Make the class final, every field private final, and add no setters. Copy mutable inputs in the constructor, and return copies or unmodifiable views from getters. A common miss is a List or Date field: without a defensive copy, the caller can still change your object's state. A record does the first part for you, but its immutability is shallow. A record holding an ArrayList is still mutable inside.
Abstract class or interface, now that interfaces have default methods?
Since Java 8, interfaces can have default and static methods, and since Java 9, private methods. The difference that remains is state. An abstract class can hold instance fields and constructors and enforce a shared lifecycle, but a class can extend only one. Use an interface for a capability many unrelated classes share. Use an abstract class when subclasses share state and setup logic.
Checked or unchecked exceptions, and when do you write a custom one?
Checked exceptions force the caller to handle a recoverable condition, such as a missing file. Unchecked exceptions signal programming errors or conditions the caller can't fix. Most modern frameworks, Spring included, favour unchecked exceptions to keep signatures clean. Write a custom exception when callers need to tell your failure apart from others, for example PaymentDeclinedException, and always pass the original cause into the constructor.
2. Collections
How does HashMap work internally?
HashMap keeps an array of buckets. It spreads the key's hash (h ^ (h >>> 16)) and picks a bucket with (n - 1) & hash. Collisions form a linked list. Since Java 8, a bucket turns into a red-black tree once it holds 8 entries and the table has at least 64 buckets, and turns back into a list at 6. The default capacity is 16 with a 0.75 load factor, so the 13th entry triggers a resize that doubles the table.
HashMap vs ConcurrentHashMap vs Collections.synchronizedMap?
synchronizedMap puts one lock around every call, so threads queue up. Since Java 8, ConcurrentHashMap uses compare-and-swap to fill empty buckets and locks only the first node of a bucket on writes, so different buckets update in parallel. Reads take no locks. It rejects null keys and values, because null would be ambiguous in concurrent get() calls, and its iterators are weakly consistent instead of fail-fast.
What is the difference between fail-fast and fail-safe iterators?
Fail-fast iterators, used by ArrayList and HashMap, check a modification counter and throw ConcurrentModificationException if the collection changes during iteration, other than through the iterator's own remove(). CopyOnWriteArrayList iterates over a snapshot, so it never throws but may show stale data. ConcurrentHashMap is weakly consistent: it never throws and may or may not show recent changes.
ArrayList or LinkedList?
ArrayList, in almost every real case. It gives O(1) random access and stores elements next to each other in memory, which the CPU cache loves. LinkedList only wins when you add or remove at both ends often, and ArrayDeque beats it there too. A good answer adds that LinkedList's per-node object overhead makes it slower than its big-O suggests.
Comparable or Comparator?
Comparable defines a class's single natural order through compareTo(). Comparator defines any number of external orders, written as Comparator.comparing(Employee::getSalary).thenComparing(Employee::getName). Mention that compareTo() should be consistent with equals(). Otherwise a TreeSet can drop elements it sees as equal even though equals() says they are different.
3. Concurrency
volatile vs synchronized vs AtomicInteger?
volatile guarantees visibility: every thread sees the latest write. It does not make compound actions atomic, so count++ on a volatile field still loses updates. synchronized gives both visibility and mutual exclusion, at the cost of blocking. AtomicInteger uses compare-and-swap for lock-free atomic updates of one variable. For counters under heavy contention, LongAdder beats AtomicLong.
What is happens-before?
It is the Java Memory Model rule that decides when one thread's write is guaranteed to be visible to another. Key edges: unlocking a monitor happens before the next lock on it, a volatile write happens before later reads of that variable, Thread.start() happens before anything in the new thread, and everything in a thread happens before another thread's successful join() on it. Without such an edge, the JVM and CPU may reorder.
How do you size a thread pool?
For CPU-bound work, about the number of cores. For blocking I/O, more threads, roughly cores × (1 + wait time / compute time). Then say why you avoid the Executors shortcuts in production: newFixedThreadPool uses an unbounded queue that can exhaust memory, and newCachedThreadPool can create unbounded threads. Build a ThreadPoolExecutor with a bounded queue and a rejection policy, or use virtual threads for blocking I/O on Java 21+.
thenApply vs thenCompose vs thenCombine in CompletableFuture?
thenApply transforms a result with a plain function, like map. thenCompose chains a call that itself returns a CompletableFuture, like flatMap, so you avoid a nested future. thenCombine joins two independent futures once both finish. Handle failures with exceptionally or handle. Also pass your own executor for blocking calls, because the async methods use the common ForkJoinPool by default.
How do you find and prevent a deadlock?
Find it with a thread dump (jcmd <pid> Thread.print or jstack). The JVM reports "Found one Java-level deadlock" and lists the threads and locks involved. Prevent it by always taking locks in one global order, keeping locked sections small, and using ReentrantLock.tryLock(timeout) where you can't control the order. Never call unknown code, such as listeners, while holding a lock.
Why can ThreadLocal leak memory, and what replaces it?
In a thread pool, threads live forever, so a ThreadLocal value set during one request stays attached to the thread and can leak into the next request. Always call remove() in a finally block. Java 25 finalised Scoped Values (JEP 506): immutable values bound for a defined scope, which also work well with virtual threads.
4. Java 8 essentials
How do streams work, and what is the difference between map and flatMap?
A stream is a lazy pipeline. Intermediate operations such as filter and map do nothing until a terminal operation such as collect or count runs, and elements flow through one at a time. map turns each element into exactly one result. flatMap turns each element into a stream and flattens them, for example a list of orders into all their line items. Never change the source collection inside a stream.
When would you avoid parallel streams?
Parallel streams run on the shared common ForkJoinPool. One slow or blocking task can starve every other parallel stream in the application. They help with large, CPU-heavy, easily split work on ArrayLists or arrays. They hurt with small data, I/O, LinkedLists, or order-dependent and stateful operations. Measure before and after, rather than assume.
How should Optional be used, and orElse vs orElseGet?
Use Optional as a return type for "may be absent", not as a field, method parameter or collection element. orElse(x) always evaluates x, even when a value is present. orElseGet(() -> x) only evaluates when the value is empty. This matters when the fallback is expensive or has side effects, such as a database call. Avoid calling get() without a check. Use orElseThrow().
Group employees by department and count them, with streams.
Map<String, Long> countByDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment, Collectors.counting()));Follow-ups to expect: the average salary per department (Collectors.averagingDouble), and a sorted result (pass TreeMap::new as the map factory).
java.time vs Date and Calendar?
Date and Calendar are mutable and not thread-safe, and SimpleDateFormat breaks when shared across threads. java.time types are immutable and thread-safe. Use Instant for machine timestamps, LocalDate and LocalDateTime for dates without a zone, and ZonedDateTime when the zone matters. A strong answer: store in UTC as Instant, and convert to the user's zone only for display.
5. Java 17, 21 and 25
What does a record give you, and where shouldn't you use one?
A record generates a canonical constructor, accessors, equals, hashCode and toString from its components. All fields are final, and it can implement interfaces but not extend a class. Validate input in a compact constructor. Use records for DTOs, API responses and value objects. Don't use them as JPA entities: Hibernate needs a no-argument constructor and mutable state.
How do sealed classes work with pattern matching?
A sealed class or interface lists exactly which classes may extend it: sealed interface Payment permits Card, Upi, Wallet. Since Java 21, a switch over a sealed type with type patterns needs no default branch. The compiler checks that every permitted type is covered. Add a new subtype and every incomplete switch fails to compile, which is the point.
double fee = switch (payment) {
case Card c -> c.amount() * 0.02;
case Upi u -> 0;
case Wallet w -> w.amount() * 0.01;
};What are virtual threads, and when do they not help?
Virtual threads (Java 21, JEP 444) are lightweight threads managed by the JVM. Thousands can block on I/O cheaply, because a blocked virtual thread releases its carrier platform thread. Create one per task. Never pool them. They do not speed up CPU-bound work. Until Java 24, a virtual thread blocking inside synchronized pinned its carrier. JEP 491 fixed that in Java 24, but native calls still pin. In Spring Boot 3.2+, enable them with spring.threads.virtual.enabled=true.
Which smaller modern features should you know?
Local-variable type inference with var (Java 10). Switch expressions with -> and yield (Java 14). Text blocks for multi-line strings (Java 15). Pattern matching for instanceof (Java 16). Sequenced collections in Java 21, which gave lists, deques and linked sets a shared getFirst(), getLast() and reversed().
What is new in Java 25?
Java 25 is the LTS release of 16 September 2025. Finalised language and library features include Scoped Values (JEP 506), module import declarations (JEP 511), compact source files and instance main methods (JEP 512), and flexible constructor bodies (JEP 513), which allow statements before super(...). On the JVM side, compact object headers (JEP 519) and generational Shenandoah (JEP 521) became product features. Most interviewers only ask what you would use: scoped values and flexible constructors are the practical ones.
6. JVM, memory and production debugging
Which garbage collector would you choose?
G1 has been the default since Java 9 and suits most services, balancing throughput and pause time. ZGC targets very low pauses on large heaps. Since Java 23 it runs in generational mode by default. Parallel GC suits batch jobs where only throughput matters. Then say how you would decide: measure pause times and throughput with GC logs or JFR under real load, don't pick by reputation.
How do you investigate an OutOfMemoryError?
First read the message. "Java heap space", "Metaspace" and "unable to create native thread" point to different causes. Run with -XX:+HeapDumpOnOutOfMemoryError, open the dump in Eclipse MAT, and check the dominator tree for what holds the memory. The usual suspects: unbounded caches or static maps, ThreadLocals in pools, listeners never removed, and, for Metaspace, classloader leaks after redeploys.
A service is at 100% CPU in production. What do you do?
Find the process, then the hot threads with top -H -p <pid>. Convert the thread ID to hex and match it to nid in a thread dump taken with jcmd <pid> Thread.print. Take three dumps a few seconds apart to separate a stuck loop from normal load. For a deeper view, use Java Flight Recorder or async-profiler. Common causes: a hot loop, regex backtracking, GC thrashing near the heap limit, or a busy spin wait.
7. Coding questions
Find the first non-repeated character in a string.
Optional<Character> first = s.chars()
.mapToObj(c -> (char) c)
.collect(Collectors.groupingBy(c -> c, LinkedHashMap::new, Collectors.counting()))
.entrySet().stream()
.filter(e -> e.getValue() == 1)
.map(Map.Entry::getKey)
.findFirst();The point interviewers look for is LinkedHashMap::new, which keeps insertion order. Mention the O(n) plain-loop version too, with an int[] count array, if they ask about performance.
Write a thread-safe singleton.
The simplest correct answer is an enum: enum Config { INSTANCE; }. It is thread-safe and safe against serialization and reflection attacks. If they want lazy loading, use the holder idiom: a private static nested class holding the instance, loaded on first access. If you write double-checked locking, the field must be volatile, or another thread can see a half-built object.
Build an LRU cache.
class LruCache<K, V> extends LinkedHashMap<K, V> {
private final int capacity;
LruCache(int capacity) { super(16, 0.75f, true); this.capacity = capacity; }
@Override protected boolean removeEldestEntry(Map.Entry<K, V> e) { return size() > capacity; }
}The third constructor argument, true, switches to access order. Interviewers may then ask for it without LinkedHashMap: a HashMap plus a doubly linked list gives O(1) get and put. For thread safety, wrap it with a lock, or use a library cache such as Caffeine.
8. Experience questions
Tell me about a production issue you fixed.
This is the question that separates 3 years from 8. Use the STAR method: the situation in one line, what you personally owned, the steps you took (logs, thread dumps, metrics, a rollback), and the outcome. Then add what you changed so it could not happen again. See STAR method examples for the structure.
How would you move a service from Java 8 to Java 21?
Upgrade build tools and dependencies first, since old Lombok, Mockito and bytecode libraries break on newer JDKs. Replace removed Java EE modules (JAXB, JAX-WS) with Jakarta dependencies. Run the full test suite on the new JDK in CI before switching production. Roll out behind a canary, compare GC and latency metrics, then adopt new features gradually. Mentioning jdeps to find internal API use is a strong signal.
What would you ask us?
Ask about their Java and Spring Boot versions, how they deploy, and what their last production incident was. It shows you think like an owner.
How to prepare in the last 48 hours
Read the job description and mark the Java version and frameworks.
Say the answers above out loud, timed under 90 seconds each. Reading is not the same as answering.
Write the three coding answers from memory, without an IDE.
Prepare two production stories in STAR form.
If Spring Boot is in the job description, also go through our Spring Boot interview questions.
You can practise these exact questions out loud with feedback in an AI mock interview. For live interviews, the InterviewPlan AI interview copilot can surface notes from your own prep on Zoom, Teams or Meet. Check your employer's rules on AI tools first.
