Topic 16.1
Core Java Interview Questions
In one line
A bank of the Core Java questions interviewers ask most, from the JVM and Strings to HashMap internals, generics, modern Java, concurrency and garbage collection, each with an interview-grade answer and a link to the topic that teaches it.
Think of it like this
A driving test isn't a quiz about the names of car parts. The examiner watches whether you check the mirror *before* you turn, and asks "why did you slow down there?". A Java interview is the same: the interviewer wants to see that you know why things work, not only what they are called.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Contract
- A rule a method must follow so that other code can rely on it, such as "equal objects must have equal hash codes".
- Bucket
- One slot in a HashMap's internal array. Keys whose hashes point to the same slot share the bucket.
- Collision
- Two different keys landing in the same bucket. The map must then compare them with equals.
- Fail-fast
- An iterator that throws an exception as soon as it notices the collection was changed behind its back, instead of giving wrong results.
- Lazy evaluation
- Work is described first and done only when a result is actually needed, as stream pipelines do.
- Happens-before
- The Java Memory Model's rule for when one thread is guaranteed to see what another thread wrote.
- GC root
- A starting point the garbage collector trusts is alive, such as a local variable on a thread's stack or a static field.
- Follow-up
- The next, deeper question an interviewer asks after your first answer, usually about the mechanism or a failure case.
Step by step
01Use the four-part answer shape
Definition, mechanism, example, trap. Practise it on any question. "What is autoboxing?" Definition: the compiler converts between int and Integer for you. Mechanism: Integer x = 5 compiles to Integer.valueOf(5), and int y = x compiles to x.intValue(). Example: list.add(5) on a List<Integer>. Trap: unboxing null throws NullPointerException, and Integer values outside -128 to 127 are separate objects, so == on them is unreliable.
The shape keeps you from rambling and shows the interviewer each layer of your understanding. Stop after the trap and let them choose the follow-up.
Integer x = 5; // compiled as Integer.valueOf(5)
int y = x; // compiled as x.intValue()
Integer none = null;
int z = none; // NullPointerException: unboxing null02The JVM family: JDK, JRE, JVM, bytecode
javac turns .java source into .class files of bytecode, instructions for a virtual machine rather than a real CPU. The JVM loads classes (Topic 14.1), verifies them, interprets the bytecode and compiles hot methods to machine code with the JIT (Topic 14.4). The JRE is a JVM plus the standard library; the JDK is the JRE plus tools such as javac, jshell, jcmd and jlink. Since Java 11, Oracle and most vendors ship only the JDK (Topic 0.2).
Follow-ups: "Is Java compiled or interpreted?" (both: compiled to bytecode, then interpreted and JIT-compiled at run time). "Why is Java platform independent?" (bytecode is the same everywhere; each platform has its own JVM).
03The equals and hashCode family
The contract (Topic 4.8): if a.equals(b) then a.hashCode() == b.hashCode(). The reverse is not required: unequal objects may share a hash code (a collision). equals must be reflexive, symmetric, transitive, consistent, and x.equals(null) must be false.
Why it matters: HashMap.get(key) first uses hashCode to pick a bucket, and only then calls equals on keys in that bucket. Override equals without hashCode and two equal keys usually land in different buckets, so the map never even compares them. The first runnable example below shows the lost key. Records (Topic 4.9) generate both methods correctly from their components.
04The HashMap family
A HashMap is an array of buckets (16 by default). put computes hash = h ^ (h >>> 16) from key.hashCode() to mix the high bits into the low bits, picks bucket hash & (length - 1), and either stores a new node there or walks the bucket comparing hash and equals, replacing the value of a matching key.
When the number of entries exceeds capacity * loadFactor (0.75 by default), the table doubles and entries are split between their old index and old index plus old capacity. Since Java 8, a bucket holding more than 8 nodes in a table of at least 64 slots is converted to a red-black tree, so even a flood of colliding keys costs O(log n) per lookup instead of O(n). One null key is allowed and lives in bucket 0. Topic 9.4 traces all of this step by step.
05The modern Java family
Know the headline features of each LTS release and be ready to write one line of each. Java 8: lambdas, streams, Optional, default methods, java.time. Java 11: var in lambda parameters, String.isBlank/strip/lines/repeat, the HttpClient, single-file java Main.java. Java 17: records, sealed classes, text blocks, pattern matching for instanceof, switch expressions. Java 21: virtual threads, record patterns, pattern matching for switch, sequenced collections. Topic 11.6 has the full map.
A common follow-up is "which feature changed how you write code most?". A good answer names one, shows before and after, and gives the reason, for example records replacing 40 lines of data-class boilerplate and removing a whole class of equals bugs.
06The concurrency and memory family
synchronized gives mutual exclusion (one thread at a time in the block) and visibility (changes made before releasing the lock are seen by the next thread to take it). volatile gives visibility and ordering for one variable but no mutual exclusion, so count++ on a volatile field is still a race (Topics 13.3 and 13.4).
For memory: each thread has its own stack of frames holding local variables and references; objects live on the shared heap; class metadata lives in Metaspace (native memory, since Java 8 replaced PermGen). The garbage collector finds live objects by tracing from GC roots, and everything unreachable is reclaimed. G1 has been the default collector since Java 9 (Topics 14.2 and 14.3).
07Practise out loud, then verify
Read a question, close the page, and answer it aloud in under two minutes using the four-part shape. Then open the model answer and the linked topic and note what you missed. Run the examples below and change them until the behaviour no longer surprises you.
Interviewers often ask you to *write* a small piece of code: an equals/hashCode pair, a thread-safe counter, a stream that groups and counts. The practice tasks below are exactly those.
Try it yourself
- 1
Fix the lost key
In "equals without hashCode loses the key", add
@Override public int hashCode() { return Objects.hash(x, y); }toPoint. Predict the second line before you run it. It should now printbad map finds: treasure. - 2
Make the compiler stop folding
In "String identity versus equality", declare
final String part = "ja";. Predicta == efirst. Afinallocal initialised with a constant is itself a constant variable, sopart + "va"is folded at compile time and the line printstrue. - 3
Watch laziness
In the stream example, delete the
limit(2)call. Predict how manyfilterlines print now (five), and how manymaplines (three). Then replace.toList()with nothing and only print"done": nofilterline prints at all, because without a terminal operation the pipeline never runs.
Code & diagrams
Point inherits Object.hashCode, so two equal Points almost always get different hashes and land in different buckets. The map never calls equals on them.
Expected output
equal keys? true
bad map finds: null
good map finds: treasure
same hash codes: true"ja" + "va" is a compile-time constant, so javac folds it into the literal "java" and it shares the pooled object. part + "va" is computed at run time and creates a new String.
Expected output
a == b true
a == c false
a.equals(c) true
a == d true
a == e false
a == "ja" + "va" trueThe for-each loop removed "bo" before the iterator noticed the change, which is why only ana and di remain after "cy" is removed. Topic 9.9 explains the modCount check behind this.
Expected output
for-each remove: ConcurrentModificationException
after it.remove: [ana, di]
after removeIf: [ana]Each element flows through the whole pipeline before the next one starts, and limit(2) stops the source early: 4 and 5 are never examined. Stream.toList() is Java 16.
Expected output
pipeline built, nothing ran yet
filter 1
map 1
filter 2
filter 3
map 3
result [10, 30]The class is final, so no subclass can add a field and break symmetry. The fields used in equals and hashCode are final, so the hash can't change while the object sits in a HashMap.
import java.util.Objects;
public final class Isbn {
private final String value;
public Isbn(String value) { this.value = Objects.requireNonNull(value); }
@Override
public boolean equals(Object o) {
if (this == o) return true; // fast path, also reflexive
if (!(o instanceof Isbn other)) return false; // handles null and other types
return value.equals(other.value); // same fields as hashCode
}
@Override
public int hashCode() {
return value.hashCode(); // equal objects, equal hashes
}
}Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Mutate a key after putting it in a HashSet
Use a mutable key class with equals/hashCode on a field, add it to a HashSet, change the field, then call set.contains(key) and set.remove(key).
Break #2
Unbox a null Integer
Write Map<String, Integer> stock = new HashMap<>(); int n = stock.get("pens");.
Myth vs fact
Myth
Interviewers want you to recite definitions.
Fact
They want the mechanism and the trap. A definition alone is the weakest possible answer; "here is what the JVM does, and here is how it bites you" is the strongest.
Myth
If two objects have the same hashCode, they are equal.
Fact
The contract only goes one way. Unequal objects can and do share hash codes; "Aa".hashCode() and "BB".hashCode() are both 2112.
Myth
Java passes objects by reference.
Fact
Java always passes by value. For an object, the value copied is the reference, so the method can change the object but can't make the caller's variable point somewhere else (Topic 3.3).
Myth
Calling System.gc() frees memory immediately.
Fact
It is only a request. The JVM may ignore it (and does with -XX:+DisableExplicitGC). Memory is reclaimed when the collector decides to run.
Interview problem
The problem
What happens when you call map.put(key, value)?
An interviewer asks: "Walk me through exactly what happens inside a HashMap when I call put, including collisions and resizing. Then tell me what changes if two threads call put at once."
You're given
- Cover hashing, the bucket index, collisions, equals, resizing and treeification.
- Give the cost of each step.
- Explain the concurrency problem and the fix.
The interviewer follows up
Why is the capacity always a power of two?
If you know you'll store 1,000 entries, how do you avoid resizes?
Why does ConcurrentHashMap reject null values?
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
HashMapstores the spread hash in each node, so resizing never callshashCodeagain, and lookups compare the storedintbefore callingequals. That is why a cheaphashCodeand an expensiveequalsstill perform well. - ▸
Treeification needs an ordering. For keys of the same class that implement
Comparable, the tree usescompareTo; otherwise it falls back to comparing class names and thenSystem.identityHashCodejust to break ties, and lookups may search both subtrees. ImplementingComparableon keys keeps tree buckets O(log n) under heavy collisions. - ▸
Since Java 9, compact strings store Latin-1 text in a
byte[]with one byte per character and switch to two bytes only when needed, roughly halving the memory of most Strings.String.hashCodecaches its result in a field, which is another reason immutability matters for map keys. - ▸
In senior interviews the question "what happens when…" is often a hook for production stories: an unbounded executor queue causing
OutOfMemoryError, a mutable map key leaking memory, aparallelStreamsaturating the common pool. One concrete incident, with its symptom and fix, is worth more than ten definitions.
Remember this
- 1
A strong answer has a shape. Start with a one-sentence definition, then explain the mechanism (what the compiler or JVM really does), then give a tiny example, then name the trap or trade-off. For "what is the String pool?" that is: a cache of String literals in the heap; the compiler puts literals in the class file's constant pool and the JVM shares one object per distinct literal;
"a" == "a"istruebutnew String("a") == "a"isfalse; so always compare withequals. - 2
Interviewers ask in families. One question about
HashMapleads tohashCode, then to collisions, then to treeification, then toConcurrentHashMap. Learn each family as a story, not a list of facts: if you understand that aHashMapis an array of buckets indexed by a hash, every follow-up is a consequence you can reason out on the spot. - 3
The most asked areas, in rough order: **
equalsandhashCode(Topic 4.8),HashMapinternals (Topic 9.4), String immutability and the pool (Topic 6.1), checked vs unchecked exceptions (Topic 7.3), overloading vs overriding (Topics 3.4 and 5.3), Java 8 features (Phase 10),synchronizedvsvolatile(Topics 13.3 and 13.4), thread pools (Topic 13.6) and garbage collection** (Topic 14.3). - 4
Many questions have a version in them. "Can an interface have method bodies?" is "no" before Java 8 and "yes,
defaultandstaticmethods" after it. "Where is the String pool?" is "PermGen" before Java 7 and "the heap" since. Saying which version a fact applies to is a quick way to sound senior, and the course'ssincetags (Topic 0.3) exist for exactly this. - 5
When you don't know, reason out loud from what you do know. "I don't remember the exact threshold, but I know long bucket chains make lookups linear, so Java must convert them to something faster past some length; I believe it's a red-black tree." That answer scores far better than silence, and it is how real engineers work.
- 6
Back answers with code you can run. The examples on this page prove the facts people get wrong: two equal keys that a
HashMapcan't find, two equal Strings that aren't==, a loop that throwsConcurrentModificationException, and a stream that does nothing until a terminal operation runs.
Explain it without notes
[JVM] What is the difference between the JDK, the JRE and the JVM?
[JVM] Is Java compiled or interpreted? Why is it called platform independent?
[JVM] What lives on the stack and what lives on the heap?
[JVM] How does class loading work, and what is parent delegation?
[String] Why is String immutable?
[String] What is the String pool, and when does == work on Strings?
[String] String vs StringBuilder vs StringBuffer: when do you use each?
[equals/hashCode] What is the equals and hashCode contract, and what breaks if you override only equals?
[equals/hashCode] Can you use a mutable object as a HashMap key?
[Collections] How does HashMap work internally?
[Collections] What changed in HashMap in Java 8, and why?
[Collections] ArrayList vs LinkedList: which is faster, and when?
[Collections] HashMap vs Hashtable vs ConcurrentHashMap?
[Collections] What are fail-fast and fail-safe iterators?
[Collections] Comparable vs Comparator?
[Collections] What is the difference between List.of, Arrays.asList and Collections.unmodifiableList?
[Exceptions] Checked vs unchecked exceptions, and Error vs Exception?
[Exceptions] Does finally always run? What if finally has a return?
[Exceptions] What does try-with-resources do, and what is a suppressed exception?
[Generics] What is type erasure, and what can't you do because of it?
[Generics] Explain wildcards and PECS.
[OOP] Overloading vs overriding? Can you override a static method?
[OOP] Abstract class vs interface, after Java 8?
[OOP] Is Java pass-by-value or pass-by-reference?
[OOP] final vs finally vs finalize?
[Java 8+] What were the main features of Java 8?
[Java 8+] Intermediate vs terminal stream operations, and map vs flatMap?
[Java 8+] How should Optional be used, and how should it not?
[Java 8+] What is a record, and what does the compiler generate?
[Java 8+] What are sealed classes, and how do they work with pattern matching?
[Concurrency] synchronized vs volatile?
[Concurrency] Runnable vs Callable, and why use an ExecutorService?
[Concurrency] What is a deadlock, and how do you prevent one?
[Concurrency] How does ConcurrentHashMap achieve thread safety?
[Concurrency] What are virtual threads, and when do they help?
[GC] How does garbage collection work, and which collector is the default?
[GC] Can a Java program have a memory leak?
[GC] OutOfMemoryError vs StackOverflowError?
Practice
Write a Money class with a long cents and a String currency that is safe to use as a HashMap key. Prove it with a map lookup using a separate but equal object.
Write a thread-safe hit counter that 4 threads increment 10,000 times each, and print the total. Use the simplest correct tool.
Given a list of words, use a stream to count how many words start with each letter, in alphabetical order of letter.
Show the difference between orElse and orElseGet with a method that prints when it is called.
Trade-offs
- ↔
Breadth vs depth: an interview rewards a deep, correct answer about a few areas more than shallow recall of everything. Go deep on HashMap, equals/hashCode, exceptions and concurrency first.
- ↔
Memorised numbers vs reasoning: thresholds such as 0.75 and 8 are good to know, but explaining *why* a threshold exists is worth more than the number itself.
- ↔
Mentioning versions shows depth, but only state a version you are sure of; a wrong version undermines an otherwise good answer.
Done when you can
Done when you can answer each question above aloud in under two minutes using definition, mechanism, example, trap.
Done when you can draw HashMap's put path from hashCode to treeification.
Done when you can write a correct equals and hashCode pair without help, and explain what breaks without it.
Done when you can name the headline features of Java 8, 11, 17 and 21.
Done when you can explain synchronized vs volatile and how ConcurrentHashMap avoids a global lock.
Done when you can describe how the GC decides an object is garbage and name the default collector.