pencils ready ✎

Java MCQs multiple-choice questions with answers & explanations

All 20 Java quiz questions on one page. Pick an answer in your head, then open Show answer to check it and read why. Want a score and a timer? Take them as a quiz instead.

  1. 1.

    With default JVM settings, what does this print?

    easy
    Integer a = 127, b = 127;
    Integer c = 128, d = 128;
    System.out.println((a == b) + " " + (c == d));
    1. Atrue false
    2. Btrue true
    3. Cfalse false
    4. Dfalse true
    Show answer

    Answer: A (true false)

    Autoboxing goes through Integer.valueOf, which must return cached instances for values from -128 to 127, so a and b are the same object. 128 is outside the default cache, so c and d are two different objects, and == compares references. Use equals to compare wrapper values.

  2. 2.

    What do these string comparisons print?

    hard
    String a = "ja";
    String b = a + "va";
    String c = "ja" + "va";
    System.out.println((b == "java") + " " + (c == "java"));
    1. Atrue true
    2. Btrue false
    3. Cfalse false
    4. Dfalse true
    Show answer

    Answer: D (false true)

    "ja" + "va" is a compile-time constant expression, so it is folded to "java" and interned: c refers to the pooled literal. a is not final, so a + "va" is evaluated at runtime and creates a new String, and == compares references. Declaring a as final would make both comparisons true.

  3. 3.

    Which overload runs when you pass null, and what is printed?

    mid
    static void print(Object o) { System.out.println("Object"); }
    static void print(String s) { System.out.println("String"); }
    
    print(null);
    1. AObject
    2. BCompile error: ambiguous call
    3. CNullPointerException
    4. DString
    Show answer

    Answer: D (String)

    null matches both overloads, so the compiler picks the most specific one: String is a subtype of Object, so print(String) wins. If there were also a print(Integer) overload, neither would be more specific than the other and the call would be an ambiguous compile error.

  4. 4.

    What does calling the static method through p print?

    mid
    class Parent { static String name() { return "Parent"; } }
    class Child extends Parent { static String name() { return "Child"; } }
    
    Parent p = new Child();
    System.out.println(p.name());
    1. AChild
    2. BParent
    3. CCompile error
    4. DNullPointerException
    Show answer

    Answer: B (Parent)

    Static methods are not overridden: Child.name() only hides Parent.name(). A static call is bound at compile time using the declared type of the reference, Parent, so it prints Parent. Calling a static method through an instance compiles with a warning, and would work even if p were null.

  5. 5.

    Given class A { void run() throws IOException {} }, which override in class B extends A fails to compile?

    mid
    1. Avoid run() {}
    2. Bvoid run() throws FileNotFoundException {}
    3. Cvoid run() throws RuntimeException {}
    4. Dvoid run() throws Exception {}
    Show answer

    Answer: D (void run() throws Exception {})

    An overriding method may declare fewer or narrower checked exceptions, but not broader ones: Exception is a supertype of IOException, and code calling through an A reference would not be prepared for it. Declaring nothing, a subclass such as FileNotFoundException, or any unchecked exception is allowed.

  6. 6.

    What does System.out.println(f()) print?

    easy
    static int f() {
        try {
            return 1;
        } finally {
            return 2;
        }
    }
    1. A1
    2. BCompile error: unreachable code
    3. C2
    4. D1 and then 2
    Show answer

    Answer: C (2)

    The finally block runs after return 1 is evaluated, and a return inside finally replaces the pending return value, so f() returns 2. The code compiles; javac only warns under -Xlint:finally. Returning from finally is a bad idea because it also silently discards any exception thrown in the try block.

  7. 7.

    What does f() return?

    hard
    static int f() {
        int x = 1;
        try {
            return x;
        } finally {
            x = 2;
        }
    }
    1. A2
    2. B1
    3. CIt does not compile
    4. D0
    Show answer

    Answer: B (1)

    When return x executes, the value of x (1) is evaluated and saved as the return value. The finally block then changes the local variable, not the saved value, so f() returns 1. Only a return inside finally could change the result. If x referred to a mutable object, changes to that object in finally would be visible.

  8. 8.

    In what order are the body, the close() calls and the finally block printed?

    easy
    record Res(String name) implements AutoCloseable {
        public void close() { System.out.print("close " + name + " "); }
    }
    
    try (Res a = new Res("a"); Res b = new Res("b")) {
        System.out.print("body ");
    } finally {
        System.out.print("finally");
    }
    1. Abody close a close b finally
    2. Bbody finally close b close a
    3. Cclose a close b body finally
    4. Dbody close b close a finally
    Show answer

    Answer: D (body close b close a finally)

    Resources are closed in the reverse order of declaration, so b closes before a. They are closed as soon as the try block finishes, before any catch or finally block of the same statement runs.

  9. 9.

    A map created with new HashMap<>() resizes its table once its size exceeds which number?

    mid
    1. A12
    2. B16
    3. C8
    4. D64
    Show answer

    Answer: A (12)

    The default capacity is 16 and the default load factor is 0.75, so the threshold is 16 × 0.75 = 12: adding the 13th entry doubles the table to 32 buckets. 8 is the per-bucket treeify threshold and 64 is the minimum table size for treeification; neither triggers a resize.

  10. 10.

    What happens when you call put("k", null) on a ConcurrentHashMap<String, String>?

    mid
    1. AIt throws NullPointerException
    2. BIt stores "k" mapped to null
    3. CIt removes the existing mapping for "k"
    4. DThe call is silently ignored
    Show answer

    Answer: A (It throws NullPointerException)

    ConcurrentHashMap rejects null keys and values with a NullPointerException. In a concurrent map, get returning null must unambiguously mean "absent", because you cannot lock the map to call containsKey in between. HashMap, by contrast, allows null values and one null key.

  11. 11.

    What happens when this loop removes "b" from the list?

    hard
    List<String> list = new ArrayList<>(List.of("a", "b", "c"));
    for (String s : list) {
        if (s.equals("b")) list.remove(s);
    }
    System.out.println(list);
    1. AConcurrentModificationException
    2. B[a, b, c]
    3. C[a, c]
    4. DIndexOutOfBoundsException
    Show answer

    Answer: C ([a, c])

    Removing "b" shrinks the size to 2, and the iterator's cursor is already 2, so hasNext() returns false and the loop ends before next() can perform its modCount check. It prints [a, c] with no exception. Removing "a" instead would throw ConcurrentModificationException, which shows why fail-fast checks are best-effort: use removeIf or Iterator.remove().

  12. 12.

    What size does this TreeSet report?

    mid
    Set<String> set = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
    set.addAll(List.of("Java", "java", "JAVA", "Kotlin"));
    System.out.println(set.size());
    1. A4
    2. B3
    3. C1
    4. D2
    Show answer

    Answer: D (2)

    A TreeSet decides whether an element is a duplicate using its comparator, not equals. The case-insensitive comparator returns 0 for "Java", "java" and "JAVA", so only the first is kept, alongside "Kotlin", giving a size of 2.

  13. 13.

    What does this stream pipeline print?

    easy
    Stream.of(1, 2, 3, 4)
          .peek(n -> System.out.print(n + " "))
          .filter(n -> n % 2 == 0)
          .map(n -> n * 10);
    1. ANothing
    2. B1 2 3 4
    3. C2 4
    4. D20 40
    Show answer

    Answer: A (Nothing)

    Intermediate operations such as peek, filter and map are lazy: they only build the pipeline. Without a terminal operation like toList(), forEach or count(), no element is ever pulled through it, so nothing is printed.

  14. 14.

    What does this Optional code print?

    mid
    static String fallback() {
        System.out.print("fallback ");
        return "default";
    }
    
    String v = Optional.of("value").orElse(fallback());
    System.out.print(v);
    1. Avalue
    2. Bfallback value
    3. Cfallback default
    4. Ddefault
    Show answer

    Answer: B (fallback value)

    orElse takes an already-computed value, so its argument fallback() is evaluated before orElse runs, printing "fallback " even though the Optional holds a value. The result is still "value". Use orElseGet(() -> fallback()) to compute the default lazily, only when the Optional is empty.

  15. 15.

    Which assignment does NOT compile?

    mid
    List<Integer> ints = new ArrayList<>();
    
    List<? extends Number> a = ints;                    // line A
    List<Number> b = ints;                              // line B
    List<? super Integer> c = new ArrayList<Number>();  // line C
    Collection<Integer> d = ints;                       // line D
    1. ALine A
    2. BLine B
    3. CLine C
    4. DLine D
    Show answer

    Answer: B (Line B)

    Generics are invariant: a List<Integer> is not a List<Number>, or you could add a Double to a list of integers through b. Line A works because ? extends Number accepts a list of any Number subtype, line C because Number is a supertype of Integer, and line D because List<Integer> is a subtype of Collection<Integer>.

  16. 16.

    Given List<? extends Number> nums = new ArrayList<Integer>();, which statement compiles?

    hard
    1. Anums.add(1);
    2. Bnums.add((Number) 1);
    3. Cnums.add(null);
    4. Dnums.add(1.5);
    Show answer

    Answer: C (nums.add(null);)

    With ? extends Number, the compiler only knows the list holds some unknown subtype of Number, which could be Integer, Double or anything else, so it rejects every add except null, which is valid for any reference type. Reading is fine: elements come out as Number. To add integers you would need List<? super Integer> (PECS).

  17. 17.

    Two threads each call increment() 1,000 times on the same Counter. What can you say about the final value of count?

    mid
    class Counter {
        private volatile int count = 0;
        void increment() { count++; }
    }
    1. AAlways exactly 2000, since the field is volatile
    2. BIt can exceed 2000 because of reordering
    3. CAt most 2000, and it may be less
    4. DAlways exactly 1000
    Show answer

    Answer: C (At most 2000, and it may be less)

    volatile guarantees visibility, but count++ is still three steps: read, add, write. Two threads can read the same value and both write back that value plus one, losing an update, so the total can be lower than 2000 but never higher. Use AtomicInteger.incrementAndGet() or a lock for an atomic increment.

  18. 18.

    Which workload benefits most from virtual threads (Java 21)?

    easy
    1. AA CPU-bound image filter that uses every core
    2. BA server with many requests waiting on I/O
    3. CSorting one large array held in memory
    4. DA single-threaded batch calculation
    Show answer

    Answer: B (A server with many requests waiting on I/O)

    Virtual threads make blocking cheap: when one waits on I/O, it unmounts from its carrier thread, so you can run huge numbers of concurrent, mostly waiting tasks with simple blocking code. They add no CPU capacity, so CPU-bound work runs no faster than on platform threads.

  19. 19.

    What happens when this code is compiled?

    hard
    static String check(Object o) {
        return switch (o) {
            case Integer i -> "int";
            case Integer i when i > 10 -> "big int";
            default -> "other";
        };
    }
    1. AIt compiles; check(42) returns "big int"
    2. BIt compiles; check(42) returns "int"
    3. CCompile error: the guarded case is dominated
    4. DCompile error: default is not allowed here
    Show answer

    Answer: C (Compile error: the guarded case is dominated)

    In a pattern switch, a case label is dominated when an earlier label matches everything it could match. case Integer i already matches every Integer, so the guarded case below it could never run, and javac rejects it. Moving the guarded case above the unguarded one fixes it.

  20. 20.

    Which class loader loads java.lang.String?

    easy
    1. AThe bootstrap class loader
    2. BThe platform class loader
    3. CThe application (system) class loader
    4. DThe thread context class loader
    Show answer

    Answer: A (The bootstrap class loader)

    Core classes in java.base are loaded by the bootstrap class loader, which is built into the JVM, so String.class.getClassLoader() returns null. Thanks to parent delegation, the application and platform loaders ask their parent first, which stops application code from replacing core classes.

esc