Frequently asked Java interview questions spanning core language fundamentals, OOP, collections, concurrency, JVM internals, and modern Java features — suitable for junior through senior developer roles.
JVM (Java Virtual Machine) executes bytecode and is platform-specific. JRE (Java Runtime Environment) includes the JVM plus standard libraries needed to run Java programs. JDK (Java Development Kit) includes the JRE plus development tools like the compiler (javac) and debugger.
The four pillars are Encapsulation (hiding state via access modifiers), Inheritance (extending classes with 'extends'), Polymorphism (method overriding and overloading), and Abstraction (abstract classes and interfaces). Java enforces single-class inheritance but allows multiple interface implementation to balance flexibility and safety.
'==' compares object references (memory addresses) for objects, or actual values for primitives. '.equals()' compares object content/state and should be overridden in custom classes to define meaningful equality. For example, two different String objects with the same text return false with '==' but true with '.equals()'.
Autoboxing is the automatic conversion of a primitive type (e.g., int) to its corresponding wrapper class (e.g., Integer) by the compiler. Unboxing is the reverse. While convenient, excessive autoboxing in tight loops can degrade performance due to object creation overhead.
String is immutable; every modification creates a new object. StringBuilder is mutable and not thread-safe, making it the preferred choice for single-threaded string manipulation. StringBuffer is mutable and thread-safe (methods are synchronized) but slower than StringBuilder due to synchronization overhead.
An abstract class can have state (instance fields), constructors, and both abstract and concrete methods; a class can extend only one abstract class. An interface (Java 8+) can have default and static methods but no instance state; a class can implement multiple interfaces. Use abstract classes for shared base behavior and interfaces for defining contracts.
The stack stores method call frames, local variables, and references to objects — each thread has its own stack. The heap stores all objects and class instances and is shared across threads, managed by the garbage collector. Primitives declared inside methods live on the stack, while objects always live on the heap.
Checked exceptions (subclasses of Exception excluding RuntimeException) must be declared with 'throws' or caught at compile time — e.g., IOException, SQLException. Unchecked exceptions (subclasses of RuntimeException) do not require explicit handling — e.g., NullPointerException, IllegalArgumentException. Errors (e.g., OutOfMemoryError) are generally unrecoverable and should not be caught.
ArrayList is backed by a dynamic array, offering O(1) random access but O(n) insertion/deletion in the middle. LinkedList is a doubly-linked list with O(1) insertion/deletion at head or tail but O(n) random access. ArrayList is generally preferred for read-heavy workloads; LinkedList suits frequent insertions/removals at the ends.
HashMap uses an array of buckets, where each bucket is a linked list (or a balanced tree in Java 8+ when a bucket exceeds 8 entries). The key's hashCode() determines the bucket index; equals() resolves collisions within a bucket. A load factor (default 0.75) triggers resizing (rehashing) when the map becomes too full.
HashMap provides O(1) average operations with no ordering guarantee. LinkedHashMap maintains insertion (or access) order while still offering O(1) average operations. TreeMap stores keys in sorted natural order (or a provided Comparator) with O(log n) operations, backed by a Red-Black tree.
'final' prevents a variable from being reassigned, a method from being overridden, or a class from being subclassed. 'finally' is a block in a try-catch-finally statement that always executes regardless of whether an exception was thrown. 'finalize()' is a deprecated Object method that was called by the GC before collecting an object — it is unreliable and should not be used.
'volatile' ensures that reads and writes to a variable are always done directly from/to main memory, making changes visible across threads. It prevents instruction reordering for that variable but does not provide atomicity for compound operations (like i++). Use it for simple flags or state variables shared across threads when you do not need full synchronization.
The 'synchronized' keyword is simpler and releases the lock automatically when the block exits or an exception is thrown. ReentrantLock (java.util.concurrent.locks) offers more control: timed lock attempts (tryLock), interruptible locking, fairness policies, and multiple condition variables. Prefer synchronized for simple cases; use ReentrantLock for advanced concurrency requirements.
A functional interface has exactly one abstract method (annotated with @FunctionalInterface, though the annotation is optional). Lambda expressions provide a concise inline implementation of that single method, eliminating boilerplate anonymous class syntax. Examples from java.util.function include Runnable, Callable, Predicate, Function, Consumer, and Supplier.
The Stream API (Java 8+) provides a declarative pipeline for processing sequences of elements with operations like filter, map, reduce, collect, and forEach. Unlike collections, streams do not store data, are lazily evaluated (intermediate operations run only when a terminal operation is invoked), and can be parallelized via parallelStream(). Collections are data structures; streams are computational pipelines over data.
Comparable defines a natural ordering by implementing compareTo() within the class itself, allowing objects to be sorted by default (e.g., Collections.sort()). Comparator is an external strategy object defining a custom ordering without modifying the class. Use Comparable for the primary sort order and Comparator for alternative or ad-hoc orderings.
Garbage collection automatically reclaims heap memory occupied by objects that are no longer reachable. Major algorithms include Serial GC (single-threaded, small apps), Parallel GC (throughput-focused, multi-threaded), G1 GC (default since Java 9, balances throughput and latency by dividing heap into regions), and ZGC/Shenandoah (low-latency collectors for large heaps with sub-millisecond pause goals).
Generics enable type-safe parameterized classes and methods (e.g., List<String>), catching type errors at compile time instead of runtime. Type erasure means generic type parameters are removed by the compiler and replaced with their bounds (usually Object) in the bytecode — generics exist only at compile time, not at runtime. This means you cannot do 'new T()' or check 'instanceof T' directly.
Singleton ensures one instance (e.g., a connection pool manager); Builder constructs complex objects step-by-step (e.g., StringBuilder, Lombok's @Builder); Factory/Abstract Factory creates objects without specifying concrete classes (e.g., Calendar.getInstance()); Observer notifies dependents of state changes (e.g., Java's EventListener); Strategy encapsulates interchangeable algorithms (e.g., Comparator passed to Collections.sort()).
© RM Full Stack & AI Engineer · All interview questions · Roadmaps · Open the app