Java Programming Foundations Interview Questions
50 questions and answers covering Java Programming Foundations, from fundamentals through practical, debugging, and design-level topics.
50 of 50 interview questions
What is the relationship between the JDK, the JVM, and bytecode?beginnerJVM & Language Fundamentals
The JDK (Java Development Kit) includes the compiler (`javac`) that compiles Java source into platform-independent bytecode; the JVM (Java Virtual Machine) is the runtime that executes that bytecode on a specific platform -- this two-step model is what lets the same compiled bytecode run unmodified on any platform with a compatible JVM ('write once, run anywhere').
What is the difference between a primitive type (like `int`) and a reference type (like `Integer`) in Java?intermediateJVM & Language Fundamentals
A primitive holds its actual value directly in memory and can never be `null`; a reference type holds a reference to an object on the heap (which could be `null`) and comes with object overhead (methods, potential autoboxing cost) -- `Integer` is the 'boxed' object wrapper around the primitive `int`.
What is autoboxing, and what is a subtle performance/correctness pitfall it can introduce?advancedJVM & Language Fundamentals
Automatic conversion between a primitive (`int`) and its wrapper object (`Integer`) -- a pitfall: comparing two boxed `Integer` objects with `==` compares object identity, not value, and can give surprising results for values outside the JVM's small cached-integer range, unlike primitive `int` comparison which always compares value.
Common mistake: Comparing two Integer objects with == instead of .equals(), which can silently pass for small cached values but fail for larger ones.
Is Java 'pass by value' or 'pass by reference' for object arguments?advancedJVM & Language Fundamentals
Java is strictly pass-by-value -- for object arguments, the VALUE being passed is a copy of the reference (pointer to the object), not the object itself. This means a method can mutate the object the reference points to, but reassigning the parameter itself inside the method does not affect the caller's original variable.
What is the difference between the stack and the heap in Java's memory model, at a basic level?advancedJVM & Language Fundamentals
The stack holds method call frames and local primitive variables/references, with automatic cleanup as methods return; the heap holds actual object instances, managed by the garbage collector rather than tied to a specific method call's lifetime.
What is garbage collection, and why doesn't a Java developer typically need to manually free object memory?intermediateJVM & Language Fundamentals
The JVM automatically identifies objects that are no longer reachable from any active reference and reclaims their memory -- unlike languages requiring manual memory management, Java developers generally don't call anything to 'free' an object; it happens automatically once nothing references it anymore.
What is the difference between `==` and `.equals()` when comparing two `String` objects in Java?beginnerJVM & Language Fundamentals
`==` compares object reference identity (are these the exact same object in memory); `.equals()` compares actual content/value equality -- two different `String` objects with identical characters are `.equals()` but may not be `==`, which is a very common Java beginner mistake.
Common mistake: Comparing two String objects with == instead of .equals(), which can appear to work for string literals due to interning but fails for strings built at runtime.
Why are `String` objects immutable in Java, and what does that mean practically?intermediateJVM & Language Fundamentals
Once created, a `String`'s content can never be changed -- any operation that appears to 'modify' a string (like concatenation) actually creates and returns a NEW `String` object. This immutability makes strings safe to share across threads and as hash keys without defensive copying.
Why might repeated string concatenation in a loop (`result = result + item;`) be inefficient, and what's the typical fix?advancedJVM & Language Fundamentals
Since `String` is immutable, each concatenation creates a brand-new string object, copying all the previous content -- in a loop, this becomes quadratic-time overall; `StringBuilder` (mutable, designed for repeated appends) avoids this by growing an internal buffer instead of creating a new object each time.
What is method overloading, and how does Java decide which overload to call?intermediateJVM & Language Fundamentals
Overloading defines multiple methods with the same name but different parameter lists (different type, count, or order) -- the compiler resolves which specific overload to call at compile time, based on the number and static types of the arguments provided in the call.
What is the difference between a Java array and a `List`?beginnerControl Flow, Methods & Collections
An array has a fixed size determined at creation and can hold primitives directly; a `List` (like `ArrayList`) is a resizable collection interface implementation that can grow/shrink dynamically, but can only hold reference types (requiring autoboxing for primitives like `int` -> `Integer`).
What is the difference between `List`, `Set`, and `Map` as core collection types, and when would you reach for each?intermediateControl Flow, Methods & Collections
`List` maintains insertion order and allows duplicates (use when order/duplicates matter, like a queue of tasks); `Set` holds unique elements with no meaningful positional order (use for membership checking without duplicates); `Map` stores key-value pairs (use when you need to look values up by a key).
What is the difference between `ArrayList` and `LinkedList` as `List` implementations, in terms of performance tradeoffs?advancedControl Flow, Methods & Collections
`ArrayList` is backed by a resizable array -- fast random access by index (O(1)) but slower insertion/removal in the middle (needs shifting elements); `LinkedList` is backed by linked nodes -- fast insertion/removal at known positions (O(1) given a reference to that node) but slower random access (O(n), must traverse from an end).
What is a `for-each` loop, and what kind of objects can you iterate with it?beginnerControl Flow, Methods & Collections
`for (Type item : collection) { ... }` -- a concise loop form for iterating any object implementing `Iterable` (arrays, `List`, `Set`, and more), reading each element in turn without manually managing an index/iterator.
for (String name : names) { System.out.println(name); }Why does a `HashMap` require correctly-implemented `hashCode()` and `equals()` on its keys to work correctly?advancedControl Flow, Methods & Collections
`HashMap` uses a key's `hashCode()` to decide which internal bucket to store it in, then `equals()` to distinguish between different keys that happen to land in the same bucket -- a broken or missing override of either can cause lookups to fail to find an entry that was genuinely stored, or treat two logically-equal keys as different.
What is the difference between method parameters and method arguments?beginnerControl Flow, Methods & Collections
Parameters are the named variables declared in a method's signature (`void greet(String name)` -- `name` is the parameter); arguments are the actual values passed when calling that method (`greet("Asha")` -- `"Asha"` is the argument) -- a common but minor terminology distinction.
What does the `static` keyword mean when applied to a method or field?intermediateControl Flow, Methods & Collections
A `static` member belongs to the class itself, not to any individual instance -- it can be accessed without creating an object (`ClassName.method()`), and all instances share the exact same `static` field rather than each having their own copy.
What is the difference between a `switch` statement and a `switch` expression in modern Java?advancedControl Flow, Methods & Collections
A `switch` statement executes code per matched case without necessarily returning a value (and traditionally needs explicit `break` to avoid fall-through); a `switch` expression (newer syntax, using `->`) directly evaluates to a value, with no fall-through and more concise arrow-case syntax.
Why is forgetting a `break` in a traditional `switch` statement (fall-through) a classic Java bug?intermediateControl Flow, Methods & Collections
Without an explicit `break`, execution continues into the NEXT case's code after a match, rather than stopping -- often unintended, and a well-known source of bugs in traditional `switch` statements (part of why the newer arrow-based `switch` expression syntax removed fall-through by default).
Common mistake: Forgetting a break statement in a traditional switch, causing execution to unintentionally fall through into the next case.
Why might you choose a `TreeMap` over a `HashMap`, given `HashMap`'s generally faster average performance?advancedControl Flow, Methods & Collections
`TreeMap` keeps its keys in sorted order (based on natural ordering or a provided comparator), which `HashMap` does not guarantee at all -- if you need to iterate keys in sorted order, or efficiently find the nearest key above/below a value, `TreeMap`'s extra performance cost is worth the ordering guarantee.
What is encapsulation, and how is it typically implemented in Java?beginnerObject-Oriented Design
Bundling an object's internal data with the methods that operate on it, and restricting direct external access to that data -- typically implemented with `private` fields plus public getter/setter methods (only where actually needed), so the class controls how its own state can be read or modified.
What is the difference between inheritance and composition, and when might 'favor composition over inheritance' apply?advancedObject-Oriented Design
Inheritance ('is-a') creates a tight, hierarchical coupling where a subclass inherits and can be affected by its parent's implementation details; composition ('has-a') builds behavior by combining smaller, independent objects as fields -- composition is often preferred when the relationship isn't a genuine specialization, since it's more flexible and avoids fragile deep inheritance hierarchies.
What is the difference between an abstract class and an interface in Java?advancedObject-Oriented Design
An abstract class can have both abstract (unimplemented) and concrete (implemented) methods plus instance state, and a class can extend only ONE abstract class; an interface traditionally declares method signatures a class must implement (with modern Java also allowing default/static method bodies), and a class can implement MULTIPLE interfaces.
What is polymorphism, and how does it show up when calling a method through a supertype reference?intermediateObject-Oriented Design
The ability for different classes to be treated through a common supertype/interface, with the actual method implementation invoked determined by the object's real runtime type -- calling `shape.area()` on a variable declared as `Shape` but actually holding a `Circle` instance invokes `Circle`'s specific implementation, not a generic `Shape` one.
What does the `@Override` annotation do, and why is it good practice to always include it when overriding a method?intermediateObject-Oriented Design
It's a compile-time check confirming the annotated method actually overrides a method from a superclass/interface -- if you mistype the method signature (wrong parameter types, a typo in the name), `@Override` causes a compile error instead of silently creating an unrelated new method that never actually overrides anything.
Common mistake: Slightly mistyping an overridden method's signature without @Override, silently creating a new, unrelated method instead of a real override.
What is a constructor, and what happens if a class defines no constructor at all?beginnerObject-Oriented Design
A special method that initializes a newly-created object's state, called via `new ClassName(...)` -- if a class defines no constructor, Java provides a default, no-argument constructor automatically (but this disappears the moment you define ANY constructor yourself, requiring an explicit no-arg one if still needed).
What does the `final` keyword mean when applied to a class, a method, or a variable, respectively?advancedObject-Oriented Design
On a class, it prevents the class from being subclassed at all; on a method, it prevents that method from being overridden by a subclass; on a variable, it prevents reassignment after initial assignment (though for a reference type, the object it points to can still be internally mutated).
Why might designing a class's fields as `private final`, set only in the constructor, be a good default for a simple data-modeling class?advancedObject-Oriented Design
It produces an immutable object -- once constructed, its state can never change, which eliminates a whole class of bugs from unexpected mutation and makes the object inherently safe to share across threads without synchronization.
What is the purpose of a `protected` access modifier, and how does it differ from `private` and `public`?advancedObject-Oriented Design
`protected` members are accessible within the same class, the same package, and by subclasses (even in different packages) -- more open than `private` (same class only) but more restricted than `public` (accessible from anywhere) -- typically used for members a subclass genuinely needs to access/override but that shouldn't be part of the class's fully public API.
Why is designing a small domain model with clear interfaces (e.g. a `PaymentMethod` interface implemented by `CreditCard` and `PayPal`) valuable for extensibility?advancedObject-Oriented Design
New payment method types can be added later by implementing the same interface, without modifying the existing code that already works against `PaymentMethod` generically -- code written against the abstraction stays stable even as concrete implementations are added, a real-world benefit of designing to interfaces.
What is the difference between a checked exception and an unchecked exception in Java?intermediateExceptions, Generics & Equality
A checked exception (extending `Exception` but not `RuntimeException`) must be either caught or declared in the method's `throws` clause -- the compiler enforces handling it; an unchecked exception (extending `RuntimeException`) requires no such declaration and typically represents a programming error that could occur almost anywhere.
Why is catching a broad `Exception` (or worse, `Throwable`) and doing nothing with it ('swallowing' it) generally a bad practice?intermediateExceptions, Generics & Equality
It silently discards information about a real failure, making the program appear to succeed when something actually went wrong -- the failure surfaces later, in a confusing, disconnected way (or never surfaces at all), making the root cause far harder to diagnose than if the exception had been logged or allowed to propagate.
Common mistake: Catching a broad Exception with an empty catch block, silently swallowing a real failure instead of logging or handling it.
What is try-with-resources, and what problem does it solve?advancedExceptions, Generics & Equality
A `try (Resource r = ...) { ... }` syntax that automatically closes any resource implementing `AutoCloseable` (like a file or database connection) once the block exits, even if an exception was thrown -- it solves the resource-leak risk of manually calling `close()` in a `finally` block, which is easy to forget or get wrong.
try (BufferedReader reader = new BufferedReader(new FileReader(path))) { return reader.readLine(); }What is a Java generic type, and what problem does it solve compared to using `Object` for a general-purpose container?intermediateExceptions, Generics & Equality
A generic (like `List<String>`) lets a class/method work with a specified type while the compiler enforces type-correctness at compile time -- using raw `Object` instead loses that safety, requiring manual casts (which can fail at runtime with a `ClassCastException`) that generics catch as compile errors instead.
What is the 'equals/hashCode contract,' and why must both be overridden together, never just one?advancedExceptions, Generics & Equality
The contract requires that two objects considered `.equals()` MUST have the same `hashCode()` -- overriding only `equals()` (leaving the default identity-based `hashCode()`) breaks hash-based collections like `HashMap`/`HashSet`, since 'equal' objects could land in different buckets and never be found as duplicates.
Common mistake: Overriding equals() without also overriding hashCode(), breaking the contract hash-based collections rely on.
What does it mean for a custom exception class to extend `RuntimeException` versus `Exception` directly?advancedExceptions, Generics & Equality
Extending `Exception` directly makes it a checked exception (callers must handle or declare it); extending `RuntimeException` makes it unchecked -- the choice signals whether callers are realistically expected to recover from this specific failure (checked) or whether it typically represents a programming error not meant to be routinely caught (unchecked).
What is a bounded type parameter in generics, e.g. `<T extends Comparable<T>>`?advancedExceptions, Generics & Equality
It restricts a generic type parameter to only types that satisfy a given constraint -- here, `T` must implement `Comparable<T>`, letting a generic method safely call `.compareTo()` on values of type `T`, which an unbounded `<T>` wouldn't allow since arbitrary `Object` doesn't have that method.
Why might an exception message like 'error' be significantly less useful than 'Failed to parse order ID "abc": expected a numeric value'?beginnerExceptions, Generics & Equality
A vague message gives no actionable information about what actually went wrong or why -- a specific message including the relevant input/context lets whoever reads it (a developer debugging, or a log-monitoring system) understand and act on the failure immediately, without needing to reproduce it from scratch.
What does it mean to 'wrap and rethrow' an exception, and why might you do it?advancedExceptions, Generics & Equality
Catching a lower-level exception and throwing a new, higher-level exception that includes the original as its 'cause' -- useful for translating a low-level implementation detail (a database driver's specific exception type) into a more meaningful, application-level exception, while preserving the original stack trace via the cause chain for debugging.
Why is immutability (an object whose state can't change after construction) often paired with a carefully-implemented `equals()`/`hashCode()`?advancedExceptions, Generics & Equality
If an object used as a `HashMap` key or `HashSet` member is mutated after insertion, its hash code can change, breaking the collection's internal bucket-based lookup (the object effectively becomes 'lost,' unfindable at its original hash location) -- immutability guarantees the hash code stays stable for the object's entire lifetime.
What is a lambda expression in Java, and what problem does it solve compared to an anonymous inner class?intermediateLambdas, Streams & Testing
A concise syntax for representing a single-method functional interface implementation inline (`x -> x * 2`) -- it solves the verbosity of Java's older anonymous-inner-class syntax for the same purpose, without changing the underlying functional-interface mechanism.
list.forEach(item -> System.out.println(item));What is the Stream API used for, and how does `stream().filter(...).map(...).collect(...)` express a data pipeline?intermediateLambdas, Streams & Testing
It provides a functional-style API for processing sequences of elements (filter, transform, aggregate) as a declarative pipeline, rather than manually writing imperative loops -- `filter` keeps only matching elements, `map` transforms each remaining element, `collect` gathers the final results into a concrete collection.
List<String> names = users.stream() .filter(u -> u.isActive()) .map(User::getName) .collect(Collectors.toList());Are Java streams lazy or eager, and why does that matter for a pipeline of `filter`/`map` operations?advancedLambdas, Streams & Testing
Intermediate operations (`filter`, `map`) are lazy -- they don't actually run until a terminal operation (`collect`, `forEach`, `count`) is invoked, at which point the whole pipeline processes each element through every step in a single pass, rather than each intermediate step fully processing the whole collection before the next.
Can a Java `Stream` be consumed (iterated with a terminal operation) more than once?advancedLambdas, Streams & Testing
No -- a stream can only be traversed once; calling a terminal operation on an already-consumed stream throws an `IllegalStateException`. If you need to process the same source data multiple times, you must create a fresh stream from the original source each time.
Common mistake: Attempting to reuse the same Stream object for a second terminal operation after it's already been consumed once.
What is a method reference (like `User::getName`), and how does it relate to a lambda expression?advancedLambdas, Streams & Testing
A shorthand syntax for a lambda that just calls an existing method -- `User::getName` is equivalent to writing `u -> u.getName()`, more concise when the lambda's entire body is a single existing method call.
What is the difference between `Optional<T>` and simply returning `null` to represent 'no value'?advancedLambdas, Streams & Testing
`Optional` makes the possibility of absence explicit in the method's return TYPE, forcing callers to deliberately handle the empty case (via `.isPresent()`, `.orElse()`, etc.) rather than risking an unchecked `NullPointerException` from an easily-overlooked `null` return that the type signature gave no hint about.
What does a JUnit test method typically look like, and what annotation marks it as a test?beginnerLambdas, Streams & Testing
A method annotated `@Test`, containing assertions (like `assertEquals(expected, actual)`) that verify expected behavior -- JUnit's test runner discovers and executes every `@Test`-annotated method, reporting pass/fail for each independently.
@Test void addsTwoNumbers() { assertEquals(5, Calculator.add(2, 3)); }What is the difference between `@BeforeEach` and `@BeforeAll` in JUnit?intermediateLambdas, Streams & Testing
`@BeforeEach` runs before EVERY individual test method in the class (useful for resetting shared state so tests don't interfere with each other); `@BeforeAll` runs ONCE before all tests in the class (useful for expensive one-time setup, and must be a `static` method).
Why might overusing complex, deeply-chained stream pipelines actually hurt code readability compared to a well-named traditional loop?advancedLambdas, Streams & Testing
A stream expression packs a lot of logic densely into one line/expression, which can become genuinely harder to read and debug than an equivalent, clearly-named imperative loop once the transformation logic gets complex -- streams are a tool for expressing clear data pipelines, not an automatic readability win in every situation.
Why should JUnit tests be independent of each other (not relying on execution order or shared mutable state)?advancedLambdas, Streams & Testing
Test frameworks don't guarantee a specific execution order by default, and tests may run in parallel -- a test that depends on another test having run first (or on leftover shared state) can pass or fail unpredictably depending on execution order, making failures much harder to diagnose and reproduce reliably.