Skip to content

Top Java Interview Questions and Answers

Updated 18 min read Aimerscrew

Whether you're applying for an internship, a junior backend role or an Android job, you'll almost certainly face Java interview questions. The good news: interviewers come back to the same core ideas again and again, such as how Java runs your code, how strings and objects are compared, how a HashMap finds a value, and what can go wrong with threads. This page collects 30 of the most common questions, from beginner to intermediate, each with a short answer you can say out loud and, where it helps, a small program with its real output.

🌍 Real-life example

Think of a driving test. The examiner doesn't ask you to recite the whole highway code; they watch a few key moments, like a parallel park, a roundabout and an emergency stop, that reveal whether you really know how to drive. A Java interview works the same way. A handful of questions, such as "why must you override hashCode when you override equals?", quickly shows whether you understand what's happening under the hood.

How should you prepare for a Java interview?

Don't memorize answers word for word. Interviewers almost always follow up with "why?" or "can you show me?". For each question below:

  1. Read the answer, then close the page and explain it in your own words.
  2. Run the example with java File.java, then change one line and predict the new output before running it again.
  3. Follow the link to the full lesson if anything feels shaky.
Map of the six Java interview topic groups on this page: core Java basics, object-oriented programming, exceptions, collections, modern Java with lambdas and streams, and memory and threads, each with the question numbers it covers
The 30 questions grouped into six topics. Most interviews touch every group at least once.

Core Java interview questions

1. What is Java, and what are its main features?

Java is a general-purpose, object-oriented, statically typed programming language first released by Sun Microsystems in 1995 and now maintained by Oracle and the OpenJDK community. Its key features are platform independence ("write once, run anywhere"), automatic memory management with a garbage collector, a huge standard library, strong type checking and built-in support for multithreading. It powers backend servers, Android apps, big data tools and enterprise systems. See introduction to Java.

2. What is the difference between the JDK, JRE and JVM?

What it isWho needs it
JVM (Java Virtual Machine)The engine that loads bytecode, checks it and runs it, compiling hot code to machine code with the JIT compilerEvery Java program, at run time
JRE (Java Runtime Environment)The JVM plus the standard class librariesPeople who only run Java programs
JDK (Java Development Kit)The JRE plus developer tools such as javac, jar and javadocDevelopers

In short: JDK ⊃ JRE ⊃ JVM. Java is platform independent because javac compiles source code to bytecode (.class files), not to machine code, and each operating system has its own JVM that runs the same bytecode. Since Java 11, Oracle no longer ships a separate JRE download; you install the JDK. More in how Java works.

3. What is the difference between primitive and reference types?

Java has 8 primitive types: byte, short, int, long, float, double, char and boolean. A primitive variable holds the value itself. Everything else (strings, arrays, your own classes) is a reference type: the variable holds a reference that points to an object on the heap, and it can be null. Each primitive has a wrapper class (int → Integer), and Java converts between them automatically; this is called autoboxing and unboxing. See data types.

4. What is the difference between == and equals()?

== compares primitive values, or, for objects, whether two references point to the same object. equals() compares contents, as defined by the class. For strings and wrapper objects, always use equals().

public class EqualsCheck {
    public static void main(String[] args) {
        String saved = "cat";
        String typed = new String("cat");
        System.out.println("saved == typed:      " + (saved == typed));
        System.out.println("saved.equals(typed): " + saved.equals(typed));

        Integer small1 = 127;
        Integer small2 = 127;
        Integer big1 = 128;
        Integer big2 = 128;
        System.out.println("127 == 127: " + (small1 == small2));
        System.out.println("128 == 128: " + (big1 == big2));
        System.out.println("128 equals 128: " + big1.equals(big2));
    }
}
saved == typed:      false
saved.equals(typed): true
127 == 127: true
128 == 128: false
128 equals 128: true

The Integer result surprises many people. By default, Java caches Integer objects from -128 to 127, so two boxed 127s are the same object, but two boxed 128s are different objects. That's a perfect example of why == on objects is a bug waiting to happen. See string comparison.

5. Why are strings immutable in Java, and what is the String pool?

A String object can never change after it's created; methods like toUpperCase() return a new string. Because strings can't change, Java can safely share them: string literals are stored once in a special area of the heap called the String pool, and every identical literal points to that one object. Immutability also makes strings safe as HashMap keys, safe to share between threads, and secure for values such as file paths and URLs that are checked before use.

public class StringPoolDemo {
    public static void main(String[] args) {
        String city = "Paris";
        String shout = city.toUpperCase();
        System.out.println(city + " / " + shout);

        String sameLiteral = "Paris";
        String fresh = new String("Paris");
        System.out.println("city == sameLiteral:    " + (city == sameLiteral));
        System.out.println("city == fresh:          " + (city == fresh));
        System.out.println("city == fresh.intern(): " + (city == fresh.intern()));
    }
}
Paris / PARIS
city == sameLiteral:    true
city == fresh:          false
city == fresh.intern(): true

new String("Paris") always creates a separate object outside the pool; intern() returns the pooled copy. More in Java strings.

6. What is the difference between String, StringBuilder and StringBuffer?

StringStringBuilderStringBuffer
Can change?No (immutable)YesYes
Thread-safe?Yes (because immutable)NoYes (synchronized methods)
Speed for many editsSlow (new object each time)FastestSlower than StringBuilder
Use it forMost textBuilding text in loopsLegacy code shared across threads

See StringBuilder.

7. What is the difference between final, finally and finalize?

  • final is a keyword: a final variable can't be reassigned, a final method can't be overridden, and a final class can't be extended. See constants.
  • finally is a block after try/catch that runs whether or not an exception happens, even after a return.
  • finalize() was a method the garbage collector might call before removing an object. It was unreliable, has been deprecated since Java 9 and is marked for removal. Use try-with-resources instead.
public class FinallyDemo {
    public static void main(String[] args) {
        System.out.println("Result: " + readSensor());
    }

    static int readSensor() {
        final int fallback = -1;
        try {
            System.out.println("Reading sensor...");
            return Integer.parseInt("21");
        } catch (NumberFormatException e) {
            return fallback;
        } finally {
            System.out.println("finally: closing the sensor");
        }
    }
}
Reading sensor...
finally: closing the sensor
Result: 21

8. What does the static keyword mean?

A static member belongs to the class itself, not to any one object. There's only one copy of a static field, shared by all objects, and you can call a static method without creating an object, like Math.max(3, 7). A static method can't use this or instance fields directly. See static.

public class TicketCounter {
    public static void main(String[] args) {
        Ticket a = new Ticket("Ana");
        Ticket b = new Ticket("Ben");
        Ticket c = new Ticket("Chloe");

        System.out.println(a.describe());
        System.out.println(b.describe());
        System.out.println(c.describe());
        System.out.println("Tickets sold: " + Ticket.sold);
    }
}

class Ticket {
    static int sold = 0;

    private final String holder;
    private final int number;

    Ticket(String holder) {
        sold++;
        this.holder = holder;
        this.number = sold;
    }

    String describe() {
        return "Ticket #" + number + " for " + holder;
    }
}
Ticket #1 for Ana
Ticket #2 for Ben
Ticket #3 for Chloe
Tickets sold: 3

9. What are the access modifiers in Java?

ModifierSame classSame packageSubclass (other package)Everywhere
privateYesNoNoNo
(none, "package-private")YesYesNoNo
protectedYesYesYesNo
publicYesYesYesYes

See access modifiers.

10. Is Java pass-by-value or pass-by-reference?

Java is always pass-by-value. For primitives, the method gets a copy of the value. For objects, the method gets a copy of the reference, so it can change the object's contents, but reassigning the parameter doesn't affect the caller's variable.

import java.util.Arrays;

public class PassByValue {
    public static void main(String[] args) {
        int points = 10;
        addBonus(points);
        System.out.println("points: " + points);

        int[] scores = {10, 20};
        changeFirst(scores);
        System.out.println("scores: " + Arrays.toString(scores));

        replaceArray(scores);
        System.out.println("scores: " + Arrays.toString(scores));
    }

    static void addBonus(int value) {
        value += 5;
    }

    static void changeFirst(int[] values) {
        values[0] = 99;
    }

    static void replaceArray(int[] values) {
        values = new int[] {0, 0};
        values[1] = 7;
    }
}
points: 10
scores: [99, 20]
scores: [99, 20]

See method parameters.

OOP interview questions

11. What are the four pillars of object-oriented programming?

  • Encapsulation: hide data in private fields and expose safe methods (lesson).
  • Inheritance: a class reuses and extends another class with extends (lesson).
  • Polymorphism: one reference type, many behaviors; the right overridden method is picked at run time (lesson).
  • Abstraction: show what an object does and hide how, using abstract classes and interfaces (lesson).

12. What is the difference between method overloading and overriding?

OverloadingOverriding
WhereSame class (or subclass)Subclass
Method signatureSame name, different parametersSame name and same parameters
DecidedAt compile timeAt run time (dynamic dispatch)
AnnotationNone@Override (recommended)
public class Notifications {
    public static void main(String[] args) {
        Notifier basic = new Notifier();
        Notifier email = new EmailNotifier();

        basic.send("Your order shipped");
        basic.send("Your order shipped", 2);
        email.send("Your order shipped");
    }
}

class Notifier {
    void send(String message) {
        System.out.println("Notify: " + message);
    }

    void send(String message, int times) {
        for (int i = 1; i <= times; i++) {
            send(message + " (" + i + ")");
        }
    }
}

class EmailNotifier extends Notifier {
    @Override
    void send(String message) {
        System.out.println("Email: " + message);
    }
}
Notify: Your order shipped
Notify: Your order shipped (1)
Notify: Your order shipped (2)
Email: Your order shipped

The variable email has type Notifier, but the object is an EmailNotifier, so its overriding send runs. See method overloading.

13. What is the difference between an abstract class and an interface?

Abstract classInterface
A class can extend/implementOnly oneMany
FieldsAny fields, including instance stateOnly public static final constants
ConstructorsYesNo
MethodsAbstract and concreteAbstract, default, static and private
Use it for"is-a" with shared code and state"can-do" abilities, like Comparable

This is also the answer to "does Java support multiple inheritance?": not for classes (to avoid the "diamond problem" of two parents with the same method), but a class can implement many interfaces. See interfaces.

14. What is a record in Java?

A record (Java 16+) is a short way to declare an immutable data class. From one line, Java generates private final fields, a constructor, accessor methods, and equals(), hashCode() and toString().

public class RecordDemo {
    public static void main(String[] args) {
        Seat first = new Seat("14C", 129.99);
        Seat second = new Seat("14C", 129.99);

        System.out.println(first);
        System.out.println(first.code() + " costs " + first.price());
        System.out.println("Equal? " + first.equals(second));
    }
}

record Seat(String code, double price) {
}
Seat[code=14C, price=129.99]
14C costs 129.99
Equal? true

See records.

15. How do you create an immutable class?

Make the class final (or use a record), make every field private final, set them only in the constructor, provide no setters, and make defensive copies of mutable things like lists, both when they come in and when you hand them out (List.copyOf(items)). String, Integer and LocalDate are all immutable classes.

16. What are the SOLID principles?

  • Single responsibility: a class should have one reason to change.
  • Open/closed: open for extension, closed for modification (add a new subclass instead of editing a big if chain).
  • Liskov substitution: a subclass must work anywhere its parent is expected.
  • Interface segregation: prefer several small interfaces over one huge one.
  • Dependency inversion: depend on interfaces, not concrete classes.

Exception handling interview questions

17. What is the difference between checked and unchecked exceptions?

CheckedUnchecked
ExtendsException (but not RuntimeException)RuntimeException (or Error)
Compiler forces you to handle it?Yes: catch it or declare throwsNo
Usually meansSomething outside your control (missing file, network down)A bug in the code (null, bad index)
ExamplesIOException, InterruptedExceptionNullPointerException, ArithmeticException

See exceptions.

18. What is the difference between throw and throws?

throw is a statement that actually throws one exception object: throw new IllegalArgumentException("Age can't be negative");. throws goes in a method signature and warns callers which checked exceptions the method might throw: void load() throws IOException. See throw and throws.

19. What is try-with-resources?

It's a try statement that declares resources in parentheses, such as files or database connections. Java closes them automatically when the block ends, even if an exception is thrown, in reverse order of opening. Any class that implements AutoCloseable works.

try (BufferedReader reader = Files.newBufferedReader(Path.of("orders.csv"))) {
    System.out.println(reader.readLine());
}   // reader.close() is called for you here

See try-with-resources.

Collections interview questions

20. What is the difference between List, Set and Map?

A List is an ordered collection that allows duplicates and has indexes (ArrayList). A Set holds no duplicates (HashSet, LinkedHashSet, TreeSet). A Map stores key-value pairs with unique keys (HashMap, LinkedHashMap, TreeMap). HashMap and HashSet have no guaranteed order; the Linked versions keep insertion order and the Tree versions keep keys sorted. See the collections framework.

21. What is the difference between ArrayList and LinkedList?

ArrayListLinkedList
Stored asA resizable arrayA chain of nodes linked both ways
get(i)Fast, O(1)Slow, O(n): walks the chain
Add/remove at the endsFast at the end, slow at the frontFast at both ends
MemoryCompactExtra memory per node for two links

In practice, ArrayList is the right default almost always; use ArrayDeque for queues. See ArrayList and LinkedList.

22. How does a HashMap work internally?

A HashMap keeps an array of buckets (16 by default). To put(key, value), it:

  1. Calls key.hashCode() and mixes the bits a little.
  2. Turns the hash into a bucket index: hash & (capacity - 1).
  3. Looks through the entries in that bucket. If one has an equal key (checked with equals()), it replaces the value; otherwise it adds a new entry.

get follows the same steps, so a lookup is O(1) on average. When the map is 75% full (the load factor 0.75), it doubles the array and redistributes the entries. When many keys collide in one bucket, Java 8+ turns that bucket's list into a balanced tree so lookups stay fast.

Animation of HashMap put: the key Mia goes through hashCode to a number, the number is reduced to bucket index 4, and equals() compares Mia with Bob, the key already in bucket 4, before a new entry is added
put() uses hashCode() to find the bucket and equals() to find the exact key inside it.

See HashMap.

23. What is the equals and hashCode contract?

If two objects are equals(), they must return the same hashCode(). (Two unequal objects may share a hash code; that's just a collision.) If you override equals but not hashCode, equal objects usually land in different buckets, so a HashSet stores duplicates and a HashMap can't find your key. If you override neither, objects are only equal to themselves:

import java.util.HashSet;
import java.util.Set;

public class HashKeys {
    public static void main(String[] args) {
        Set<PlainCard> plain = new HashSet<>();
        plain.add(new PlainCard("4111"));
        plain.add(new PlainCard("4111"));
        System.out.println("PlainCard set size: " + plain.size());
        System.out.println("PlainCard found: " + plain.contains(new PlainCard("4111")));

        Set<Card> cards = new HashSet<>();
        cards.add(new Card("4111"));
        cards.add(new Card("4111"));
        System.out.println("Card set size: " + cards.size());
        System.out.println("Card found: " + cards.contains(new Card("4111")));
    }
}

class PlainCard {
    private final String number;

    PlainCard(String number) {
        this.number = number;
    }

    String getNumber() {
        return number;
    }
}

record Card(String number) {
}
PlainCard set size: 2
PlainCard found: false
Card set size: 1
Card found: true

A record generates a matching equals and hashCode from its fields, so two Card("4111") objects count as one. See the Object class.

24. What is the difference between Comparable and Comparator?

Comparable defines a class's natural order inside the class itself, with one method compareTo (strings sort alphabetically, numbers numerically). A Comparator is a separate object that defines any other order, so you can have many: Comparator.comparing(Product::price), then .reversed() or .thenComparing(...). See sorting collections.

Modern Java interview questions

25. What is a lambda expression and a functional interface?

A functional interface has exactly one abstract method, like Runnable, Comparator or Predicate<T>. A lambda expression is a short way to write an implementation of that one method: (a, b) -> a + b. A method reference such as String::length is an even shorter lambda. See lambda expressions and functional interfaces.

26. Streams vs loops: what's the difference?

A loop says how to do something step by step. A stream describes what you want as a pipeline of operations (filter, map, sorted) ending in a terminal operation (toList, sum, count). Streams don't modify the source, are lazy (nothing runs until the terminal operation) and can only be used once. Both give the same result:

import java.util.ArrayList;
import java.util.List;

public class LoopVsStream {
    public static void main(String[] args) {
        List<Integer> orders = List.of(12, 85, 40, 150, 9, 64);

        List<Integer> bigLoop = new ArrayList<>();
        for (int amount : orders) {
            if (amount >= 40) {
                bigLoop.add(amount * 2);
            }
        }

        List<Integer> bigStream = orders.stream()
                .filter(amount -> amount >= 40)
                .map(amount -> amount * 2)
                .toList();

        System.out.println("Loop:   " + bigLoop);
        System.out.println("Stream: " + bigStream);
    }
}
Loop:   [170, 80, 300, 128]
Stream: [170, 80, 300, 128]

Streams shine for filter-transform-collect work; plain loops are often clearer when you need indexes, early exits or complex state. (Stream.toList() needs Java 16+.) See streams.

27. What is Optional, and why use it?

Optional<T> is a box that either holds a value or is empty. Returning it from a method like findUser(id) makes "there might be no result" part of the method's type, so callers must handle it with orElse, map or ifPresent instead of risking a NullPointerException. Use it for return values, not for fields or parameters. See Optional.

Memory and multithreading interview questions

28. How does garbage collection work in Java?

Objects live on the heap. The garbage collector (GC) regularly finds objects that no running code can reach anymore and frees their memory automatically, so you never call free() like in C. Most collectors are generational: new objects start in a "young" area that's cleaned often, because most objects die young, and survivors move to an "old" area. You can't force a collection; System.gc() is only a hint. Memory leaks are still possible if you keep references you no longer need, such as an ever-growing static list.

29. What is the difference between extending Thread and implementing Runnable?

Both define code to run on a new thread. Implementing Runnable (usually as a lambda) is preferred: your class stays free to extend something else, and the task is separate from the thread that runs it, so you can also hand it to an ExecutorService. Call start() to run it on a new thread (calling run() directly just runs it on the current thread), and join() to wait for it to finish.

public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        long[] totals = new long[2];

        Runnable firstHalf = () -> totals[0] = sum(1, 500_000);
        Thread worker1 = new Thread(firstHalf);
        Thread worker2 = new Thread(() -> totals[1] = sum(500_001, 1_000_000));

        worker1.start();
        worker2.start();
        worker1.join();
        worker2.join();

        System.out.println("First half:  " + totals[0]);
        System.out.println("Second half: " + totals[1]);
        System.out.println("Total:       " + (totals[0] + totals[1]));
    }

    static long sum(int from, int to) {
        long total = 0;
        for (int n = from; n <= to; n++) {
            total += n;
        }
        return total;
    }
}
First half:  125000250000
Second half: 375000250000
Total:       500000500000

See threads.

30. What do synchronized and volatile do?

count++ is really three steps (read, add, write), so two threads can interleave and lose updates; this is called a race condition. Marking a method synchronized lets only one thread run it at a time on the same object, so the count is always right:

public class SyncCounter {
    public static void main(String[] args) throws InterruptedException {
        LikeCounter likes = new LikeCounter();

        Runnable task = () -> {
            for (int i = 0; i < 10_000; i++) {
                likes.increment();
            }
        };
        Thread phone = new Thread(task);
        Thread laptop = new Thread(task);
        phone.start();
        laptop.start();
        phone.join();
        laptop.join();

        System.out.println("Likes: " + likes.get());
    }
}

class LikeCounter {
    private int count = 0;

    synchronized void increment() {
        count++;
    }

    synchronized int get() {
        return count;
    }
}
Likes: 20000

Without synchronized, the result would often be less than 20000 and different on each run. volatile is lighter: it guarantees every thread sees the latest value of a field (handy for a running flag), but it does not make count++ safe. For counters, AtomicInteger is a common alternative. See concurrency.

Common mistakes in Java interviews

1. Saying "Java passes objects by reference"

Java passes a copy of the reference. Show the reassignment example from question 10 if you're asked to prove it.

2. Assuming HashMap keeps order

A HashMap has no guaranteed order. Say LinkedHashMap for insertion order and TreeMap for sorted keys.

3. Mixing up final, finally and finalize

Three different things that just sound alike. Also mention that finalize is deprecated.

4. Answering without an example

"Strings are immutable" is fine; "city.toUpperCase() returns a new string and city stays the same" is much better. Short, concrete examples show real understanding.

Practice exercise

🏋️ Try it yourself

A favorite live-coding question: write static boolean isAnagram(String first, String second) that returns true when two phrases use exactly the same letters, ignoring spaces and case. Test it with listen/silent, Dormitory/dirty room and coffee/toffee.

Show solution
import java.util.Arrays;

public class AnagramCheck {
    public static void main(String[] args) {
        System.out.println("listen / silent: " + isAnagram("listen", "silent"));
        System.out.println("Dormitory / dirty room: " + isAnagram("Dormitory", "dirty room"));
        System.out.println("coffee / toffee: " + isAnagram("coffee", "toffee"));
    }

    static boolean isAnagram(String first, String second) {
        char[] a = first.replace(" ", "").toLowerCase().toCharArray();
        char[] b = second.replace(" ", "").toLowerCase().toCharArray();
        Arrays.sort(a);
        Arrays.sort(b);
        return Arrays.equals(a, b);
    }
}
listen / silent: true
Dormitory / dirty room: true
coffee / toffee: false

Sorting both character arrays puts the letters in the same order whenever the words are anagrams. Interviewers often ask for a faster version next: count letters with an int[26] array instead of sorting.

What's next?

Keep every answer within reach with the Java cheat sheet, a one-page reference to the syntax, collections, streams and format specifiers you use every day.

Key takeaways

  • The JDK contains the JRE, which contains the JVM; javac compiles code to bytecode and the JVM runs it on any platform.
  • Use == for primitives and reference identity, and equals() to compare the contents of strings and other objects.
  • Strings are immutable, which lets Java share identical literals in the String pool.
  • Java is always pass-by-value; for objects, the value passed is a copy of the reference.
  • HashMap uses hashCode() to pick a bucket and equals() to find the key, so equal objects must have equal hash codes.
  • Prefer interfaces, records and immutable classes; use synchronized or atomic classes when threads share data.

Frequently asked questions

What is the difference between JDK, JRE and JVM?

The JVM runs Java bytecode. The JRE is the JVM plus the standard libraries needed to run programs. The JDK is the JRE plus development tools like the javac compiler, so developers install the JDK.

What is the difference between == and equals() in Java?

For objects, == checks whether two references point to the same object, while equals() compares their contents. Always use equals() to compare strings and wrapper objects such as Integer.

Why is String immutable in Java?

Immutability lets Java store identical string literals once in the String pool and share them safely. It also makes strings thread-safe, reliable as HashMap keys and secure for values such as file paths.

Why must you override hashCode when you override equals?

Hash-based collections such as HashMap and HashSet use hashCode() to find the bucket and equals() to find the exact object. If equal objects have different hash codes, they land in different buckets, so lookups fail and duplicates appear.

Is Java pass-by-value or pass-by-reference?

Java is always pass-by-value. Primitives are copied, and for objects the reference is copied, so a method can change an object's contents but cannot make the caller's variable point to a different object.

What is the difference between an abstract class and an interface?

A class can extend only one abstract class, which can have fields, constructors and concrete methods. A class can implement many interfaces, which have no instance fields and define abilities through abstract, default and static methods.