All interview guides

Java Interview Questions and Answers

24 questions that come up in Java technical interviews, each with the answer and an explanation of why it is right.

Topics covered: equality, strings, collections, numbers, concurrency, streams, optional, arrays, immutability, resources, inheritance, references, operators.

Test yourself — 90 question bank

1. What does this print?

Advanced
java
Integer a = 127, b = 127;
Integer c = 128, d = 128;
System.out.println((a == b) + " " + (c == d));

Answer: true false

Integer caches boxed values from -128 to 127, so `a` and `b` are the same object and `==` is true. Above that range autoboxing creates new objects, so reference comparison fails. Always compare boxed types with `equals`.

Official documentation →

2. This class breaks when used in a HashSet — duplicates appear. Which line is the cause?

Advanced
java
1  class User {
2      final String email;
3      User(String e) { this.email = e; }
4      @Override public boolean equals(Object o) {
5          return o instanceof User u && u.email.equals(email);
6      }
7  }

Answer: The class overrides equals but not hashCode

HashSet finds the bucket by hashCode first and only then calls equals. Two equal Users inherit different identity hash codes, land in different buckets, and are never compared — so both are stored. The contract is absolute: if you override equals you must override hashCode.

Official documentation →

3. What does this print?

Advanced
java
String a = "hi";
String b = "hi";
String c = new String("hi");
System.out.println((a == b) + " " + (a == c) + " " + a.equals(c));

Answer: true false true

String literals are interned, so `a` and `b` reference the same pooled object. `new String(...)` forces a distinct object, so `==` fails while `equals` compares contents and succeeds. Calling `c.intern()` would return the pooled instance.

Official documentation →

4. What does this print?

Advanced
java
List<Integer> list = new ArrayList<>(List.of(1, 2, 3));
list.remove(1);
System.out.println(list);

Answer: [1, 3]

`remove(int)` removes by index and `remove(Object)` removes by value — the int overload wins here, so index 1 (the value 2) is dropped. To remove the *value* 1 you must box it: `list.remove(Integer.valueOf(1))`. A genuinely nasty overload trap.

Official documentation →

5. This throws ConcurrentModificationException. Which line is responsible?

Advanced
java
1  List<String> items = new ArrayList<>(List.of("a", "b", "c"));
2  for (String s : items) {
3      if (s.equals("b")) {
4          items.remove(s);
5      }
6  }

Answer: Line 4 — structurally modifying the list during iteration invalidates the iterator

The iterator tracks a modification count and fails fast when the list changes underneath it. Use `Iterator.remove()`, or `items.removeIf(s -> s.equals("b"))`, which handles the bookkeeping correctly.

Official documentation →

6. What does this print?

Advanced
java
System.out.println(0.1 + 0.2 == 0.3);
System.out.println(new BigDecimal("0.1").add(new BigDecimal("0.2"))
    .compareTo(new BigDecimal("0.3")) == 0);

Answer: false then true

Doubles are binary and cannot hold 0.1 exactly. BigDecimal built from *strings* is exact — passing a double to its constructor would inherit the same error. Note also that `equals` on BigDecimal compares scale, so 0.30 does not equal 0.3; use `compareTo`.

Official documentation →

7. Fill in the blank so the field's writes are visible to other threads immediately.

Advanced
java
private ____ boolean running = true;

public void stop() { running = false; }
public void run() { while (running) { work(); } }

Answer: volatile

Without `volatile` the JIT may cache `running` in a register, so the loop never observes the write from another thread and spins forever. `volatile` guarantees visibility and prevents reordering — but not atomicity, so it is not enough for compound operations like `i++`.

Official documentation →

8. What does this print?

Advanced
java
Map<String, Integer> m = new HashMap<>();
m.put("a", 1);
System.out.println(m.get("b") + " " + m.getOrDefault("b", 0));

Answer: null 0

A missing key returns null, and printing null is fine. The danger is assigning it to a primitive — `int x = m.get("b")` unboxes null and throws NullPointerException. `getOrDefault` avoids the whole problem.

Official documentation →

9. What does this print?

Advanced
java
List<String> l = List.of("a", "b");
try {
    l.add("c");
} catch (UnsupportedOperationException e) {
    System.out.println("immutable");
}

Answer: immutable

`List.of` returns an immutable list, so mutating it throws at runtime rather than compile time — the type is still `List`. `Arrays.asList` is a different trap: it is fixed-size, so `set` works but `add` throws.

Official documentation →

10. What does this print?

Advanced
java
Stream<Integer> s = Stream.of(1, 2, 3);
System.out.println(s.count());
try {
    s.count();
} catch (IllegalStateException e) {
    System.out.println("consumed");
}

Answer: 3 then consumed

A stream can be traversed once. After a terminal operation it is closed, and reusing it throws IllegalStateException. Create a new stream from the source each time, or collect to a List if you need the results more than once.

Official documentation →

11. What is the complexity of building a string this way for n iterations?

Advanced
java
String s = "";
for (int i = 0; i < n; i++) {
    s += i;
}

Answer: O(n²) — each concatenation copies the whole string

Strings are immutable, so each `+=` allocates a new StringBuilder, copies everything, and discards it. The compiler optimises a single concatenation expression but cannot hoist the builder out of a loop. Use a StringBuilder explicitly for O(n).

Official documentation →

12. Two threads calling increment() concurrently lose updates. Which line explains it?

Advanced
java
1  class Counter {
2      private volatile int count = 0;
3      public void increment() {
4          count++;
5      }
6  }

Answer: Line 4 — count++ is read-modify-write, and volatile gives visibility but not atomicity

`count++` is three operations, and two threads can interleave between the read and the write. `volatile` only guarantees each read sees the latest value. Use `AtomicInteger.incrementAndGet()`, or synchronize the method.

Official documentation →

13. What does this print?

Advanced
java
List<String> l = new ArrayList<>(List.of("b", "a", "c"));
Collections.sort(l);
System.out.println(l + " " + l.getClass().getSimpleName());

Answer: [a, b, c] ArrayList

Wrapping in `new ArrayList<>(...)` produces a mutable copy, so sorting in place works. Sorting the `List.of` result directly would throw UnsupportedOperationException — the defensive-copy step is what makes this safe.

Official documentation →

14. What does this print?

Advanced
java
Optional<String> o = Optional.ofNullable(null);
System.out.println(o.isPresent() + " " + o.orElse("fallback"));

Answer: false fallback

`ofNullable` accepts null and yields an empty Optional, whereas `Optional.of(null)` throws immediately. Prefer `map`/`orElse` chains over `isPresent()` followed by `get()` — the latter reintroduces exactly the null check Optional was meant to remove.

Official documentation →

15. What does this print?

Advanced
java
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
System.out.println(a.equals(b) + " " + Arrays.equals(a, b));

Answer: false true

Arrays do not override equals, so they fall back to Object's reference comparison. `Arrays.equals` compares contents, and `Arrays.deepEquals` handles nested arrays. The same applies to `toString` — printing an array directly gives a type tag and hash code.

Official documentation →

16. What does this print?

Advanced
java
List<String> names = List.of("anna", "bob", "amy");
System.out.println(
  names.stream()
       .filter(n -> n.startsWith("a"))
       .map(String::toUpperCase)
       .collect(Collectors.joining(", "))
);

Answer: ANNA, AMY

Streams preserve encounter order for ordered sources, so filtering and mapping keep the original sequence. `Collectors.joining` builds the delimited string. Intermediate operations are lazy — nothing runs until the terminal `collect`.

Official documentation →

17. Declaring a field `final` makes the object it references immutable.

Advanced
java
final List<String> items = new ArrayList<>();
items.add("x");

Answer: False

`final` fixes the reference, not the object — the add succeeds, while `items = new ArrayList<>()` would not compile. For an immutable view use `List.copyOf(items)` or `Collections.unmodifiableList`, and remember those are shallow.

Official documentation →

18. Fill in the blank so both resources are closed automatically, even on exception.

Advanced
java
try (____ BufferedReader r = new BufferedReader(new FileReader(f))) {
    return r.readLine();
}

Answer: (nothing — try-with-resources needs no keyword)

Try-with-resources takes the declaration directly in the parentheses; any resource implementing AutoCloseable is closed in reverse order when the block exits. It also suppresses secondary exceptions properly, which a manual finally block usually gets wrong.

Official documentation →

19. What does this print?

Advanced
java
Map<String, List<String>> m = new HashMap<>();
m.computeIfAbsent("k", k -> new ArrayList<>()).add("v1");
m.computeIfAbsent("k", k -> new ArrayList<>()).add("v2");
System.out.println(m);

Answer: {k=[v1, v2]}

`computeIfAbsent` creates the list only on the first call and returns the existing one afterwards, so both values land in the same list. It replaces the classic get-check-put-add dance and is atomic on ConcurrentHashMap.

Official documentation →

20. This deadlocks under load. Which line explains it?

Advanced
java
1  void transfer(Account a, Account b, int amt) {
2      synchronized (a) {
3          synchronized (b) {
4              a.debit(amt); b.credit(amt);
5          }
6      }
7  }

Answer: Lines 2-3 — two threads transferring in opposite directions acquire the locks in opposite order

`transfer(x, y)` and `transfer(y, x)` running concurrently each hold one lock and wait for the other — a classic deadlock. The fix is a consistent global lock ordering, for example by comparing account ids and always locking the lower one first.

Official documentation →

21. What does this print?

Advanced
java
class A { static String who() { return "A"; } }
class B extends A { static String who() { return "B"; } }

A ref = new B();
System.out.println(ref.who());

Answer: A

Static methods are hidden, not overridden — they are resolved at compile time from the declared type, which is `A`. Only instance methods are dispatched dynamically. This is why calling a static method through an instance reference is discouraged.

Official documentation →

22. What does this print?

Advanced
java
StringBuilder sb = new StringBuilder("ab");
modify(sb);
System.out.println(sb);

static void modify(StringBuilder s) {
    s.append("c");
    s = new StringBuilder("zz");
}

Answer: abc

Java passes references by value. Mutating the object through the parameter is visible to the caller; reassigning the parameter only rebinds the local copy of the reference. This is the cleanest demonstration that Java is not pass-by-reference.

Official documentation →

23. What does this print?

Advanced
java
System.out.println("5" + 3 + 2);
System.out.println(5 + 3 + "2");

Answer: 532 then 82

`+` is left-associative. Once one operand is a String the result is a String, so the first line concatenates throughout. The second adds 5 + 3 numerically first and only then concatenates, giving "82".

Official documentation →

24. This leaks a thread pool and the JVM never exits. Which line should change?

Advanced
java
1  ExecutorService ex = Executors.newFixedThreadPool(4);
2  for (Task t : tasks) {
3      ex.submit(t);
4  }
5  System.out.println("submitted");

Answer: Line 5 — the executor is never shut down, so its non-daemon threads keep the JVM alive

A fixed pool creates non-daemon threads that live until shut down. Call `ex.shutdown()` and then `awaitTermination`, ideally in a finally block — or use try-with-resources, since ExecutorService is AutoCloseable from Java 19.

Official documentation →

Ready to test yourself?

The full Java bank has 90 questions across 3 difficulty levels — timed, shuffled, and scored.

Take the Java quiz