Interview Prep — Java Track

Java Interview Questions

15 Java questions — == vs .equals(), the String pool, checked exceptions, the Integer cache, HashMap internals, and generics type erasure. The concepts backend and enterprise interviewers ask every time. Tagged for FAANG and startups.

CoreOOPAdvanced
30Total
6Core
4OOP
5Advanced
Progress saved locally. Check off questions — saves to your browser. No account needed.
0
/ 15 Done
01What is the difference between == and .equals()?Java▾
== compares references (do two variables point to the same object). .equals() compares logical equality (do two objects have the same content, as defined by the class).
java
String a = new String("hi");
String b = new String("hi");
a == b;        // false — different objects
a.equals(b);  // true  — same content
Rule: always use .equals() for objects (especially Strings). If you override equals(), you must also override hashCode() to keep the contract for hash-based collections.
02Why are Strings immutable, and what is the String pool?Java▾
String is immutable — once created, its contents never change; every "modification" produces a new object. Benefits: thread safety, safe use as HashMap keys, and security (paths, connection strings can't be mutated after validation).

The String pool is a special heap region where string literals are interned and reused. Two identical literals point to the same pooled object.
java
String a = "hi";         // pooled
String b = "hi";         // same pooled object
a == b;                   // true
String c = new String("hi");  // forced new heap object
a == c;                   // false
Tip: use StringBuilder for heavy concatenation in loops — building with + creates a new String each iteration (O(n²)).
03Checked vs unchecked exceptions?Java▾
CheckedUnchecked (RuntimeException)
Compiler enforcesYes — declare or handleNo
RepresentsRecoverable external problemsProgramming bugs
ExamplesIOException, SQLExceptionNullPointerException, ArrayIndexOutOfBounds
Checked exceptions are unique to Java. The compiler forces you to handle or declare them with throws, which some praise for robustness and others find noisy.
04What is autoboxing, and what is the Integer cache trap?Java▾
Autoboxing is the automatic conversion between a primitive (int) and its wrapper (Integer). Java caches Integer objects for values −128 to 127, so == on boxed values in that range can accidentally look correct.
java
Integer a = 127, b = 127;
a == b;   // true  — cached, same object
Integer c = 200, d = 200;
c == d;   // false — outside cache, different objects
c.equals(d);  // true — always use equals for wrappers
05Difference between final, finally, and finalize?Java▾
  • final — a modifier: a final variable can't be reassigned, a final method can't be overridden, a final class can't be extended.
  • finally — a block after try/catch that always runs, used for cleanup.
  • finalize() — a deprecated Object method the GC once called before reclaiming an object. Do not rely on it; use try-with-resources instead.
06Difference between ArrayList and LinkedList?Java▾
ArrayListLinkedList
BackingResizable arrayDoubly-linked nodes
Random accessO(1)O(n)
Insert/remove at endsO(1) amortized (end)O(1)
Insert in middleO(n) shiftO(1) once located
MemoryCompactHigher (node pointers)
In practice: ArrayList is the right default almost always — cache-friendly and fast random access. LinkedList rarely wins outside niche queue/deque cases.
07What does the JVM actually do, and how do JDK, JRE and JVM relate?Java▾

javac compiles source to platform-independent bytecode in a .class file. The JVM loads that bytecode, verifies it, and executes it — interpreting at first, then JIT-compiling hot methods to native code.

JVM runs bytecode. JRE = JVM plus the standard library, enough to run an application. JDK = JRE plus the tools to build one (javac, jar, javadoc).

"Write once, run anywhere" is the bytecode, not the source: the same .class file runs on any JVM, and the JIT specialises it for the machine it finds itself on.
08Why can a Java method never swap its caller’s two variables?Java▾

Java is pass-by-value everywhere. For an object, the value copied is the reference — so the method can mutate what the reference points at, but reassigning the parameter only rebinds the local copy.

void swap(int[] a, int[] b) { int[] t = a; a = b; b = t; }   // no effect on the caller
void fill(int[] a)            { a[0] = 42; }                  // caller sees this

That is why the standard idiom is to return a new value, or to mutate an object the caller already holds.

09String concatenation in a loop — why is it a problem, and what do you use instead?Java▾

String is immutable, so s += x allocates a new string and copies the whole contents each time. In a loop over n items that is O(n²) copying.

StringBuilder sb = new StringBuilder();
for (String part : parts) sb.append(part);
String out = sb.toString();

StringBuilder keeps a growable char buffer and appends in amortised O(1). StringBuffer is the synchronised version — only worth the lock if the builder is genuinely shared across threads. Outside a loop, plain + is fine: the compiler rewrites it to a builder for you.

10What is the contract between equals and hashCode?Java▾

If two objects are equal, they must return the same hash code. The converse is not required — unequal objects may collide.

Override one without the other and hash-based collections break silently: an object put into a HashMap becomes unfindable, because lookup goes to a different bucket than insertion did.

Keep the fields used in equals and hashCode identical, and keep them effectively immutable — mutating a key after insertion strands it in the wrong bucket.

11When would you choose HashMap, LinkedHashMap or TreeMap?Java▾

HashMap: O(1) average lookup, no order guarantee — the default. LinkedHashMap: same cost plus a linked list keeping insertion order (or access order, which makes an LRU cache almost free). TreeMap: a red-black tree, O(log n), keys always sorted and range queries available.

Pick on the guarantee you need, not on speed. If you never iterate, HashMap. If iteration order must be predictable, LinkedHashMap. If you need "all keys between A and B", TreeMap.
12Interface vs abstract class — when do you use each?Java▾
InterfaceAbstract Class
Multiple inheritanceYes (implement many)No (extend one)
State (fields)Only constantsInstance fields allowed
ConstructorsNoYes
Method bodiesdefault/static onlyConcrete + abstract
Use an interface for a capability many unrelated types can share (Comparable, Runnable). Use an abstract class as a partial base for closely related types that share state and code.
13Method overloading vs overriding?Java▾
  • Overloading — same method name, different parameter lists, in the same class. Resolved at compile time (static polymorphism).
  • Overriding — a subclass provides a new implementation of a parent method with the same signature. Resolved at runtime (dynamic dispatch).
Note: overloads can't differ by return type alone. Always use @Override when overriding — the compiler then catches signature typos.
14What are records, and how do they differ from a class?Java▾
A record (Java 16+) is an immutable data carrier. One line generates the constructor, accessors, equals(), hashCode(), and toString().
java
record Point(int x, int y) {}

Point p = new Point(3, 4);
p.x();                       // 3 — accessor
p.equals(new Point(3, 4)); // true — value equality, free
Records are implicitly final, their fields are final, and they can't extend a class (they already extend Record).
15Explain the four access modifiers.Java▾
ModifierClassPackageSubclassWorld
private✓✗✗✗
(default)✓✓✗✗
protected✓✓✓✗
public✓✓✓✓
Default (no keyword) is "package-private". Encapsulation favours the tightest modifier that still works.
16What is composition over inheritance, and when does inheritance still win?Java▾

Inheritance couples a subclass to its parent’s implementation: a change upstream can break every descendant, and you get the whole interface whether it fits or not. Composition holds a collaborator as a field and delegates, so you expose only what you mean to.

Inheritance earns its place when the relationship is genuinely is-a and the base is designed for extension — documented hooks, or an abstract class that exists precisely to be subclassed.

The test: would a reader be surprised if the subclass were used wherever the base is expected? If yes, that is composition wearing an inheritance costume.
17What does the final keyword mean on a class, a method and a field?Java▾

On a class, it cannot be subclassed. On a method, it cannot be overridden. On a field, it must be assigned exactly once and cannot be reassigned after.

final on a reference field freezes the reference, not the object. final List<String> xs can still have items added — use List.copyOf for an unmodifiable one.

A final field also carries a memory-model guarantee: once a constructor completes, other threads see the fully-initialised value without extra synchronisation.

18Why is the singleton pattern criticised, and what is the safest way to write one in Java?Java▾

It is global mutable state: it hides dependencies from a class’s signature, makes tests order-dependent, and is awkward to substitute. Prefer passing the instance in.

public enum Config {
  INSTANCE;
  private final Properties props = load();
  public String get(String k) { return props.getProperty(k); }
}

If you do need one, an enum is the safest form — the JVM guarantees a single instance, initialisation is thread-safe, and it is immune to the serialisation and reflection tricks that defeat a lazily-built holder.

19What is the Liskov Substitution Principle, in terms of a concrete bug?Java▾

A subclass must be usable anywhere its base is expected without changing correctness. Violations look like a subclass that strengthens what it demands or weakens what it promises.

class Rectangle { void setWidth(int w); void setHeight(int h); }
class Square extends Rectangle { /* setWidth also sets height */ }

// caller: r.setWidth(5); r.setHeight(4); assert r.area() == 20;
// holds for Rectangle, fails for Square

The fix is not a cleverer Square: it is recognising that an immutable Shape with an area() is the right model, and mutable-rectangle is not a supertype of square.

20What problem do default methods on interfaces solve, and what do they cost?Java▾

They let an interface gain a method without breaking every existing implementer — which is how Collection.stream() was added to interfaces implemented across the whole ecosystem.

The cost is a diamond: implement two interfaces that both default the same signature and the code will not compile until you override it explicitly and pick, with Iface.super.method().

They are for evolution, not for shipping behaviour. State still cannot live on an interface, so anything needing fields belongs in an abstract class.

21How does a HashMap work internally?Java▾
A HashMap is an array of buckets. A key's hashCode() (further spread by an internal hash) selects a bucket index. Collisions in a bucket form a linked list, which converts to a balanced tree (Java 8+) once it exceeds 8 entries, improving worst-case lookup from O(n) to O(log n).
  • Load factor default 0.75 — at 75% full it resizes (doubles) and rehashes.
  • Average get/put: O(1); worst case with tree bins: O(log n).
Contract: keys must implement hashCode() and equals() consistently. Mutating a key after insertion corrupts lookups.
22Comparable vs Comparator?Java▾
  • Comparable<T> — the class's natural ordering, via compareTo(). One ordering per class.
  • Comparator<T> — an external, swappable ordering, via compare(). Many per class.
java
people.sort(Comparator.comparing(Person::getAge)
    .thenComparing(Person::getName));   // multi-key sort
23What is generics type erasure?Java▾
Java generics exist only at compile time. The compiler checks types, then erases them — List<String> and List<Integer> are both just List at runtime. This preserved backward compatibility with pre-generics code.

Consequences:
  • You can't do new T() or new T[].
  • instanceof List<String> is illegal.
  • Overloads that differ only by generic parameter clash (same erased signature).
Contrast: C++ templates and C# generics are reified (types survive to runtime). Java's erasure is a common interview "gotcha".
24How does Java garbage collection work?Java▾
The JVM automatically reclaims objects no longer reachable from GC roots (stack references, statics). The heap is generational:
  • Young generation (Eden + survivor spaces) — new objects; frequent, fast minor GCs.
  • Old generation — long-lived objects; less frequent, costlier major GCs.
Modern collectors (G1, ZGC, Shenandoah) aim for low pause times. You cannot force GC — System.gc() is only a hint.
25What is a fail-fast iterator?Java▾
A fail-fast iterator throws ConcurrentModificationException if the collection is structurally modified during iteration (except through the iterator's own remove()). It detects modification via an internal modCount.
java
for (String s : list) {
    if (s.isEmpty()) list.remove(s);  // throws CME
}
// Fix: use iterator.remove() or removeIf(String::isEmpty)
Concurrent collections like CopyOnWriteArrayList are fail-safe — they iterate over a snapshot and never throw.
26Why is synchronized not enough, and what does volatile actually guarantee?Java▾

volatile guarantees visibility and ordering: a write is published to other threads and cannot be reordered across the access. It does not give atomicity — count++ on a volatile is still read-modify-write and still races.

volatile boolean running = true;   // correct: a flag one thread sets, another reads
volatile int  count   = 0;
count++;                           // wrong: use AtomicInteger

synchronized gives both atomicity and visibility, at the cost of blocking. For a single counter, AtomicInteger is a compare-and-swap loop with neither lock nor race.

27What does a Java Stream give you that a loop does not — and when is a loop better?Java▾

Streams are lazy and composable: intermediate operations build a pipeline that runs once on terminal invocation, so filter().map().findFirst() touches only the elements it needs. They also express intent — the reader sees "filter then map", not index bookkeeping.

A loop is better when you need to break out on a condition with side effects, when the body mutates several things at once, or when the stream version needs a comment to explain it. parallelStream() in particular is a trap: it costs a fork-join dispatch that only pays off on large, CPU-bound, side-effect-free work.

Streams are single-use: consuming one twice throws IllegalStateException.

28What is Optional for, and how is it misused?Java▾

It is a return type that makes "there might be no value" part of the signature, so the caller cannot forget the empty case the way a bare null lets them.

Optional<User> findByEmail(String email);         // good: absence is expected
user.getName().orElse("anonymous");                // good

void setUser(Optional<User> u);                    // misuse: parameters
private Optional<String> name;                     // misuse: fields

Do not use it for parameters or fields — it is not serialisable, adds an allocation, and a null Optional is the worst of both worlds. And never call get() without isPresent(); that is just a null pointer with extra steps.

29Explain the JVM memory areas and where OutOfMemoryError comes from.Java▾

Heap holds objects and is what the garbage collector manages. Stack is per-thread, holding frames, locals and return addresses. Metaspace holds class metadata (native memory since Java 8, replacing PermGen).

OutOfMemoryError: Java heap space means live objects exceed the heap — usually a leak such as an ever-growing static collection, not a genuinely large working set. StackOverflowError is unbounded recursion. Metaspace exhaustion usually means classloader leaks, which is a classic in redeployed application servers.

Take a heap dump on OOM (-XX:+HeapDumpOnOutOfMemoryError) and look at dominators. The leak is nearly always one collection that nothing ever removes from.
30What is the executor framework, and why not just create threads?Java▾

A thread is an expensive OS resource; creating one per task means unbounded thread counts under load and a scheduler that thrashes. An ExecutorService owns a bounded pool and a queue, so concurrency stays capped and tasks queue instead of overwhelming the machine.

ExecutorService pool = Executors.newFixedThreadPool(8);
Future<Report> f = pool.submit(() -> build(id));
Report r = f.get();          // blocks until ready
pool.shutdown();

Size the pool to the work: roughly the core count for CPU-bound tasks, higher for I/O-bound ones that spend their time blocked. Virtual threads (Java 21) change the calculus for blocking I/O — they are cheap enough to create per task.

No questions match your search.