Top 50 Java Interview Questions and Answers

Preparing for a Java interview? Whether you're applying for a junior, mid-level, or senior Java developer position, you need more than memorized definitions. Interviewers often start with fundamental Java concepts and then use follow-up questions to test how well you understand the language and its behavior in real-world applications.
This guide covers 50 of the most important Java interview questions, from core Java fundamentals and object-oriented programming to collections, exceptions, streams, concurrency, JVM concepts, and modern Java features.
For each question, you'll find a concise explanation, key points to remember, and, where relevant, the kind of follow-up question an interviewer may ask.
Don't try to memorize every answer word for word. A strong Java interview answer explains not only what something is, but also why it works that way and when you would use it.
Questions
What is Java?
- Difficulty
- 🟢 Beginner
- Topic
- Java Fundamentals
- What the interviewer is testing
- Your understanding of Java as a programming language, its main characteristics, and how Java code runs.
🎯 What a strong candidate should say
"Java is a high-level, object-oriented programming language designed to be platform-independent. Java source code is compiled into bytecode, which runs on the Java Virtual Machine (JVM). This allows the same compiled code to run on different operating systems as long as they have a compatible JVM.
Java is widely used for backend applications, enterprise systems, APIs, Android applications, and distributed systems. Its ecosystem also provides features such as automatic memory management through garbage collection, strong type checking, and a large standard library."
That's a strong answer because it covers what Java is, how it achieves portability, and where it's used without turning the answer into a history lesson.
🔍 Let's understand it
Java is both a programming language and an ecosystem built around the JVM.
The basic execution process looks like this:
Java source code
↓
javac compiler
↓
Java bytecode
↓
JVM
↓
Operating System
For example, you might write:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
The Java compiler (javac) compiles this source code into bytecode, usually stored in a .class file.
The JVM then loads and executes that bytecode.
This is the idea behind Java's famous:
"Write once, run anywhere."
The important distinction is that Java bytecode is platform-independent, while the JVM itself is platform-specific. A Windows machine, Linux server, and macOS machine each have their own JVM implementation.
Java is also statically typed, meaning types are checked at compile time:
int age = 30; String name = "Alex";And Java uses automatic memory management. Developers don't normally allocate and free memory manually; the JVM's garbage collector identifies objects that are no longer reachable and can reclaim their memory.
🎤 Interviewer follow-up
"Is Java completely platform-independent?"
A strong answer: "The Java language and compiled bytecode are platform-independent, but the JVM is platform-specific. That's why Java can run on different operating systems: each operating system has a JVM capable of executing the same bytecode."
A common follow-up might be: "Then why is Java called both a programming language and a platform?"
You could say: "Java is a programming language because it defines the syntax and semantics used to write programs. It's also considered a platform because the JVM and Java runtime environment provide the environment needed to execute those programs across different operating systems."
💡 How to make your answer stronger
If you're interviewing for a backend or senior Java position, don't stop at: "Java is object-oriented and platform-independent."That's technically correct but very generic.
A stronger candidate can connect Java's design to real-world development: "Java's portability, strong type system, mature ecosystem, garbage collection, and concurrency support make it particularly well suited to large-scale backend and enterprise applications."
That shows you understand why Java is still widely used, rather than simply knowing its definition.
⚠️ Common mistakes
- "Java is platform-independent because the JVM is platform-independent." The JVM is actually platform-specific. The bytecode is what provides portability.
- "Java is 100% object-oriented." This is an oversimplification because Java has primitive types such as
int,boolean, anddouble, which aren't objects. - Giving a five-minute history of Java. The interviewer asked what Java is, not who created it or when it was released. Start with the definition and let the interviewer ask for more detail.
🧠 Remember
Java = high-level, statically typed, object-oriented language + JVM ecosystem.
Source code → bytecode → JVM → operating system
The bytecode is portable; the JVM is platform-specific.
What are the main features of Java?
- Difficulty
- 🟢 Beginner
- Topic
- Java Fundamentals
- What the interviewer is testing
- Whether you understand the characteristics that make Java different from other programming languages and why it is widely used for building applications.
🎯 What a strong candidate should say
Java's main features include platform independence, object-oriented programming, strong static typing, automatic memory management, security, robustness, multithreading, and a large ecosystem.
Java achieves platform independence by compiling source code into bytecode that runs on the JVM. It also provides automatic garbage collection, built-in support for multithreading, strong compile-time type checking, exception handling, and a mature standard library and ecosystem.
These features make Java particularly suitable for building large, maintainable, and scalable applications.
This is a much better interview answer than simply listing ten buzzwords. Name the feature, then briefly explain why it matters.
🔍 Let's understand it
Here are the main Java features worth knowing for an interview:
1. Platform independent
Java source code is compiled into bytecode, which can run on any compatible JVM.
Java source
↓
Compiler
↓
Bytecode
↓
JVM
↓
Windows / Linux / macOS
This is the foundation of Java's "write once, run anywhere" philosophy.
2. Object-oriented
Java is primarily an object-oriented programming language.
It supports concepts such as:
- Encapsulation
- Inheritance
- Polymorphism
- Abstraction
These concepts help developers organize large applications into reusable and maintainable components.
3. Statically typed
Java is statically typed, meaning variables have a type that is checked at compile time.
int age = 30; String name = "John";The compiler can catch many type-related errors before the application runs. This becomes particularly valuable in large codebases where catching errors early can reduce runtime problems.
4. Automatic memory management
Java manages memory automatically using garbage collection.
Developers generally don't need to explicitly free objects when they're no longer needed.
User user = new User();When the object is no longer reachable, the garbage collector can eventually reclaim its memory.
5. Robust
Java includes several mechanisms designed to make applications more reliable:
- Strong type checking
- Exception handling
- Automatic memory management
- Runtime checks
- Avoidance of direct pointer manipulation
For example:
try {
processPayment();
} catch (PaymentException e) {
handleFailure(e);
}
Exception handling allows applications to deal with unexpected situations in a controlled way.
6. Multithreaded
Java has built-in support for concurrent programming.
You can work with:
- Threads
- Executors
- Synchronization
- Locks
- Concurrent collections
- CompletableFuture
- Virtual threads
Modern Java makes concurrent programming particularly powerful with virtual threads, introduced as a final feature in Java 21.
7. Secure
Java was designed with security in mind and provides mechanisms such as:
- Strong type checking
- Access modifiers
- Bytecode verification
- No direct pointer arithmetic
- A security-oriented runtime architecture
However, avoid saying "Java applications are secure by default." Java provides security mechanisms, but application security still depends heavily on how developers build and configure the application.
8. High performance
Java is not traditionally considered a low-level language like C or C++, but modern JVMs can achieve very high performance.
The JVM uses Just-In-Time (JIT) compilation to compile frequently executed bytecode into optimized native machine code at runtime.
This allows the JVM to optimize application performance based on actual execution behavior.
9. Rich ecosystem
One of Java's biggest strengths isn't actually the language itself — it's the ecosystem around it.
Java has:
- A large standard library
- Mature build tools such as Maven and Gradle
- Frameworks such as Spring and Spring Boot
- Libraries for databases, messaging, networking, security, testing, and more
- A huge developer community
This is one of the reasons Java remains popular for enterprise and backend development.
🎤 Interviewer follow-up
"Which Java feature do you think is most important for enterprise applications?"
There isn't one universally correct answer. A strong response could be:
"I wouldn't choose a single feature because Java's strength comes from the combination of its characteristics. Platform independence, strong typing, automatic memory management, concurrency support, and its mature ecosystem all contribute to making Java suitable for large-scale applications. For enterprise development specifically, I would say the ecosystem is one of its biggest advantages because it provides mature solutions for most common backend problems."
Another common follow-up:
"Is Java really platform-independent?"
"The bytecode produced by the Java compiler is platform-independent, but the JVM is platform-specific. Each operating system needs a JVM implementation capable of executing that bytecode."
💡 How to make your answer stronger
Don't simply recite: "Java is robust, secure, portable, object-oriented, multithreaded..." That sounds memorized.
Instead, connect the features to real-world benefits.
For example:
"Java's strong typing helps catch errors early, garbage collection simplifies memory management, the JVM provides portability and runtime optimization, and the ecosystem gives developers mature tools for building large-scale applications."
That sounds much more like someone who has actually worked with Java.
For a senior Java interview, you can also mention modern JVM features such as virtual threads, JIT compilation, and improvements in garbage collection if the interviewer asks you to go deeper.
⚠️ Common mistakes
- Just listing features without explaining them
- Saying Java is "100% object-oriented" (Java has primitive types)
- Saying garbage collection means Java has no memory problems
- Saying Java is automatically secure
🧠 Remember
- Platform independence → bytecode + JVM
- Object-oriented → reusable, maintainable design
- Static typing → errors caught early
- Garbage collection → automatic memory management
- Concurrency → threads, executors, virtual threads
- JIT + JVM → runtime optimization
- Mature ecosystem → frameworks, libraries, tools
The real interview trick: don't just name the features — explain why they matter.
What is the difference between JDK, JRE, and JVM?
- Difficulty
- 🟢 Beginner
- Topic
- Java Fundamentals / JVM
- What the interviewer is testing
- Whether you understand how Java applications are developed and executed, and the relationship between the JDK, JRE, and JVM.
🎯 What a strong candidate should say
The JVM is the component that actually executes Java bytecode. The JRE provides the JVM together with the libraries and runtime components needed to run Java applications. The JDK is the complete development kit and includes the tools needed to develop Java applications, as well as the components required to run them.
In simple terms: JVM runs Java programs, JRE provides the environment to run them, and JDK provides the tools to develop them.
A useful way to remember the relationship is:
JDK
├── Development tools
│ ├── javac
│ ├── javadoc
│ ├── jdb
│ └── other tools
│
└── Runtime components
├── Java libraries
└── JVM
🔍 Let's understand it
1. JVM — Java Virtual Machine
The JVM is the runtime engine that executes Java bytecode.
When you compile:
public class Hello {
public static void main(String[] args) {
System.out.println("Hello");
}
}
the Java compiler produces bytecode:
Hello.java
↓
javac
↓
Hello.class
↓
JVM
↓
Program execution
The JVM is responsible for things such as:
- Loading classes
- Verifying bytecode
- Executing bytecode
- Managing memory
- Garbage collection
- Runtime optimizations such as JIT compilation
An important detail is that the JVM itself is platform-specific. There are different JVM implementations for different operating systems and architectures.
2. JRE — Java Runtime Environment
The JRE is the environment required to run Java applications.
Conceptually:
JRE = JVM + Java runtime libraries + runtime components
For example, if you only wanted to run an already-compiled Java application, you wouldn't need the Java compiler. Historically, the JRE was distributed separately from the JDK.
However, there's an important modern-Java nuance: Since Java 11, Oracle no longer provides a separate JRE distribution. Modern JDK distributions are generally what developers install, and custom runtime images can be created when a smaller runtime is needed.
So if an interviewer asks about JRE, it's good to explain the concept while acknowledging the modern Java ecosystem.
3. JDK — Java Development Kit
The JDK is what developers use to build Java applications. It includes the tools required to compile, debug, package, document, and run Java applications.
For example:
javac Hello.javajavac is the Java compiler and is part of the JDK.
Other common JDK tools include:
javac→ Java compilerjava→ launches a Java applicationjavadoc→ generates API documentationjdb→ Java debuggerjar→ creates/manages JAR filesjlink→ creates custom runtime images
The exact tools available can vary somewhat between JDK versions and distributions.
🧩 The easiest way to visualize it
Think of it like this:
JDK
┌─────────────────────┐
│ Development tools │
│ │
│ javac, javadoc, │
│ jdb, jar, ... │
│ │
│ Runtime │
│ ┌─────────────┐ │
│ │ JVM │ │
│ └─────────────┘ │
│ + Java libraries │
└─────────────────────┘
Or, for an interview:
JDK → develop
JRE → run
JVM → execute
🎤 Interviewer follow-up
"Can you run a Java program without installing the JDK?"
"You don't need the development tools of the JDK to execute an already-compiled Java application. Historically, you could use a JRE for this. With modern Java, separate JRE distributions aren't generally provided, so applications can use a JDK installation or a custom runtime image created with tools such as jlink."
Another common follow-up:
"Is the JVM platform-independent?"
"No. The Java bytecode is platform-independent, but the JVM implementation is platform-specific. That's what allows the same bytecode to execute on different operating systems."
This is a particularly good follow-up because it tests whether you actually understand Java's platform independence, rather than simply memorizing the phrase "write once, run anywhere."
💡 How to make your answer stronger
A beginner might say: "JDK is for development, JRE is for running applications, and JVM executes bytecode." That's correct.
A stronger candidate adds the modern Java context: "Conceptually, the JRE is the runtime environment consisting of the JVM and runtime libraries, while the JDK adds development tools. However, since Java 11, a separate JRE is no longer distributed by Oracle, so in modern Java development we typically install a JDK and can create custom runtime images when needed."
That small clarification demonstrates that you understand the difference between the traditional Java architecture and how modern Java is actually distributed.
⚠️ Common mistakes
- Saying JDK and JVM are the same thing — They're not. The JVM executes bytecode, while the JDK provides the development tools and runtime components needed to build and run Java applications.
- Saying JRE contains the Java compiler — It doesn't. The compiler,
javac, is a JDK development tool. - Saying you always need a JRE installed separately — That's outdated for modern Java distributions.
- Saying JVM converts Java source code directly into machine code — The normal process is:
.java ↓ javac ↓ bytecode (.class) ↓ JVM ↓ execution
The JVM can then use mechanisms such as JIT compilation to compile frequently executed bytecode into native machine code at runtime.
🧠 Remember
- JDK = tools for developing Java applications
- JRE = conceptual runtime environment
- JVM = engine that executes bytecode
Source code → JDK/compiler → bytecode → JVM → execution
And the key modern-Java detail: Since Java 11, a separate JRE distribution is no longer provided by Oracle.
Why is Java platform independent?
- Difficulty
- 🟢 Beginner
- Topic
- Java Fundamentals
- What the interviewer is testing
- Your understanding of Java's portability.
🎯 What a strong candidate should say
Java is platform-independent because its source code is compiled into platform-independent bytecode, which runs on the JVM. Each operating system has its own JVM implementation, allowing the same bytecode to run across different platforms.
🔍 Let's understand it
Java code → javac → Bytecode → JVM → OS
The key is that bytecode is platform-independent, while the JVM is platform-specific.
🎤 Interviewer follow-up
"Why is the JVM platform-specific?"
"Because each operating system requires a JVM implementation that knows how to interact with that operating system."
💡 How to make your answer stronger
"Java achieves portability by separating the compiled bytecode from the underlying operating system through the JVM."
⚠️ Common mistake
- Saying "the JVM is platform-independent." The bytecode is platform-independent; the JVM is not.
🧠 Remember
Write once → compile to bytecode → run anywhere with a compatible JVM.
What is the difference between == and equals()?
- Difficulty
- 🟢 Beginner
- Topic
- Core Java
- What the interviewer is testing
- Whether you understand reference comparison versus object equality.
🎯 What a strong candidate should say
For primitives, == compares values. For objects, == compares references, meaning whether two variables point to the same object. equals() is used to compare the logical equality of two objects, based on how the class implements it.
🔍 Let's understand it
String a = new String("Java");
String b = new String("Java");
a == b; // false
a.equals(b); // true
Why? a and b are different objects, so == returns false. Their contents are equal, so equals() returns true.
🎤 Interviewer follow-up
"What happens if you override equals() but not hashCode()?"
"It can break the contract required by hash-based collections such as HashMap and HashSet. If two objects are equal according to equals(), they must have the same hashCode()."
💡 How to make your answer stronger
Mention that equals() is not automatically a value comparison—its behavior depends on the class's implementation.
⚠️ Common mistake
- Saying "== always compares memory addresses." For objects, it compares references, not necessarily raw memory addresses.
🧠 Remember
- Primitives → == compares values
- Objects → == compares references
- Objects → equals() compares logical equality
Why should equals() and hashCode() be overridden together?
- Difficulty
- 🟡 Intermediate
- Topic
- Object Equality / Collections
- What the interviewer is testing
- Your understanding of the equals()/hashCode() contract and hash-based collections.
🎯 What a strong candidate should say
If two objects are considered equal by equals(), they must return the same hashCode(). If you override equals() without overriding hashCode(), equal objects can produce different hash codes, causing incorrect behavior in collections such as HashMap and HashSet.
🔍 Let's understand it
Hash-based collections use hashCode() to determine where an object belongs, and equals() to check whether objects are actually equal.
Set<User> users = new HashSet<>();
users.add(new User(1, "John"));
users.contains(new User(1, "John")); // may be false
If User overrides equals() but not hashCode(), the two logically equal objects may have different hash codes, so the HashSet may look in a different bucket and fail the containment check.
🎤 Interviewer follow-up
"Does the reverse rule also apply?"
"No. If two objects have the same hash code, they don't have to be equal. Hash collisions are allowed."
💡 How to make your answer stronger
Mention the core contract:
equals()returns true → samehashCode()- But the reverse is not required.
⚠️ Common mistake
- Saying objects with the same
hashCode()must be equal. They don't—hash collisions can occur.
🧠 Remember
- Override
equals()→ overridehashCode() - Equal objects must have equal hash codes.
What is the difference between final, finally, and finalize()?
- Difficulty
- 🟢 Beginner
- Topic
- Core Java / Exception Handling
- What the interviewer is testing
- Whether you can distinguish three unrelated Java concepts that have similar names.
🎯 What a strong candidate should say
final is a keyword used to prevent changes, finally is a block used to execute cleanup code after a try/catch, and finalize() was a method that could be called by the garbage collector before an object was reclaimed. However, finalize() was deprecated in Java 9 and removed in Java 18.
🔍 Let's understand it
final
final int MAX = 100;A final variable cannot be reassigned. It can also be used with methods and classes to prevent overriding or inheritance.
finally
try {
process();
} finally {
cleanup();
}The finally block is used for cleanup that should normally happen regardless of whether an exception occurs.
finalize()
Historically:
@Deprecated // deprecated in Java 9, removed in Java 18
protected void finalize() throws Throwable {
// cleanup
}It was intended for cleanup before garbage collection, but it was unreliable and is now removed from modern Java. Prefer try-with-resources or AutoCloseable for resource cleanup.
🎤 Interviewer follow-up
"What should you use instead of finalize()?"
"For resources such as files or database connections, use try-with-resources with AutoCloseable. For general object cleanup, design explicit lifecycle methods rather than relying on garbage collection."
💡 How to make your answer stronger
Mention that finalize() should not be used in modern Java. Knowing that it was removed in Java 18 shows awareness of current Java versions.
⚠️ Common mistake
- Saying
finallyis always executed. Situations such asSystem.exit()or JVM termination can prevent it from running.
🧠 Remember
- final → prevent changes
- finally → cleanup block
- finalize() → obsolete; removed in Java 18
What are the four pillars of OOP?
- Difficulty
- 🟢 Beginner
- Topic
- Object-Oriented Programming
- What the interviewer is testing
- Your understanding of the fundamental principles behind object-oriented design.
🎯 What a strong candidate should say
The four pillars of OOP are Encapsulation, Abstraction, Inheritance, and Polymorphism. Together, they help structure code so it is easier to maintain, reuse, and extend.
🔍 Let's understand it
- Encapsulation → Bundling data and behavior together while controlling access to the object's state.
- Abstraction → Hiding implementation details and exposing only what is necessary.
- Inheritance → Allowing a class to reuse or extend behavior from another class.
- Polymorphism → Allowing the same interface or method call to behave differently depending on the object.
For example:
Animal animal = new Dog();
animal.makeSound(); // Dog's implementation
The reference is Animal, but the actual object's behavior is determined by Dog — that's polymorphism.
🎤 Interviewer follow-up
"What's the difference between abstraction and encapsulation?"
"Abstraction focuses on what an object does while hiding unnecessary implementation details. Encapsulation focuses on how the object's state and behavior are packaged and protected."
💡 How to make your answer stronger
Don't just memorize the four names. Give a one-line explanation and a simple Java example for each. That demonstrates understanding rather than recall.
⚠️ Common mistake
- Saying inheritance is always the best way to reuse code. In Java, composition is often preferred over inheritance when there isn't a true is-a relationship.
🧠 Remember
- Encapsulation → protect
- Abstraction → hide
- Inheritance → reuse/extend
- Polymorphism → one interface, different behavior
What is the difference between abstraction and encapsulation?
- Difficulty
- 🟢 Beginner
- Topic
- Object-Oriented Programming
- What the interviewer is testing
- Whether you understand two closely related OOP concepts and can clearly distinguish their purposes.
🎯 What a strong candidate should say
Abstraction focuses on hiding unnecessary implementation details and exposing only what the object does. Encapsulation focuses on bundling data and behavior together while controlling access to the object's internal state.
In simple terms: abstraction hides complexity, while encapsulation protects the state.
🔍 Let's understand it
Abstraction:
interface Payment {
void pay();
}The interface tells us what can be done without exposing how the payment is processed.
Encapsulation:
class BankAccount {
private double balance;
public double getBalance() {
return balance;
}
}The balance is hidden using private, and access is controlled through methods.
🎤 Interviewer follow-up
"Can you give a real-world example?"
"A car's steering wheel is an example of abstraction—you use it without knowing how the steering mechanism works. Encapsulation is like the car's internal components being protected inside the system, with controlled ways to interact with them."
💡 How to make your answer stronger
A concise distinction that works well in interviews:
- Abstraction = what the object exposes.
- Encapsulation = how the object's state is protected.
⚠️ Common mistake
- Saying abstraction and encapsulation are the same because both "hide information." They hide different things and solve different problems.
🧠 Remember
- Abstraction → hide implementation complexity
- Encapsulation → protect internal state
What is method overloading?
- Difficulty
- 🟢 Beginner
- Topic
- OOP / Polymorphism
- What the interviewer is testing
- Whether you understand compile-time polymorphism and how Java distinguishes overloaded methods.
🎯 What a strong candidate should say
Method overloading means having multiple methods with the same name but different parameter lists in the same class. The methods can differ in the number, type, or order of parameters. Overloading is resolved at compile time.
🔍 Let's understand it
void print(String text) {
System.out.println(text);
}
void print(int number) {
System.out.println(number);
}
void print(String text, int count) {
// ...
}All three methods are named print, but they accept different parameters.
The return type alone cannot be used to overload a method.
🎤 Interviewer follow-up
"Is method overloading compile-time or runtime polymorphism?"
"Compile-time polymorphism, because the compiler determines which overloaded method to call based on the method signature and the arguments."
💡 How to make your answer stronger
Mention that overloading improves readability by allowing related operations to share the same method name while accepting different inputs.
⚠️ Common mistake
❌ Saying you can overload methods by changing only the return type.
int calculate() { /* ... */ }
double calculate() { /* ... */ } // ❌ Not valid🧠 Remember
- Same method name + different parameters = overloading
- Resolved at compile time.
What is method overriding?
- Difficulty
- 🟢 Beginner
- Topic
- OOP / Polymorphism
- What the interviewer is testing
- Whether you understand inheritance and runtime polymorphism in Java.
🎯 What a strong candidate should say
Method overriding occurs when a subclass provides its own implementation of a method inherited from its parent class. The method must have the same signature, and the actual implementation is determined at runtime based on the object's type.
🔍 Let's understand it
class Animal {
void makeSound() {
System.out.println("Some sound");
}
}
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Woof");
}
}
Animal animal = new Dog();
animal.makeSound(); // Woof
Even though the reference type is Animal, Java calls Dog's implementation. This is runtime polymorphism.
🎤 Interviewer follow-up
"Can a static method be overridden?"
"No. Static methods are associated with the class rather than the object. If a subclass defines a static method with the same signature, it is called method hiding, not overriding."
💡 How to make your answer stronger
Mention the @Override annotation. It isn't required by the JVM, but it helps the compiler detect mistakes in the method signature.
⚠️ Common mistake
- Saying overriding requires a different method signature. That's overloading. Overriding requires the same method signature, subject to Java's rules for return types and access modifiers.
🧠 Remember
- Overriding → subclass changes inherited behavior
- Same signature → resolved at runtime
@Override→ helps catch mistakes
What is the difference between an interface and an abstract class?
- Difficulty
- 🟡 Intermediate
- Topic
- OOP / Abstraction
- What the interviewer is testing
- Whether you understand when to use an interface versus an abstract class in Java.
🎯 What a strong candidate should say
An interface defines a contract that a class can implement, while an abstract class provides a common base that can contain both abstract and concrete behavior, as well as instance state. A class can implement multiple interfaces but can extend only one class.
In general, use an interface for a capability or contract, and an abstract class when related classes need to share state or implementation.
🔍 Let's understand it
Interface:
interface Flyable {
void fly();
}
class Bird implements Flyable {
@Override
public void fly() {
System.out.println("Flying");
}
}A class can implement multiple interfaces:
class Bird implements Flyable, Swimmable {
}Abstract class:
abstract class Animal {
protected String name;
abstract void makeSound();
void sleep() {
System.out.println("Sleeping");
}
}An abstract class can have state, constructors, concrete methods, and abstract methods.
🎤 Interviewer follow-up
"Can an interface have implemented methods?"
"Yes. Since Java 8, interfaces can have default and static methods with implementations. Since Java 9, they can also have private methods."
💡 How to make your answer stronger
Don't say "interfaces are for methods and abstract classes are for implementation." That's outdated.
A better distinction is:
- Interface → contract/capability
- Abstract class → shared state and behavior
⚠️ Common mistake
- Saying a class can extend multiple abstract classes. Java supports single class inheritance, so a class can extend only one class but implement multiple interfaces.
🧠 Remember
- Interface → contract + multiple implementation
- Abstract class → shared state + shared behavior
- Class → extends one class, implements multiple interfaces
Can a Java class implement multiple interfaces?
- Difficulty
- 🟢 Beginner
- Topic
- OOP / Interfaces
- What the interviewer is testing
- Whether you understand Java's support for multiple interface implementation.
🎯 What a strong candidate should say
Yes. A Java class can implement multiple interfaces. This allows a class to combine multiple contracts or capabilities, even though Java doesn't support multiple class inheritance.
🔍 Let's understand it
interface Flyable {
void fly();
}interface Swimmable {
void swim();
}class Duck implements Flyable, Swimmable {
public void fly() {
System.out.println("Flying");
}
public void swim() {
System.out.println("Swimming");
}
}A class can implement as many interfaces as needed:
class Duck implements Flyable, Swimmable, Runnable {
}🎤 Interviewer follow-up
"What happens if two interfaces have the same default method?"
"The class must resolve the conflict by overriding that method."
💡 How to make your answer stronger
Point out the key design benefit: Interfaces allow Java to achieve multiple inheritance of type without multiple inheritance of classes.
⚠️ Common mistake
- Saying a class can extend multiple interfaces. The correct syntax is:
class MyClass implements InterfaceA, InterfaceB
🧠 Remember
- One class → one superclass
- One class → multiple interfaces
Can Java support multiple inheritance?
- Difficulty
- 🟢 Beginner
- Topic
- OOP / Inheritance
- What the interviewer is testing
- Whether you understand Java's approach to multiple inheritance and why classes cannot have multiple parent classes.
🎯 What a strong candidate should say
Java does not support multiple inheritance of classes. A class can extend only one class, but it can implement multiple interfaces. This avoids ambiguity, such as the classic "diamond problem," where a class could inherit conflicting implementations from multiple parents.
🔍 Let's understand it
This is not allowed:
class C extends A, B { } // ❌But this is valid:
class C extends A implements B, D { } // ✅Java uses interfaces to provide a way for a class to have multiple types or capabilities without allowing multiple class inheritance.
🎤 Interviewer follow-up
"What is the diamond problem?"
"It's an ambiguity that can occur when a class inherits the same method through multiple parent classes. Java avoids this problem by allowing only single class inheritance."
💡 How to make your answer stronger
Mention that Java does support multiple inheritance of type through interfaces, including interfaces with default methods, but conflicts between default methods must be resolved explicitly.
⚠️ Common mistake
- Saying "Java doesn't support multiple inheritance." That's too broad. A more accurate statement is: Java doesn't support multiple inheritance of classes, but it supports multiple interface implementation.
🧠 Remember
- Multiple classes → ❌
- Multiple interfaces → ✅
- One class + multiple interfaces → ✅
What is composition and how is it different from inheritance?
- Difficulty
- 🟡 Intermediate
- Topic
- OOP / Design
- What the interviewer is testing
- Whether you understand the difference between "has-a" and "is-a" relationships and can choose the appropriate design.
🎯 What a strong candidate should say
Composition means building a class by combining other objects that it uses, creating a "has-a" relationship. Inheritance creates an "is-a" relationship where a class extends another class. Composition is generally more flexible because components can be changed or replaced without changing the inheritance hierarchy.
🔍 Let's understand it
Inheritance — "is-a":
class Dog extends Animal {
}
A Dog is an Animal.
Composition — "has-a":
class Car {
private Engine engine;
Car(Engine engine) {
this.engine = engine;
}
}
A Car has an Engine.
With composition, you can also easily change the implementation:
Car car = new Car(new ElectricEngine());
🎤 Interviewer follow-up
"Why is composition often preferred over inheritance?"
"Composition reduces coupling and makes behavior easier to change or reuse. With inheritance, a subclass is tightly coupled to its parent and its implementation."
💡 How to make your answer stronger
Don't say "composition is always better." A better answer is:
"Use inheritance when there's a genuine is-a relationship and polymorphism is useful. Use composition when you want to assemble behavior or keep components interchangeable."
⚠️ Common mistake
- Using inheritance just to reuse code. Code reuse alone doesn't necessarily justify an inheritance relationship.
🧠 Remember
- Inheritance → "is-a"
- Composition → "has-a"
- Composition → usually more flexible and less tightly coupled.
What are immutable objects?
- Difficulty
- 🟢 Beginner
- Topic
- Core Java / OOP
- What the interviewer is testing
- Whether you understand immutability and why it is useful in Java.
🎯 What a strong candidate should say
An immutable object is an object whose state cannot be changed after it is created. If you need a different state, you create a new object instead. String is a common example of an immutable Java class.
🔍 Let's understand it
String name = "Java";
name = name.toUpperCase();
System.out.println(name); // JAVA
The original "Java" object wasn't changed. toUpperCase() created a new String object.
A typical immutable class:
final class User {
private final String name;
User(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
The class prevents modification of its state after construction.
🎤 Interviewer follow-up
"Why are immutable objects useful?"
"They're easier to reason about, naturally thread-safe when properly designed, and safe to share or use as keys in hash-based collections."
💡 How to make your answer stronger
Mention that immutability is more than making fields final. If a class contains mutable objects, you may need defensive copies to prevent its internal state from being changed indirectly.
⚠️ Common mistake
- Saying
finalautomatically makes an object immutable.finalprevents a reference from being reassigned, but the object it references can still be mutable.
🧠 Remember
- Immutable = state cannot change after creation.
finalreference ≠ immutable object.
How do you create an immutable class?
- Difficulty
- 🟡 Intermediate
- Topic
- Core Java / OOP
- What the interviewer is testing
- Whether you understand the practical rules for designing immutable objects.
🎯 What a strong candidate should say
To create an immutable class, make the class final, keep its fields private and final, initialize them through the constructor, provide no setters, and prevent mutable internal objects from being modified by using defensive copies.
🔍 Let's understand it
public final class User {
private final String name;
private final List<String> roles;
public User(String name, List<String> roles) {
this.name = name;
this.roles = List.copyOf(roles);
}
public String getName() {
return name;
}
public List<String> getRoles() {
return roles;
}
}Here:
- final class → cannot be subclassed
- private final fields → cannot be reassigned
- No setters → state can't be changed externally
List.copyOf()→ prevents the original mutable list from changing the object's state
🎤 Interviewer follow-up
"Is making all fields final enough?"
"No. If a field references a mutable object, that object can still be changed. You need defensive copying or immutable types for mutable fields."
💡 How to make your answer stronger
Mention defensive copying. That's usually the detail that separates a basic answer from a strong one.
⚠️ Common mistake
❌ Assuming this is immutable:
private final List<String> roles;The reference is final, but the list itself can still be modified.
🧠 Remember
Final class + private final fields + no setters + defensive copies = immutable class
Why is String immutable in Java?
- Difficulty
- 🟡 Intermediate
- Topic
- Core Java / Immutability
- What the interviewer is testing
- Whether you understand the practical reasons behind String immutability and its impact on Java applications.
🎯 What a strong candidate should say
String is immutable for several reasons: security, thread safety, string pooling, and performance. Since a String cannot change after creation, it can be safely shared between threads and reused in the String Pool. It also makes values such as class names, file paths, and credentials safer to use.
🔍 Let's understand it
String name = "Java";
name = name.concat(" 21");
System.out.println(name); // Java 21
The original "Java" string wasn't modified. concat() created a new String.
Immutability also makes the String Pool possible:
String a = "Java";
String b = "Java";
System.out.println(a == b); // true
Both variables can safely reference the same pooled object because its value cannot be changed.
🎤 Interviewer follow-up
"What would happen if String were mutable?"
"Sharing strings through the String Pool would become unsafe because changing one reference could unexpectedly change the value seen by other references. It could also introduce security and thread-safety problems."
💡 How to make your answer stronger
Mention that immutability allows String to cache its hash code, which is useful because strings are frequently used as keys in HashMap and other hash-based collections.
⚠️ Common mistake
- Saying String is immutable only because of the String Pool. The String Pool benefits from immutability, but security, thread safety, caching, and safe sharing are also important reasons.
🧠 Remember
- String immutable → safe sharing
- Enables String Pool reuse
- Thread safety + security benefits
- Cached hash code improves hash-based lookups
What is the String pool?
- Difficulty
- 🟢 Beginner
- Topic
- Core Java / Memory Management
- What the interviewer is testing
- Whether you understand how Java reuses String objects and why String immutability matters.
🎯 What a strong candidate should say
The String Pool is a special area managed by the JVM where string literals are stored and reused. If two string literals have the same value, Java can make them reference the same object instead of creating duplicates.
🔍 Let's understand it
String a = "Java";
String b = "Java";
System.out.println(a == b); // true
Both variables refer to the same pooled String.
But:
String a = new String("Java");
String b = new String("Java");
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true
new String() explicitly creates separate objects.
You can also add a string to the pool using intern():
String a = new String("Java");
String b = a.intern();
System.out.println(b == "Java"); // true
🎤 Interviewer follow-up
"Why can the JVM safely reuse String objects?"
"Because String is immutable. If strings could be changed, modifying one reference could unexpectedly affect every other reference to the same pooled object."
💡 How to make your answer stronger
Mention that the pool is primarily about reusing identical string values and reducing unnecessary object creation, rather than thinking of it simply as a normal collection of strings.
⚠️ Common mistake
- Saying all String objects are stored in the String Pool. Only strings that are interned, including string literals, are pooled. Strings created with
new String()are separate objects unless explicitly interned.
🧠 Remember
- String literal → pooled and potentially shared
new String()→ new objectintern()→ returns the pooled representation
What is the difference between String, StringBuilder, and StringBuffer?
- Difficulty
- 🟢 Beginner
- Topic
- Strings / Performance
- What the interviewer is testing
- Whether you understand immutability, mutability, and when to use each class.
🎯 What a strong candidate should say
String is immutable, while StringBuilder and StringBuffer are mutable. StringBuilder is generally preferred for modifying strings because it is faster in single-threaded code. StringBuffer provides synchronized methods, making it thread-safe but usually slower.
🔍 Let's understand it
With String (immutable), any modification creates a new object:
String text = "Hello";
text += " World"; // creates a new String
With StringBuilder, the existing object can be modified in place:
StringBuilder builder = new StringBuilder("Hello");
builder.append(" World");
System.out.println(builder); // Hello World
StringBuffer works similarly but its methods are synchronized (thread-safe, typically slower):
StringBuffer buffer = new StringBuffer("Hello");
buffer.append(" World");
🎤 Interviewer follow-up
"When would you use StringBuilder instead of String?"
"When I need to perform multiple string modifications, especially inside loops. StringBuilder avoids creating a new String object for every modification."
💡 How to make your answer stronger
A useful rule:
- String → immutable text
- StringBuilder → mutable text, preferred for most single-threaded use
- StringBuffer → mutable text with synchronization when shared across threads
⚠️ Common mistake
- Saying StringBuffer is always necessary when working with multiple threads. Thread safety depends on how the object is shared and used. Synchronization can also introduce unnecessary overhead.
🧠 Remember
- String → immutable
- StringBuilder → mutable + fast
- StringBuffer → mutable + synchronized
What are Java wrapper classes?
- Difficulty
- 🟢 Beginner
- Topic
- Core Java / Primitive Types
- What the interviewer is testing
- Whether you understand how Java represents primitive values as objects.
🎯 What a strong candidate should say
Wrapper classes are object representations of Java's primitive types. For example, int has Integer, double has Double, and boolean has Boolean. They are useful when an object is required, such as when working with collections or generic types.
🔍 Let's understand it
Java provides wrapper classes for all primitive types:
byte → Byte short → Short int → Integer long → Long float → Float double → Double char → Character boolean → Boolean
For example:
List<Integer> numbers = List.of(1, 2, 3);Generics work with objects, not primitives, so List<int> is not valid.
Java also provides autoboxing and unboxing:
Integer number = 10; // autoboxing
int value = number; // unboxing🎤 Interviewer follow-up
"Why can't we use primitives with generics?"
"Java generics work with reference types, not primitive types. Wrapper classes provide the object representation needed by generic APIs."
💡 How to make your answer stronger
Mention that wrapper classes also provide useful methods and constants, such as Integer.parseInt() and Integer.MAX_VALUE.
⚠️ Common mistake
- Saying wrapper classes are simply "objects containing primitives." They represent primitive values as objects and also provide additional functionality.
🧠 Remember
- Primitive → Wrapper
- int → Integer
- double → Double
- boolean → Boolean
- Wrappers allow primitive values to be used where objects are required.
What is autoboxing and unboxing?
- Difficulty
- 🟢 Beginner
- Topic
- Core Java / Wrapper Classes
- What the interviewer is testing
- Whether you understand the automatic conversion between primitives and their wrapper classes.
🎯 What a strong candidate should say
Autoboxing is the automatic conversion of a primitive into its corresponding wrapper object, while unboxing is the conversion of a wrapper object back into a primitive.
🔍 Let's understand it
Integer number = 10; // autoboxing
int value = number; // unboxing
Java performs these conversions automatically when needed.
🎤 Interviewer follow-up
"Can unboxing cause an exception?"
"Yes. Unboxing a null wrapper throws a NullPointerException."
Integer number = null;
int value = number; // NullPointerException
💡 How to make your answer stronger
Mention that autoboxing can create unnecessary objects, so you should still use primitives when you don't need objects.
⚠️ Common mistake
- Thinking
Integerandintare interchangeable in every situation. They differ in nullability, memory behavior, and how they interact with generics.
🧠 Remember
- Autoboxing: int → Integer
- Unboxing: Integer → int
What is the difference between primitive types and reference types?
- Difficulty
- 🟢 Beginner
- Topic
- Core Java / Memory
- What the interviewer is testing
- Whether you understand Java's two broad categories of types.
🎯 What a strong candidate should say
Primitive types represent simple values directly, such as int, double, and boolean. Reference types hold a reference to an object, such as String, arrays, and custom classes. Primitives cannot be null, while references can.
🔍 Let's understand it
int age = 30; // primitive
String name = "John"; // reference
User user = new User(); // reference
Java has 8 primitive types:
byte, short, int, long, float, double, char, boolean
Everything else is a reference type.
🎤 Interviewer follow-up
"Can a primitive be null?"
"No. null can only be assigned to reference types. If you need a nullable integer, for example, you use Integer instead of int."
💡 How to make your answer stronger
Mention that reference variables can point to objects, while a primitive variable directly represents its value.
⚠️ Common mistake
- Saying primitives are always stored on the stack and objects are always stored on the heap. That's an oversimplification. Where values are physically stored depends on JVM implementation and context.
🧠 Remember
- Primitive → value
- Reference → reference to an object
- Primitive → cannot be null
What is the difference between stack and heap memory?
- Difficulty
- 🟡 Intermediate
- Topic
- JVM / Memory Management
- What the interviewer is testing
- Whether you understand the main memory areas used during Java program execution.
🎯 What a strong candidate should say
The stack stores method call information, local variables, and references associated with each thread. The heap is the shared memory area where objects and arrays are generally allocated. Stack memory is thread-specific, while the heap is shared and managed by the garbage collector.
🔍 Let's understand it
public void calculate() {
int number = 10;
User user = new User();
}
Conceptually:
Stack Heap
----- ----
number = 10 User object
user ----------------> User objectEach thread has its own stack, while objects on the heap can be accessed by multiple threads.
🎤 Interviewer follow-up
"Can stack memory cause an error?"
"Yes. Excessive or deeply nested method calls can exhaust the stack and result in StackOverflowError."
💡 How to make your answer stronger
Avoid claiming that all local variables live on the stack. The JVM can optimize memory allocation, for example through escape analysis.
⚠️ Common mistake
- Saying the stack is garbage-collected. Stack frames are automatically removed as methods return. The garbage collector primarily manages objects in the heap.
🧠 Remember
- Stack → method calls + thread-specific execution data
- Heap → objects + shared memory
- Stack → thread-specific
- Heap → garbage collected
What is garbage collection?
- Difficulty
- 🟡 Intermediate
- Topic
- JVM / Memory Management
- What the interviewer is testing
- Whether you understand how Java automatically manages object memory.
🎯 What a strong candidate should say
Garbage collection is the JVM's automatic process of identifying objects that are no longer reachable and reclaiming the memory they occupy. It reduces the need for manual memory management, although it doesn't guarantee that memory is immediately reclaimed.
🔍 Let's understand it
User user = new User();
user = null;
Once the User object is no longer reachable, it becomes eligible for garbage collection. The JVM's garbage collector can eventually reclaim its memory.
Modern JVMs use different garbage collectors, such as G1, ZGC, and Shenandoah, depending on the Java version and configuration.
🎤 Interviewer follow-up
"Can you force garbage collection in Java?"
"No. You can request it with System.gc(), but the JVM is not required to perform garbage collection as a result."
💡 How to make your answer stronger
Use the phrase "eligible for garbage collection" rather than saying an object is immediately deleted.
⚠️ Common mistake
- Saying garbage collection prevents memory leaks. Java can still have memory leaks when objects remain reachable even though the application no longer needs them.
🧠 Remember
- GC → finds unreachable objects → reclaims their memory.
System.gc()→ request, not a guarantee.
How does HashMap work internally?
- Difficulty
- 🟡 Intermediate
- Topic
- Collections / Hashing
- What the interviewer is testing
- Whether you understand hashing, buckets, collisions, and how HashMap achieves fast lookups.
🎯 What a strong candidate should say
HashMap stores key-value pairs in an internal table of buckets. It uses the key's hash code to determine a bucket, then uses equals() to find the correct key within that bucket. With a good hash distribution, get() and put() are O(1) on average.
🔍 Let's understand it
Conceptually:
key → hashCode() → bucket → equals() → value
For example:
Map<String, Integer> map = new HashMap<>();
map.put("Java", 100);
Integer score = map.get("Java");The hash determines where "Java" should be stored. When retrieving it, HashMap uses the hash and equals() to locate the correct entry.
In modern Java, heavily-colliding buckets can be converted into balanced trees (tree bins), improving lookup performance in those cases.
🎤 Interviewer follow-up
"What happens when two keys have the same hash code?"
"A hash collision occurs. The keys can be stored in the same bucket, and equals() is then used to distinguish between them."
💡 How to make your answer stronger
Mention that hashCode() narrows down the search (bucket), while equals() determines actual key equality.
⚠️ Common mistake
- Saying HashMap uses only hashCode() to determine whether two keys are equal. Two different keys can share the same hash code.
🧠 Remember
- hashCode() → finds the bucket
- equals() → finds the key
- Average
get()/put()→ O(1)
What happens when two keys have the same hash code?
- Difficulty
- 🟡 Intermediate
- Topic
- Collections / Hashing
- What the interviewer is testing
- Whether you understand hash collisions and the role of equals().
🎯 What a strong candidate should say
A hash collision occurs when two different keys produce the same hash code. HashMap can store both keys in the same bucket and uses equals() to determine which key actually matches.
🔍 Let's understand it
Key A ──┐
├── same bucket → equals() → correct entry
Key B ──┘
A collision does not mean the keys are considered equal.
a.hashCode() == b.hashCode() // could be true
a.equals(b) // could be false🎤 Interviewer follow-up
"Can two equal objects have different hash codes?"
"No. If equals() returns true, the objects must have the same hash code."
💡 How to make your answer stronger
Mention that good hashCode() implementations aim for an even distribution of keys to minimize collisions.
⚠️ Common mistake
- Saying the same hash code means the objects are equal. It doesn't. Hash collisions are allowed.
🧠 Remember
- Same hash → possible collision
- Same hash ≠ equal objects
What is the difference between HashMap and Hashtable?
- Difficulty
- 🟡 Intermediate
- Topic
- Collections / Concurrency
- What the interviewer is testing
- Whether you understand the differences between these two map implementations and why Hashtable is rarely used in modern Java.
🎯 What a strong candidate should say
HashMap is generally preferred in modern Java. It allows one null key and multiple null values and is not synchronized. Hashtable is a legacy synchronized collection that doesn't allow null keys or values. If thread-safe map access is needed, ConcurrentHashMap is usually a better choice.
🔍 Let's understand it
HashMap Hashtable
Synchronized ❌ ✅
null key One ❌
null values Yes ❌
Modern usage ✅ Rare
🎤 Interviewer follow-up
"What should you use instead of Hashtable for concurrent applications?"
"Usually ConcurrentHashMap, because it is designed specifically for concurrent access and generally provides better scalability than synchronizing an entire map."
💡 How to make your answer stronger
Don't just say "Hashtable is thread-safe, so use it when you need thread safety." That's outdated advice.
⚠️ Common mistake
- Saying HashMap cannot be used in multithreaded applications. It can be used with appropriate external synchronization, but ConcurrentHashMap is usually the better choice for concurrent access.
🧠 Remember
- HashMap → modern + allows nulls + not synchronized
- Hashtable → legacy + synchronized + no nulls
What is the difference between ArrayList and LinkedList?
- Difficulty
- 🟡 Intermediate
- Topic
- Collections / Data Structures
- What the interviewer is testing
- Whether you understand the performance and structural differences between the two List implementations.
🎯 What a strong candidate should say
ArrayList is backed by a dynamic array, while LinkedList is based on linked nodes. ArrayList provides fast random access with O(1) indexing, while LinkedList can be efficient for insertions or removals at known positions, but accessing elements by index is O(n). In most everyday use cases, ArrayList is the better default choice.
🔍 Let's understand it
ArrayList:
[ A ][ B ][ C ][ D ] ↑ fast index access
LinkedList:
[A] ↔ [B] ↔ [C] ↔ [D]
ArrayList is generally more cache-friendly and often performs better in practice, even for some operations where LinkedList has a better theoretical complexity.
🎤 Interviewer follow-up
"When would you choose LinkedList?"
"When I specifically need linked-list operations and frequently insert or remove elements through an iterator or at the ends. Otherwise, I'd generally choose ArrayList."
💡 How to make your answer stronger
Don't automatically say "LinkedList is faster for insertion." Insertion is only O(1) once you already have the node/position. Finding an arbitrary index can take O(n).
⚠️ Common mistake
- Saying LinkedList is always faster for adding and removing elements. The actual performance depends on where the operation occurs and whether you already have the relevant position or iterator.
🧠 Remember
- ArrayList → fast index access
- LinkedList → efficient node insertion/removal when position is known
- Default choice → usually ArrayList
What is the difference between ArrayList and HashSet?
- Difficulty
- 🟢 Beginner
- Topic
- Collections
- What the interviewer is testing
- Whether you understand the difference between ordered lists and sets of unique elements.
🎯 What a strong candidate should say
ArrayList is an ordered collection that allows duplicate elements and provides index-based access. HashSet stores unique elements and does not guarantee iteration order. ArrayList is useful when order and indexing matter, while HashSet is useful when uniqueness and fast membership checks matter.
🔍 Let's understand it
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Java"); // allowedSet<String> set = new HashSet<>();
set.add("Java");
set.add("Java"); // ignoredConceptually:
ArrayList → [Java, Java, Spring] HashSet → [Java, Spring]
HashSet is backed by a hash table and typically provides O(1) average-time add(), remove(), and contains() operations.
🎤 Interviewer follow-up
"Does HashSet preserve insertion order?"
"No. If insertion order matters, use LinkedHashSet. If you need sorted elements, use TreeSet."
💡 How to make your answer stronger
Focus on the use case, not just implementation:
- Need duplicates + index → ArrayList
- Need uniqueness + fast lookup → HashSet
⚠️ Common mistake
- Saying HashSet is always unordered. More precisely, HashSet does not guarantee iteration order. Its current order can appear consistent, but you shouldn't rely on it.
🧠 Remember
- ArrayList → ordered + duplicates + index
- HashSet → unique + no guaranteed order + fast lookup
How does HashSet ensure uniqueness?
- Difficulty
- 🟡 Intermediate
- Topic
- Collections / Hashing
- What the interviewer is testing
- Whether you understand how HashSet uses hashCode() and equals() to prevent duplicates.
🎯 What a strong candidate should say
HashSet uses hashCode() to identify a bucket and equals() to determine whether an equivalent element already exists. If an existing element is equal to the new element, the new element is not added.
🔍 Let's understand it
Set<String> names = new HashSet<>();
names.add("John");
names.add("John");
System.out.println(names.size()); // 1
Conceptually:
hashCode() → bucket ↓ equals() ↓ duplicate? → don't add
🎤 Interviewer follow-up
"What happens if you override equals() but not hashCode()?"
"The HashSet may fail to recognize logically equal objects as duplicates because they can end up in different buckets."
💡 How to make your answer stronger
Mention that the equals()/hashCode() contract is essential for hash-based collections.
⚠️ Common mistake
- Saying HashSet uses only equals() to detect duplicates. It uses both hashCode() and equals().
🧠 Remember
- hashCode() → find the bucket
- equals() → check equality
- Equal elements → only one stored
What is the difference between HashMap and TreeMap?
- Difficulty
- 🟡 Intermediate
- Topic
- Collections / Maps
- What the interviewer is testing
- Whether you understand the difference between hash-based and sorted maps.
🎯 What a strong candidate should say
HashMap stores entries based on hashing and provides O(1) average-time lookup, while TreeMap stores keys in sorted order and provides O(log n) operations. Use HashMap when you need fast lookup without ordering, and TreeMap when you need sorted keys or range-based operations.
🔍 Let's understand it
Map<Integer, String> map = new TreeMap<>();
map.put(3, "C");
map.put(1, "A");
map.put(2, "B");
// Iteration: 1, 2, 3
TreeMap maintains keys in their natural ordering or according to a supplied Comparator.
🎤 Interviewer follow-up
"How does TreeMap keep its keys sorted?"
"It is based on a self-balancing tree, specifically a Red-Black tree, which keeps operations at O(log n)."
💡 How to make your answer stronger
Mention that TreeMap is useful when you need operations such as firstKey(), lastKey(), or range queries, not simply because you want a sorted result.
⚠️ Common mistake
- Saying TreeMap sorts entries based on their values. It sorts based on the keys.
🧠 Remember
- HashMap → fast lookup, no ordering guarantee
- TreeMap → sorted keys, O(log n)
What is the difference between Comparable and Comparator?
- Difficulty
- 🟡 Intermediate
- Topic
- Collections / Sorting
- What the interviewer is testing
- Whether you understand Java's two approaches to defining object ordering.
🎯 What a strong candidate should say
Comparable defines an object's natural ordering inside the class itself, using compareTo(). Comparator defines an external ordering using compare(), allowing multiple different ways to sort the same type.
🔍 Let's understand it
Using Comparable:
class User implements Comparable<User> {
public int compareTo(User other) {
return this.age - other.age;
}
}
Using Comparator:
Comparator<User> byName =
Comparator.comparing(User::getName);
The same User can have multiple comparators without changing the class.
🎤 Interviewer follow-up
"When would you prefer Comparator?"
"When I need multiple sorting strategies or when I can't modify the class being sorted."
💡 How to make your answer stronger
Avoid subtraction such as this.age - other.age in real code because it can overflow. Prefer:
Integer.compare(this.age, other.age);⚠️ Common mistake
- Saying Comparable can define multiple orderings. A class generally has one natural ordering, while you can create multiple Comparator implementations.
🧠 Remember
- Comparable → compareTo() → natural ordering
- Comparator → compare() → custom/external ordering
What are fail-fast and fail-safe iterators?
- Difficulty
- 🟡 Intermediate
- Topic
- Collections / Iterators
- What the interviewer is testing
- Whether you understand what happens when a collection is structurally modified during iteration.
🎯 What a strong candidate should say
A fail-fast iterator detects certain structural modifications to a collection during iteration and typically throws ConcurrentModificationException. A "fail-safe" iterator is a commonly used informal term for iterators that don't fail this way, often because they iterate over a snapshot or use concurrent collection semantics.
🔍 Let's understand it
With a regular collection:
List<String> names = new ArrayList<>();
names.add("John");
names.add("Mary");
for (String name : names) {
names.remove(name); // may throw ConcurrentModificationException
}
With CopyOnWriteArrayList, iteration works over a snapshot:
CopyOnWriteArrayList<String> names =
new CopyOnWriteArrayList<>();
for (String name : names) {
names.remove(name);
}
🎤 Interviewer follow-up
"Does fail-fast guarantee that ConcurrentModificationException will always be thrown?"
"No. Fail-fast behavior is best-effort and should not be relied upon for program correctness."
💡 How to make your answer stronger
Instead of treating "fail-safe" as an official Java term, explain what mechanism the collection actually uses—for example, snapshot iteration with CopyOnWriteArrayList.
⚠️ Common mistake
- Saying fail-safe iterators guarantee thread safety. The behavior depends on the specific collection and how it is used.
🧠 Remember
- Fail-fast → detects modification → typically ConcurrentModificationException
- "Fail-safe" → informal term, often snapshot/concurrent iteration
What is the difference between checked and unchecked exceptions?
- Difficulty
- 🟡 Intermediate
- Topic
- Exception Handling
- What the interviewer is testing
- Whether you understand Java's compile-time exception checking and when exceptions must be handled or declared.
🎯 What a strong candidate should say
Checked exceptions are checked by the compiler and must either be handled or declared with throws. Unchecked exceptions are subclasses of RuntimeException and don't have this requirement. Checked exceptions are generally used for conditions a caller may reasonably be expected to recover from, while unchecked exceptions often indicate programming errors or invalid state.
🔍 Let's understand it
Checked:
void readFile() throws IOException {
// ...
}
The caller must handle or declare the exception.
Unchecked:
int result = 10 / 0; // ArithmeticException
No try/catch or throws is required.
The hierarchy is:
Throwable
├── Error
└── Exception
├── RuntimeException ← unchecked
└── other exceptions ← checked
🎤 Interviewer follow-up
"Is every exception a checked exception?"
"No. RuntimeException and its subclasses are unchecked. Error types are also unchecked, but they generally represent serious JVM or system-level problems that applications shouldn't normally handle."
💡 How to make your answer stronger
Don't frame the distinction simply as "checked = recoverable, unchecked = programmer error". That's a useful rule of thumb, but not an absolute rule. The key technical distinction is whether the compiler requires handling or declaration.
⚠️ Common mistake
- Saying Error is a checked exception. Error is unchecked.
🧠 Remember
- Checked → compiler requires handle/declare
- Unchecked → RuntimeException → no requirement
What is the difference between throw and throws?
- Difficulty
- 🟢 Beginner
- Topic
- Exception Handling
- What the interviewer is testing
- Whether you understand the difference between explicitly throwing an exception and declaring that a method may throw one.
🎯 What a strong candidate should say
throw is used to explicitly throw an exception, while throws is used in a method signature to declare that the method may throw one or more exceptions.
🔍 Let's understand it
throw new IllegalArgumentException("Invalid age");
throw actually throws the exception.
void readFile() throws IOException {
// ...
}
throws declares that the method may throw an exception.
🎤 Interviewer follow-up
"Can a method declare multiple exceptions with throws?"
"Yes."
void process() throws IOException, SQLException {
}
💡 How to make your answer stronger
A simple distinction:
- throw → inside the method → actually throws an exception
- throws → method signature → declares possible exceptions
⚠️ Common mistake
- Saying
throwsactually throws the exception. It only declares it.
🧠 Remember
- throw = action
- throws = declaration
How does try-with-resources work?
- Difficulty
- 🟡 Intermediate
- Topic
- Exception Handling / Resource Management
- What the interviewer is testing
- Whether you understand automatic resource management in Java.
🎯 What a strong candidate should say
Try-with-resources automatically closes resources that implement AutoCloseable when the try block finishes, even if an exception occurs. It eliminates the need for explicit cleanup in a finally block.
🔍 Let's understand it
try (BufferedReader reader =
new BufferedReader(new FileReader("file.txt"))) {
System.out.println(reader.readLine());
} catch (IOException e) {
// handle exception
}
When the try block finishes, reader.close() is automatically called.
Multiple resources can also be declared:
try (InputStream in = ...;
OutputStream out = ...) {
// ...
}
🎤 Interviewer follow-up
"What happens if both the try block and close() throw exceptions?"
"The exception from the try block is the primary exception, while the exception from close() is added as a suppressed exception."
💡 How to make your answer stronger
Mention AutoCloseable. That's the key interface behind try-with-resources.
⚠️ Common mistake
- Saying it only works with files. It works with any resource that implements AutoCloseable, such as streams, database connections, and many other resources.
🧠 Remember
try (...) → automatic cleanup → AutoCloseable
What is the difference between Stream and Collection?
- Difficulty
- 🟡 Intermediate
- Topic
- Streams / Collections
- What the interviewer is testing
- Whether you understand the difference between storing data and processing data.
🎯 What a strong candidate should say
A Collection stores and manages data, while a Stream provides a way to process data through operations such as filtering, mapping, and reducing. A collection represents the data; a stream represents a computation over that data.
🔍 Let's understand it
List<String> names = List.of("John", "Mary", "Alex");
List<String> result = names.stream()
.filter(name -> name.startsWith("A"))
.toList();
The List stores the names.
The Stream processes them. A stream also doesn't normally modify the underlying collection.
🎤 Interviewer follow-up
"Can you reuse a Stream?"
"No. A stream is single-use. Once a terminal operation has been performed, the stream is considered consumed."
💡 How to make your answer stronger
Mention that streams support lazy evaluation: intermediate operations generally aren't executed until a terminal operation is called.
⚠️ Common mistake
- Saying a Stream is another type of Collection. A stream doesn't store elements itself.
🧠 Remember
- Collection → stores data
- Stream → processes data
What is the difference between map() and flatMap()?
- Difficulty
- 🟡 Intermediate
- Topic
- Streams
- What the interviewer is testing
- Whether you understand transformation versus flattening in the Stream API.
🎯 What a strong candidate should say
map() transforms each element into another element, while flatMap() transforms each element into a stream and then flattens those streams into a single stream.
🔍 Let's understand it
With map():
List<String> names = List.of("John", "Mary");
List<Integer> lengths = names.stream()
.map(String::length)
.toList();
Result:
[4, 4]
With flatMap():
List<List<String>> lists = List.of(
List.of("A", "B"),
List.of("C", "D")
);
List<String> result = lists.stream()
.flatMap(List::stream)
.toList();
Result:
[A, B, C, D]
🎤 Interviewer follow-up
"What would map() produce in the second example?"
"A Stream<List<String>>, because each list remains a single element."
💡 How to make your answer stronger
Think:
- map() → one element becomes one element
- flatMap() → one element becomes multiple elements, then they're flattened
⚠️ Common mistake
- Saying
flatMap()is simply a faster version ofmap(). It solves a different problem: flattening nested structures.
🧠 Remember
- map() → transform
- flatMap() → transform + flatten
What is the difference between intermediate and terminal stream operations?
- Difficulty
- 🟡 Intermediate
- Topic
- Streams
- What the interviewer is testing
- Whether you understand stream pipelines and lazy evaluation.
🎯 What a strong candidate should say
Intermediate operations transform or filter a stream and return another Stream, while terminal operations produce a final result or side effect and trigger the execution of the pipeline. Intermediate operations are lazy; terminal operations are what cause them to execute.
🔍 Let's understand it
Intermediate operations:
filter()map()sorted()distinct()
Terminal operations:
collect()toList()forEach()count()reduce()findFirst()
Example:
long count = names.stream()
.filter(name -> name.length() > 4) // intermediate
.count(); // terminal
The filter() isn't actually executed until count() triggers the pipeline.
🎤 Interviewer follow-up
"Why are intermediate operations lazy?"
"Lazy evaluation allows the Stream API to optimize processing, avoid unnecessary work, and potentially combine multiple operations into a more efficient pipeline."
💡 How to make your answer stronger
Mention that intermediate operations can be stateless (map, filter) or stateful (sorted, distinct), which can affect how the stream is processed.
⚠️ Common mistake
- Saying
map()immediately processes all elements. Intermediate operations are lazy and don't execute until a terminal operation is invoked.
🧠 Remember
- Intermediate → returns Stream + lazy
- Terminal → produces result + triggers execution
What is a functional interface?
- Difficulty
- 🟢 Beginner
- Topic
- Functional Programming
- What the interviewer is testing
- Whether you understand the foundation of lambdas and functional programming in Java.
🎯 What a strong candidate should say
A functional interface is an interface that has exactly one abstract method. It can be used as the target type for a lambda expression or method reference.
🔍 Let's understand it
@FunctionalInterface
interface Calculator {
int calculate(int a, int b);
}It can be used with a lambda:
Calculator add = (a, b) -> a + b;Common built-in functional interfaces include Predicate, Function, Consumer, and Supplier.
🎤 Interviewer follow-up
"Can a functional interface have default or static methods?"
"Yes. It can have multiple default or static methods, but only one abstract method."
💡 How to make your answer stronger
Mention @FunctionalInterface. It isn't required, but the compiler verifies that the interface satisfies the functional-interface contract.
⚠️ Common mistake
- Saying a functional interface can have only one method. It can have one abstract method, plus default, static, and private methods.
🧠 Remember
Functional interface = exactly one abstract method → lambda-compatible
What are lambda expressions?
- Difficulty
- 🟢 Beginner
- Topic
- Functional Programming
- What the interviewer is testing
- Whether you understand how Java represents behavior as a value.
🎯 What a strong candidate should say
A lambda expression is a concise way to represent an implementation of a functional interface. It allows behavior to be passed as an argument without creating an anonymous class.
🔍 Let's understand it
Without a lambda:
Runnable task = new Runnable() {
public void run() {
System.out.println("Hello");
}
};With a lambda:
Runnable task = () -> System.out.println("Hello");Lambdas are commonly used with the Stream API:
names.stream()
.filter(name -> name.startsWith("A"));🎤 Interviewer follow-up
"Can you use a lambda with any interface?"
"No. The target interface must be a functional interface with exactly one abstract method."
💡 How to make your answer stronger
Mention that lambdas enable functional-style programming and are heavily used with Streams and the Java Collections API.
⚠️ Common mistake
- Saying a lambda is an object itself. A lambda is an expression that provides an implementation of a functional interface.
🧠 Remember
Lambda → concise implementation of a functional interface
What is Optional and why is it used?
- Difficulty
- 🟡 Intermediate
- Topic
- Core Java / Functional Programming
- What the interviewer is testing
- Whether you understand how Optional can represent the presence or absence of a value.
🎯 What a strong candidate should say
Optional is a container that may or may not contain a value. It makes the possibility of a missing result explicit and provides APIs for handling that case without directly relying on null.
🔍 Let's understand it
Optional<User> user = findUser(id);
user.ifPresent(u -> System.out.println(u.getName()));Or provide a fallback:
User user = findUser(id)
.orElse(defaultUser);🎤 Interviewer follow-up
"Should Optional be used everywhere instead of null?"
"No. It's mainly useful for return types where a result may be absent. Using it for fields, parameters, or every nullable value can make code unnecessarily complex."
💡 How to make your answer stronger
Mention that Optional helps make absence explicit in an API, but it doesn't eliminate NullPointerException or guarantee null-safe code.
⚠️ Common mistake
- Calling
get()without checking whether a value exists. Prefer methods such as orElse(), orElseGet(), orElseThrow(), or ifPresent() when appropriate.
🧠 Remember
- Optional → value may be present or absent
- Useful mainly for expressing optional return values
What is multithreading?
- Difficulty
- 🟢 Beginner
- Topic
- Concurrency
- What the interviewer is testing
- Whether you understand how Java can execute multiple tasks concurrently.
🎯 What a strong candidate should say
Multithreading is the execution of multiple threads within a process, allowing tasks to run concurrently. In Java, threads share the process's memory while each thread has its own execution stack.
🔍 Let's understand it
Thread thread = new Thread(() ->
System.out.println("Running")
);
thread.start();Java applications can have multiple threads performing different tasks concurrently, such as handling requests, processing background work, or performing I/O.
🎤 Interviewer follow-up
"What's the difference between concurrency and parallelism?"
"Concurrency means multiple tasks can make progress during the same period. Parallelism means multiple tasks are actually executing at the same time, typically on different CPU cores."
💡 How to make your answer stronger
Mention that shared mutable state introduces challenges such as race conditions, visibility issues, and synchronization requirements.
⚠️ Common mistake
- Assuming multiple threads always execute simultaneously. Concurrency doesn't necessarily mean parallel execution.
🧠 Remember
Multithreading → multiple threads executing concurrently
What is the difference between synchronized and volatile?
- Difficulty
- 🟡 Intermediate
- Topic
- Concurrency / Thread Safety
- What the interviewer is testing
- Whether you understand synchronization, visibility, and atomicity in multithreaded Java programs.
🎯 What a strong candidate should say
volatile guarantees visibility of changes to a variable across threads, while synchronized provides mutual exclusion and visibility. synchronized can protect a critical section from concurrent access, whereas volatile does not provide mutual exclusion or make compound operations atomic.
🔍 Let's understand it
With volatile:
private volatile boolean running = true;If one thread changes running, other threads can reliably observe the updated value.
With synchronized:
synchronized void increment() {
count++;
}Only one thread at a time can execute the synchronized method for the same monitor.
🎤 Interviewer follow-up
"Is volatile int count enough for count++?"
"No. count++ is a read-modify-write operation, so it isn't atomic just because the variable is volatile. You'd need synchronization or an atomic type such as AtomicInteger."
💡 How to make your answer stronger
The simplest distinction is:
- volatile → visibility
- synchronized → mutual exclusion + visibility
⚠️ Common mistake
- Saying volatile makes operations thread-safe. It only provides specific visibility and ordering guarantees; it does not make compound operations atomic.
🧠 Remember
- volatile → visibility
- synchronized → locking + visibility + happens-before
What is the difference between wait(), notify(), and notifyAll()?
- Difficulty
- 🟡 Intermediate
- Topic
- Concurrency / Thread Communication
- What the interviewer is testing
- Whether you understand Java's low-level thread coordination mechanism.
🎯 What a strong candidate should say
wait() makes a thread release the monitor and wait, while notify() wakes one waiting thread and notifyAll() wakes all waiting threads. These methods must be called while holding the object's monitor, typically inside a synchronized block or method.
🔍 Let's understand it
synchronized (lock) {
while (!ready) {
lock.wait();
}
}Another thread can signal it:
synchronized (lock) {
ready = true;
lock.notifyAll();
}A thread calling wait() releases the lock while waiting and reacquires it before continuing.
🎤 Interviewer follow-up
"Why should wait() usually be called inside a while loop?"
"Because the condition should always be rechecked after waking up. A thread can wake without the condition being satisfied, and another thread may have changed the state."
💡 How to make your answer stronger
Mention that modern Java code often prefers higher-level concurrency utilities such as BlockingQueue, CountDownLatch, or CompletableFuture instead of manually coordinating threads with wait() and notify().
⚠️ Common mistake
- Saying
notify()releases the lock immediately. It wakes a waiting thread, but the notifying thread keeps the monitor until it exits the synchronized section.
🧠 Remember
- wait() → release monitor + wait
- notify() → wake one
- notifyAll() → wake all
What is ExecutorService?
- Difficulty
- 🟡 Intermediate
- Topic
- Concurrency / Thread Pools
- What the interviewer is testing
- Whether you understand how Java manages and executes asynchronous tasks without manually creating threads.
🎯 What a strong candidate should say
ExecutorService is a framework for managing a pool of threads and executing submitted tasks. It separates task submission from thread management, making concurrent code easier to control and reuse.
🔍 Let's understand it
ExecutorService executor =
Executors.newFixedThreadPool(4);
executor.submit(() -> {
System.out.println("Running task");
});
executor.shutdown();Instead of creating a new thread for every task, the executor can reuse a pool of worker threads.
It can also return results:
Future<Integer> result =
executor.submit(() -> 42);🎤 Interviewer follow-up
"Why should you call shutdown()?"
"It tells the executor to stop accepting new tasks and eventually terminate its worker threads after submitted tasks complete."
💡 How to make your answer stronger
Mention that ExecutorService helps control thread creation, task scheduling, and resource usage.
⚠️ Common mistake
- Creating an ExecutorService and never shutting it down. That can leave its threads running and prevent an application from terminating cleanly.
🧠 Remember
ExecutorService → submit tasks → thread pool executes them
What is a deadlock?
- Difficulty
- 🟡 Intermediate
- Topic
- Concurrency
- What the interviewer is testing
- Whether you understand how threads can become permanently blocked waiting for each other.
🎯 What a strong candidate should say
A deadlock occurs when two or more threads are permanently blocked because each is waiting for a resource or lock held by another thread.
🔍 Let's understand it
Thread A holds Lock 1
↓
waits for Lock 2
Thread B holds Lock 2
↓
waits for Lock 1
Neither thread can continue.A typical example:
synchronized (lockA) {
synchronized (lockB) {
// ...
}
}
// In another thread
synchronized (lockB) {
synchronized (lockA) {
// ...
}
}🎤 Interviewer follow-up
"How can you prevent deadlocks?"
"Use a consistent lock ordering, avoid unnecessary nested locks, minimize lock scope, or use higher-level concurrency utilities."
💡 How to make your answer stronger
Know the four classic conditions for deadlock: Mutual exclusion + hold and wait + no preemption + circular wait. Breaking any one of them can prevent deadlock.
⚠️ Common mistake
- Confusing deadlock with starvation. In a deadlock, threads are waiting on each other. In starvation, a thread may wait indefinitely because other threads continually get the resources or execution opportunities.
🧠 Remember
Deadlock → Thread A waits for B, B waits for A → nobody progresses
What is the difference between a process and a thread?
- Difficulty
- 🟢 Beginner
- Topic
- Operating Systems / Concurrency
- What the interviewer is testing
- Whether you understand the basic unit of execution and resource sharing.
🎯 What a strong candidate should say
A process is an independent running program with its own memory space, while a thread is an execution unit within a process. Threads within the same process share resources such as heap memory, making communication easier but synchronization more important.
🔍 Let's understand it
Process
├── Thread 1
├── Thread 2
└── Thread 3
Shared heapFor example, a Java application runs as a process containing multiple threads that can execute different tasks concurrently.
🎤 Interviewer follow-up
"Why are threads generally cheaper than processes?"
"Threads share the process's resources and address space, so creating and switching between them generally requires less overhead than creating and switching between separate processes."
💡 How to make your answer stronger
Focus on the key distinction: Process → isolated memory space; Thread → shares memory within a process.
⚠️ Common mistake
- Saying threads have completely shared memory. Threads share the process's heap and other resources, but each thread has its own stack and execution state.
🧠 Remember
- Process → independent program + isolated memory
- Thread → execution unit + shared process resources
What are the most important Java features introduced in recent versions?
- Difficulty
- 🟡 Intermediate
- Topic
- Java Evolution
- What the interviewer is testing
- Whether you keep up with modern Java rather than only knowing older language features.
🎯 What a strong candidate should say
Some of the most important modern Java features include records, sealed classes, pattern matching, text blocks, switch expressions, virtual threads, and sequenced collections. Modern Java also includes improvements to the JVM, garbage collectors, and concurrency APIs.
🔍 Let's understand it
Some key milestones:
Java Important features 8 Lambdas, Streams, Optional, default methods 9 Modules, private interface methods 10–11 var, HTTP Client, improvements to APIs 14–17 Switch expressions, text blocks, records, sealed classes, pattern matching 21 Virtual threads, pattern matching for switch, record patterns, sequenced collections 25 Further language, JVM, and API improvements
Java 21 and Java 25 are particularly important to know because they are LTS (Long-Term Support) releases.
🎤 Interviewer follow-up
"What are virtual threads?"
"Virtual threads are lightweight threads managed by the JVM rather than directly corresponding to OS threads. They make it practical to run very large numbers of concurrent, mostly I/O-bound tasks."
💡 How to make your answer stronger
Don't try to list every Java feature. Pick 3–5 relevant features and explain how they improve real-world development. For a modern backend interview, records, sealed classes, pattern matching, and virtual threads are particularly worth knowing.
⚠️ Common mistake
- Listing features without knowing which Java version introduced them. Interviewers may immediately follow up with: "Which Java version introduced that?" Know the major LTS versions: 8, 11, 17, 21, and 25.
🧠 Remember
Modern Java → Records + Sealed Classes + Pattern Matching + Virtual Threads + better APIs/JVM