25 questions · Updated
Java Interview Questions and Answers
Java interviews combine language fundamentals with questions about the JVM and the collections framework, and increasingly about modern Java features. Interviewers still ask about the equals and hashCode contract, how HashMap works internally, checked versus unchecked exceptions and String immutability, but they also expect you to know records, sealed classes, pattern matching for switch and virtual threads, all standard since Java 21, the current long-term-support baseline in most companies alongside Java 25. This page collects 25 Java interview questions and answers in beginner, intermediate and advanced groups, plus a set of Java coding exercises. Advanced questions cover the Java memory model, garbage collectors such as G1 and ZGC, concurrency utilities and the trade-offs of virtual threads. Answers are short enough to give verbally, with code where it clarifies the point. If the role is Spring-based, expect framework questions as well — dependency injection, transactions and bean scopes — but solid core Java answers come first. Work through each level, answer aloud, and open the answer to check.
Beginner Java interview questions
1.What is the difference between the JDK, JRE and JVM?
The JVM executes bytecode and provides memory management and JIT compilation. The JRE historically bundled the JVM with the core class libraries needed to run programs. The JDK adds development tools such as javac, jshell and jlink. Since Java 11, vendors ship the JDK as the main distribution, and jlink can build a trimmed custom runtime for deployment.
2.Is Java pass-by-value or pass-by-reference?
Java is always pass-by-value. For primitives the value is copied. For objects, the value of the reference is copied, so a method can mutate the object the reference points to, but reassigning the parameter does not affect the caller's variable.
3.Why are Strings immutable, and what is the string pool?
Immutability makes strings safe to share between threads, safe as HashMap keys (their hash code can be cached), and safe for security-sensitive values like class names and file paths. String literals are interned in a pool so identical literals share one object. That is why == can appear to work for literals, but you must compare content with equals. Use StringBuilder for building strings in loops.
4.What is the equals and hashCode contract?
If two objects are equal according to equals, they must return the same hashCode. Unequal objects may share a hash code, but fewer collisions mean better performance. Overriding equals without hashCode breaks HashMap and HashSet, because equal keys land in different buckets. Records generate both correctly from their components.
5.What are the four pillars of OOP in Java?
Encapsulation hides state behind methods and access modifiers. Inheritance lets a class extend another and reuse its behaviour. Polymorphism lets a reference of a supertype call the overridden method of the actual object at runtime. Abstraction exposes what an object does through interfaces and abstract classes while hiding how. Interviewers often follow up by asking you to prefer composition over inheritance and explain why.
6.What is the difference between an abstract class and an interface?
A class can implement many interfaces but extend only one class. Interfaces can have abstract, default, static and private methods but no instance state, while abstract classes can have fields, constructors and any access level. Use interfaces to define capabilities and abstract classes to share state and implementation among closely related classes.
7.What is the difference between checked and unchecked exceptions?
Checked exceptions extend Exception (but not RuntimeException) and must be declared or caught, representing recoverable conditions such as IOException. Unchecked exceptions extend RuntimeException and usually indicate programming errors, such as NullPointerException or IllegalArgumentException. Errors such as OutOfMemoryError should generally not be caught. Use try-with-resources to close AutoCloseable resources reliably.
Intermediate Java interview questions
8.How does HashMap work internally?
HashMap stores entries in an array of buckets. It computes the key's hashCode, spreads the high bits, and uses it to pick a bucket; collisions are stored in a linked list that turns into a balanced tree when a bucket grows past a threshold of 8 entries (and the table is large enough). When size exceeds capacity times the load factor (0.75 by default), the table doubles and entries are redistributed. HashMap is not thread-safe; use ConcurrentHashMap for concurrent access.
9.ArrayList vs LinkedList: which should you use?
ArrayList is backed by a resizable array, giving O(1) random access and cache-friendly iteration; inserting in the middle is O(n). LinkedList is a doubly linked list with O(1) insertion at known nodes but O(n) access and poor memory locality. In practice ArrayList wins almost always, and ArrayDeque is the better choice for queues and stacks.
10.What are records?
Records (standard since Java 16) are concise, immutable data carriers. Declaring record Point(int x, int y) generates private final fields, a canonical constructor, accessors, equals, hashCode and toString. You can add a compact constructor for validation and extra methods, but not instance fields beyond the components. They work well with pattern matching.
record Money(long cents, String currency) {
Money {
if (cents < 0) throw new IllegalArgumentException("negative");
}
}11.What are sealed classes and pattern matching for switch?
A sealed class or interface lists exactly which classes may extend it with permits. Combined with records and pattern matching for switch (standard in Java 21), the compiler can check that a switch over a sealed hierarchy handles every case, with no default needed. Record patterns can destructure components inline.
sealed interface Shape permits Circle, Square {}
record Circle(double r) implements Shape {}
record Square(double side) implements Shape {}
double area(Shape s) {
return switch (s) {
case Circle(double r) -> Math.PI * r * r;
case Square(double side) -> side * side;
};
}12.How do Streams work, and what is the difference between intermediate and terminal operations?
A stream is a lazy pipeline over a data source. Intermediate operations such as map, filter and sorted return a new stream and do nothing until a terminal operation such as collect, reduce, forEach or count runs. Streams are single-use. Avoid side effects in lambdas, and be careful with parallel streams, which help only for large, CPU-bound, easily split workloads.
Map<String, Long> countByDept = employees.stream()
.filter(e -> e.salary() > 50_000)
.collect(Collectors.groupingBy(Employee::dept, Collectors.counting()));13.What is Optional and how should it be used?
Optional is a container that may or may not hold a value, intended mainly as a method return type to signal that a result might be absent. Use map, flatMap, orElse, orElseGet and orElseThrow instead of isPresent followed by get. Do not use it for fields, method parameters or collections, where it adds overhead without clarity.
14.What is the difference between Comparable and Comparator?
Comparable defines a class's natural ordering through compareTo, implemented by the class itself. Comparator defines an external ordering and can be composed, for example Comparator.comparing(Person::lastName).thenComparing(Person::firstName).reversed(). Use Comparator when you need several orderings or cannot modify the class.
15.What does the volatile keyword do?
volatile guarantees visibility: a write to a volatile field by one thread is visible to subsequent reads by other threads, and it prevents certain reorderings through happens-before rules. It does not make compound operations such as count++ atomic. For that, use AtomicInteger, LongAdder or synchronization.
Advanced Java interview questions
16.What are virtual threads?
Virtual threads, standard since Java 21, are lightweight threads scheduled by the JVM onto a small pool of platform (OS) threads. When a virtual thread blocks on I/O, it unmounts and frees its carrier, so you can run millions of them and write simple blocking code that scales like asynchronous code. They help I/O-bound workloads, not CPU-bound ones. Do not pool them; create one per task.
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (var url : urls) executor.submit(() -> fetch(url));
}17.How is JVM memory organised?
The heap holds objects and is divided into generations for garbage collection (young and old in most collectors). Each thread has its own stack of frames holding local variables and references. Metaspace, in native memory, holds class metadata and replaced PermGen in Java 8. There are also code cache and direct buffer areas outside the heap.
18.Which garbage collectors should you know?
G1 is the default: a region-based, mostly concurrent collector balancing throughput and pause times. ZGC targets very low pause times, typically under a millisecond, independent of heap size, and is generational by default in recent JDKs. Parallel GC maximises throughput for batch jobs, and Serial GC suits small heaps. Tune with goals such as pause-time targets rather than many low-level flags.
19.What is the difference between synchronized and ReentrantLock?
synchronized is a built-in monitor lock that is released automatically when the block exits. ReentrantLock offers more: tryLock with timeouts, interruptible locking, fairness options and multiple Condition objects, but you must unlock in a finally block. Both are reentrant. Since JDK 24, synchronized no longer pins virtual threads to their carrier, which removed a common reason to switch.
20.How does ConcurrentHashMap achieve thread safety?
Modern ConcurrentHashMap uses CAS operations for inserting into empty buckets and locks only the first node of a bucket for updates, so different buckets can be written concurrently. Reads are generally lock-free. Use its atomic methods such as computeIfAbsent and merge rather than separate get and put calls, which would be a race condition.
Java coding interview questions
21.Count word frequencies in a string.
Split on whitespace, normalise case, and collect into a map with groupingBy and counting, or use merge in a loop. Clarify how to treat punctuation.
Map<String, Long> freq = Arrays.stream(text.toLowerCase().split("\\s+"))
.filter(w -> !w.isBlank())
.collect(Collectors.groupingBy(w -> w, Collectors.counting()));22.Reverse a linked list.
Walk the list keeping prev, current and next pointers, reversing each link as you go. This is O(n) time and O(1) extra space. A recursive version is shorter but uses O(n) stack space.
ListNode reverse(ListNode head) {
ListNode prev = null, curr = head;
while (curr != null) {
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}23.Check whether a string has balanced brackets.
Push opening brackets onto a stack and, on each closing bracket, pop and check it matches. The string is balanced if every pop matches and the stack is empty at the end. Use ArrayDeque rather than the legacy Stack class.
boolean balanced(String s) {
Deque<Character> stack = new ArrayDeque<>();
Map<Character, Character> pairs = Map.of(')', '(', ']', '[', '}', '{');
for (char c : s.toCharArray()) {
if (pairs.containsValue(c)) stack.push(c);
else if (pairs.containsKey(c) && (stack.isEmpty() || !stack.pop().equals(pairs.get(c)))) return false;
}
return stack.isEmpty();
}24.Implement a thread-safe lazy singleton.
The initialization-on-demand holder idiom relies on class loading being thread-safe: the instance is created when the holder class is first accessed, with no explicit locking. An enum with a single constant is the other recommended approach and also handles serialization.
public final class Config {
private Config() {}
private static class Holder {
static final Config INSTANCE = new Config();
}
public static Config getInstance() { return Holder.INSTANCE; }
}25.Find the first non-repeated character in a string.
A LinkedHashMap preserves insertion order, so count characters into it and return the first entry with a count of one. This is O(n) time.
Character firstUnique(String s) {
Map<Character, Integer> counts = new LinkedHashMap<>();
for (char c : s.toCharArray()) counts.merge(c, 1, Integer::sum);
return counts.entrySet().stream()
.filter(e -> e.getValue() == 1)
.map(Map.Entry::getKey)
.findFirst().orElse(null);
}Preparing for a Java interview
Common questions about getting ready
How do I prepare for a Java interview?
Review core concepts first: OOP, equals and hashCode, String immutability, exceptions and the collections framework, including how HashMap works. Then cover Java 8+ streams and lambdas, Java 21 features such as records, sealed classes, pattern matching and virtual threads, and finish with concurrency and JVM memory basics. Practise coding problems in Java to get fluent with the collections API.
Which Java version should I know for interviews?
Java 21 is the most common baseline, with Java 17 still widespread and Java 25 the newest LTS release. Know what changed since Java 8, since many interviewers ask what you use from modern Java.
Are Spring questions part of Java interviews?
For back-end roles, usually yes: dependency injection, bean scopes, @Transactional behaviour, Spring Boot auto-configuration and REST controllers are common. Core Java questions still come first.
How important are JVM and GC questions?
For senior roles they are expected. Be able to explain heap generations, how G1 and ZGC differ, and how you would diagnose a memory leak or long pauses using heap dumps, GC logs and tools such as JDK Flight Recorder.

