Are you struggling to find the right Java interview questions that are actually asked in top MNCs? If yes, you’re at the right place.
After attending 100+ interviews across leading companies like TCS, Wipro, Cognizant, Accenture, IBM, Amazon, and KPMG, I’ve handpicked and curated a list of 200+ topic-wise Java interview questions that are most frequently asked for experienced roles. Whether you’re a 2 years, 3 years, 5 years or even more experienced Java developer, this guide will help you prepare efficiently and confidently.
The questions cover Core Java, Java 8 features, Spring Boot, Microservices, Hibernate, Kafka, and even topics from the latest Java versions – everything you need to crack your next interview.
👉 This list is regularly updated every week to include the latest trends and questions from real-time interviews, ensuring you stay ahead in your preparation.
🔥 Don’t just read—start preparing now! Bookmark this page and revisit it weekly as we keep adding new real-time questions from top MNC interviews. Whether you’re aiming for your first MNC job or planning a switch, these questions can be your shortcut to success.
✅ Ready to level up your Java interview prep?
Scroll down and start mastering the most asked questions now!
Java interview Questions for Experienced MNCs
📚 Java Basics & 🧠 Miscellaneous Topics
1. What is the purpose of the static keyword in Java?
The static
keyword in Java means the member (variable, method, or block) belongs to the class, not to any object. So, it can be accessed without creating an object.
Example:
class Demo {
static int count = 0;
static void showCount() {
System.out.println("Count is: " + count);
}
}
public class Main {
public static void main(String[] args) {
Demo.count = 5; // Accessing static variable without object
Demo.showCount(); // Calling static method without object
}
}
Output:
Count is: 5
✅count
and showCount()
belong to the Demo
class itself, not to an object.
2. What is the purpose of the static block in Java?
A static block
is used to initialize static variables or run setup code. It executes only once, when the class is loaded into memory (before any object is created or any static method is called).
Example:
class Main {
static int count;
static {
count = 10;
System.out.println("Static block executed");
}
public static void main(String[] args) {
System.out.println("Count: " + count);
}
};
Output:
Static block executed
Count: 10
✅ The static block runs first, initializing count
.
3. What are the different access modifiers in Java? Explain their significance.
Access modifiers in Java control the accessibility of classes, methods, and variables, means who can access a class, method, or variable.
- Public: Accessible from any other class.
- Protected: Accessible within the same package or by subclasses (even in other packages).
- Default (no modifier): Accessible only within the same package.
- Private: Accessible only within the class where it is declared.
Example:
public class Car {
private int year; // Only accessible inside Car
protected String model; // Accessible in same package or subclass
String brand; // Default: accessible in same package
public void drive() { // Accessible from anywhere
System.out.println("Driving...");
}
}
✅ Use access modifiers to protect data and control visibility.
4. How does Java handle memory management and garbage collection?
Java manages memory using automatic garbage collection, which frees up memory by removing objects that are no longer reachable or used, helping prevent memory leaks.
Types of garbage collectors include:
- Serial Collector: Works in a single thread, Used in small applications with limited resources.
- Parallel Collector: Uses multiple threads, good for medium to large applications that need faster performance.
- G1 Collector: Used in large applications with a large amount of memory and processors, splits memory into regions for efficient cleanup.
✅ Developers don’t manually free memory—Java does it in the background.
5. What is the difference between String, StringBuilder, and StringBuffer?
🔹 String – Immutable
- New object is created on modification.
- Once created, can’t be changed.
Example
String s = "Hello";
String modified = s.concat(" Java");
System.out.println(s); // Output: Hello
System.out.println(modified); // Output: Hello Java
- When
s.concat(" Java")
is called, it does not change the original strings
. Instead, it returns a newString
object which is pointed to bymodified
. - The original string
s
remains unchanged as “Hello”, demonstrating its immutabilit.
✅ Good for constants and read-only data.
🔹 StringBuilder – Mutable & Fast (Not Thread-Safe)
- Can be modified without creating new objects.
- Ideal for single-threaded scenarios.
- Good choice for strings that require frequent modification in single-threaded contexts.
Example
StringBuilder sb = new StringBuilder("Hello");
sb.append(" Java");
System.out.println(sb); // Output: Hello Java
Here, StringBuilder
allows appending strings without creating new objects, making it more memory efficient for frequently modified strings in a non-concurrent scenario.
✅ Use when performance matters and no threading involved.
🔹 StringBuffer – Mutable & Thread-Safe
- Similar to StringBuilder but synchronized.
- Slower, but safe in multi-threaded environments.
- Suitable for strings modified by multiple threads.
Example
StringBuffer sbf = new StringBuffer("Hello");
sbf.append(" Java");
System.out.println(sbf); // Output: Hello Java
In this example, StringBuffer
is used like StringBuilder
, but it is synchronized to avoid issues in a multi-threaded environment. This synchronization, however, comes at the cost of performance compared to StringBuilder
.
✅ Use in multi-threaded applications.
6. Explain the concept of reflection in Java.
Reflection allows Java programs to inspect and manipulate classes, methods, fields, and constructors at runtime, even if their names are unknown at compile time. It is part of the java.lang.reflect
package.
Using reflection, you can:
- Find out which class an object belongs to.
- Access and run methods, even if they’re private.
- Get details like class name, constructors, and methods.
Example
Class<?> clazz = Class.forName("java.util.ArrayList");
System.out.println("Class Name: " + clazz.getName());
✅ This loads the ArrayList
class and prints its name.
Reflection is powerful but should be used carefully due to performance and security risks.
7. Explain the concept of a design pattern. Give examples of commonly used design patterns in Java.
Design patterns are ready-made solutions to common problems in software development. They are not actual code, but templates or blueprints that guide developers in building better software.
Why Use Design Patterns?
- Reusable – Solve similar problems in different projects.
- Standardized – Provide a common language for developers.
- Efficient – Save time by using tried-and-tested solutions.
- Flexible – Can be adapted to different needs.
Design Pattern Examples :
- Singleton Pattern: Ensures a class has only one instance.
- Observer Pattern: Allows a class to observe and react to changes in another class.
- Factory Pattern: Creates objects without exposing the creation logic.
8. Explain the concept of inner classes in Java. What are the different types of inner classes?
Inner classes, also called nested classes, are classes defined within another class. They help group related logic, improve encapsulation, and enhance code readability.
🔹 Benefits of Inner Classes
- To keep code clean and organized
- To logically associate classes that are only used together
- To access outer class members easily
🔹 Types of inner class:
- Member Inner Class: Non-static and can access the member variables of the outer class.
- Static Inner Class: Static and cannot access instance variables of the outer class directly.
- Local Inner Class: Defined within a block, typically inside a method.
- Anonymous Inner Class: Local inner class without a name, often used for implementing interface methods on the fly.
9. Explain the concept of Generics in Java. How does it provide type safety?
Generics allow you to write code that works with any data type while maintaining type safety at compile time. They make your code more reusable, reliable, and easy to maintain.
🔹 Why use Generics?
- Improves code readability and reuse
- Avoids ClassCastException
- Ensures type consistency
Example:
class Box<T> { // T is a placeholder type
T value;
void set(T value) { this.value = value; }
T get() { return value; }
}
public class Main {
public static void main(String[] args) {
Box<String> strBox = new Box<>();
strBox.set("Hello");
System.out.println(strBox.get()); // Output: Hello
Box<Integer> intBox = new Box<>();
intBox.set(100);
System.out.println(intBox.get()); // Output: 100
}
}
10. How does Java handle memory leaks? Explain strong and weak references.
Java uses Garbage Collection (GC) to automatically free memory taken by objects no longer in use.
However, if your code still holds references to unused objects (especially strong ones), the GC can’t remove them and this causes a memory leak.
🔑 What is a Memory Leak?
A memory leak happens when:
- Objects are no longer needed but
- Still have references, so GC won’t remove them
- Over time, this fills up memory and slows down or crashes the app
💪 Strong Reference (Default Type)
- This is the normal reference you create.
- As long as a strong reference exists, the object is not garbage collected.
String name = new String("John"); // Strong reference
🔸 Even if the object isn’t used anymore, it stays in memory unless name = null
is set.
🧹 Weak Reference
- A weak reference doesn’t prevent GC from cleaning up the object.
- Used when you want the object to be cleared if it’s not used elsewhere.
import java.lang.ref.WeakReference;
public class Main {
public static void main(String[] args) {
String strong = new String("Hello");
WeakReference<String> weak = new WeakReference<>(strong);
strong = null; // Remove strong reference
System.gc(); // Suggest GC to run
System.out.println("Weak Ref: " + weak.get()); // May return null if GC ran
}
}
✅ After strong = null
, only a weak reference is left. If GC runs, the "Hello"
object can be collected, and weak.get()
returns null
.
11. Explain the concept of annotations in Java. How can you create and use custom annotations?
Annotations are special tags (starting with @
) that add metadata to your code. They tell the compiler or runtime something about the code, like instructions or info.
🛠️ Custom Annotations: You can create your own annotation using @interface
🔹 Example: Creating and Using a Custom Annotation
// Step 1: Create Custom Annotation
@interface TestInfo {
String createdBy() default "User";
String description();
}
// Step 2: Apply Annotation
@TestInfo(description = "This is a test class", createdBy = "Prakash")
public class Demo {
public static void main(String[] args) {
System.out.println("Running Demo...");
}
}
12. Explain the concept of immutable objects in Java. Why are they important?
Immutable objects are objects whose state (data) cannot be changed after they are created.
Once you set their values in the constructor, you can only read them not modify. Some of default immutable class in Java are String, Integer, LocalDate etc
🔒 Why immutable objects are Important?
- Thread-safe – No data is changed, so multiple threads can use them safely.
- Predictable – They always behave the same, no side effects
- Safe for caching and sharing
- Used in functional programming and concurrent applications
Immutable Class Example
final class Person {
private final String name;
private final int age;
// Constructor to set values once
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Only getter methods, no setters
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
How it works:
- You cannot change the
name
orage
once the object is created. - There are no setter methods.
- Fields are
private
andfinal
. - The class is
final
(cannot be extended).
13. What are the principles of SOLID design in Java? Explain each principle.
SOLID principles help develop software that is easier to maintain and extend:
- Single Responsibility Principle: Each class should have one reason to change.
- Open/Closed Principle: Classes should be open for extension but closed for modification.
- Liskov Substitution Principle: Objects of a superclass should be replaceable with objects of subclasses without affecting the application.
- Interface Segregation Principle: Clients should not be forced to depend on interfaces they do not use.
- Dependency Inversion Principle: High-level modules should not depend on low-level modules but on abstractions.
14. What is the difference between System.out.println() and System.err.println()?
System.out.println() is used for general output, while System.err.println() is used for outputting error messages. Although both output to the console, System.err is typically used to log error messages and can be redirected separately from System.out.
15. What are the different types of class loaders in Java? Explain their roles.
A ClassLoader in Java is part of the Java Runtime Environment (JRE) that dynamically loads classes into memory at runtime. It reads the .class
files (bytecode) and loads them into the Java Virtual Machine (JVM).
Types of class loaders:
- Bootstrap Class Loader: Loads core Java API classes.
- Extension Class Loader: Loads classes from the extensions directories.
- System Class Loader: Loads classes from the system classpath.
Class Loader Hierarchy
Bootstrap ClassLoader
↓
Extension ClassLoader
↓
System (Application) ClassLoader
↓
Custom ClassLoader (optional)
16. Explain the concept of caching in Java. How can you implement caching mechanisms?
Caching in Java involves storing frequently accessed or expensive-to-fetch data (like DB results or API responses) in memory, so you can access it faster next time improving performance and reducing load.
Caching can be implemented using various data structures like HashMaps, LinkedHashMap or third-party libraries such as EhCache, Caffeine, Guava which provide advanced features like eviction policies, statistics, and disk overflow.
Example with Caffeine:
Cache<String, String> cache = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();
17. Explain the concept of AOP (Aspect-Oriented Programming) in Java. How can you use AOP frameworks like AspectJ?
Aspect-Oriented Programming (AOP) is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns. It does so by adding additional behavior to existing code (an advice) without modifying the code itself, instead separately specifying which code is modified via a “pointcut” specification. This means that behaviors that cut across many parts of an application, such as logging, transaction management, or security, can be modularized into distinct aspects rather than spread throughout the code.
In simple way we can also explain AOP as AOP is a way in Java to separate repeated tasks (like logging, security checks, or transaction handling) from your main business logic. It helps you avoid writing the same code in multiple places and instead, you define it once and apply it where needed automatically. That’s how AOP works — you define common functionality once and it gets automatically inserted wherever needed in your app.
🧩 Key Concepts of Aspect-Oriented Programming (In Simple Terms)
Term | Simple Meaning |
---|---|
Aspect | The common thing you want to apply (e.g., logging, security, transactions) |
Join Point | A point in your program where extra code can be added (like before/after a method runs) |
Pointcut | A rule that selects which methods or classes should get the aspect |
Advice | The actual code that runs (e.g., what should happen before or after the method) |
Weaving | The process of adding the advice into your main code (can happen during compile or runtime) |
🛠️ Example: Logging with AOP (Without changing the main logic!)
Imagine you have this class:
public class PaymentService {
public void processPayment() {
System.out.println("Processing payment...");
}
}
Instead of writing System.out.println("Logging...")
inside every method manually, you write an Aspect like this:
@Aspect
public class LoggingAspect {
@Before("execution(* PaymentService.*(..))")
public void logBefore() {
System.out.println("Logging: Method is about to execute...");
}
}
So now, every time any method in PaymentService
runs, the log will automatically appear without changing your original method!
Java Oops
1. What is the difference between method overloading and method overriding?
Method Overloading occurs when two or more methods in the same class have the same name but different parameters (different type, number, or both). It helps in increasing the readability of the program.
Method Overriding occurs when a subclass has a method with the same name and parameters as a method in its superclass. In overriding, the method in the subclass is used instead of the one in the superclass, allowing for dynamic method dispatch and polymorphism.
Example of Overloading and Overriding
// Method Overloading
class Display {
void show(int a) {
System.out.println(a);
}
void show(String a) {
System.out.println(a);
}
} //show method overloaded
// Method Overriding
class Animal {
void eat() {
System.out.println("This animal eats food.");
}
}
class Cat extends Animal {
void eat() {
System.out.println("Cat eats fish.");
}
}
//eat() method overridden
2. Explain the concept of Polymorphism in Java.
Polymorphism in Java is the ability of an object to take on many forms. It lets us perform a single action in different ways. Java supports polymorphism through method overriding (runtime polymorphism) and method overloading (compile-time polymorphism). It allows for actions to behave differently based on the actual object that is performing the action.
Example:
class Bird {
void fly() {
System.out.println("Bird flies.");
}
}
class Parrot extends Bird {
void fly() {
System.out.println("Parrot is flying.");
}
}
3. What is the difference between an abstract class and an interface? How have these concepts evolved in Java 8?
Abstract Class
An abstract class is a class that cannot be instantiated (you can’t create objects of it directly). It serves as a base class for other classes and can have both concrete (methods with a body) and abstract (methods without a body) methods. It’s used when classes have some methods in common but also have some that require unique implementations per subclass. Abstract classes in Java can have constructors. But we can’t create objects of an abstract class.
The abstract class constructor runs first and Then the child class constructor runs.
🧠 Why Use an Abstract Class?
Use it when:
- You want to define common behavior for multiple classes
- But also want to enforce subclasses to implement specific methods
- You need to share code across related classes
🧪 Example of Abstract Class:
abstract class Animal {
void eat() { // Concrete method
System.out.println("This animal eats food");
}
abstract void makeSound(); // Abstract method
}
class Dog extends Animal {
void makeSound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
void makeSound() {
System.out.println("Cat meows");
}
}
Explanation
Animal
is an abstract class.eat()
is common to all animals (so it’s concrete).makeSound()
is different for each animal (so it’s abstract).Dog
andCat
must implementmakeSound()
.
Interface
An interface is like a blueprint of a class. It defines a set of methods that any class can implement. It helps to define behavior without specifying how it should be done.
In simple words: An interface says “what needs to be done”, but not “how it will be done.”
Earlier (before Java 8), interfaces could only have:
- ✅ Abstract methods (no body)
- ✅ Constants (
public static final
variables)What Changed in Java 8?
In Java 8, interfaces were upgraded to support:
- Default methods (with body)
- Static methods (with body)
This allowed:
- Adding new methods to interfaces without breaking existing implementations
- Writing shared logic directly in the interface
🧪 Example of Interface with Default Method:
interface Animal {
void makeSound(); // Abstract method
default void sleep() {
System.out.println("Sleeping...");
}
}
class Dog implements Animal {
public void makeSound() {
System.out.println("Dog barks");
}
}
public class Main {
public static void main(String[] args) {
Dog d = new Dog();
d.makeSound(); // Dog barks
d.sleep(); // Sleeping...
}
}
🧠 Why It’s Useful:
Before Java 8: If you added a new method to an interface, all implementing classes had to update.
After Java 8: You can add default methods, and existing classes won’t break.
4. What is the purpose of the this keyword in Java?
The this keyword in Java refers to the current instance of the class. It is commonly used to:
- Differentiate between class fields and parameters with the same names.
- Pass the current object as a parameter to another method.
- Invoke current class methods.
Example of this keyword:
class Point {
int a, b;
Point(int a, int b) {
this.a = a;
this.b = b;
}
void display() {
System.out.println("Point: (" + this.a + ", " + this.b + ")");
}
}
5. How does Java handle static and dynamic binding? Explain the concept of virtual method invocation.
Static Binding: Also known as early binding, occurs when the method is resolved at compile time. It is used for methods marked as final or private, which cannot be overridden.
Dynamic Binding: Occurs when the method is resolved at runtime. Java uses this to support polymorphism. The method to be executed is determined based on the object’s runtime type.
Virtual Method Invocation: In Java, non-static methods are by default “virtual functions.” This means the method implementation that gets invoked is the one belonging to the runtime type of the object, not the type of the reference.
Example:
class Animal {
void eat() {
System.out.println("Animal eats.");
}
}
class Dog extends Animal {
void eat() {
System.out.println("Dog eats bone.");
}
}
class Test {
public static void main(String[] args) {
Animal myAnimal = new Dog();
myAnimal.eat(); // Dog's eat method is called, demonstrating virtual method invocation
}
}
6. Cohesion vs Coupling in Java
Cohesion
- Definition: Cohesion refers to how closely related and focused the responsibilities of a single class or module are.
- Goal: High cohesion.
- High Cohesion: The class does one specific task, making it easier to maintain and reuse.
Example: A UserService
class handling only user-related logic (not payment or product tasks).
Coupling
- Definition: Coupling refers to how much one class or module depends on another.
- Goal: Low coupling.
- Low Coupling: Classes are independent and interact through well-defined interfaces.
Example: If OrderService
depends directly on PaymentService
logic, it has tight coupling. Using interfaces or dependency injection can reduce it.
7. What is the use of Clone Method and implementation in Java?
The clone()
method is used to create a copy (duplicate) of an object in Java. It performs a field-by-field copy of the object. It is defined in the Object
class and must be overridden for custom classes.
Important Points:
- The class must implement
Cloneable
interface to avoidCloneNotSupportedException
. - The
clone()
method should be overridden aspublic
in your class. - It returns a shallow copy by default (deep copy must be manually implemented).
Clone Example
class Student implements Cloneable {
int id;
String name;
Student(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); // shallow copy
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Student s1 = new Student(1, "John");
Student s2 = (Student) s1.clone();
System.out.println(s1.name); // John
System.out.println(s2.name); // John
}
}
8. List of Object methods in Java.
Method Signature | Description |
---|---|
protected Object clone() | Creates and returns a copy of the object (requires Cloneable ) |
boolean equals(Object obj) | Compares this object with another for equality |
protected void finalize() | Called by GC before object is removed (deprecated since Java 9) |
Class<?> getClass() | Returns the runtime class of the object |
int hashCode() | Returns the object’s hash code |
String toString() | Returns a string representation of the object |
void notify() | Wakes up a single thread waiting on this object’s monitor |
void notifyAll() | Wakes up all threads waiting on this object’s monitor |
void wait() | Causes current thread to wait until notify() or notifyAll() is called |
void wait(long timeout) | Waits for the specified time or until notified |
void wait(long timeout, int nanos) | Waits for the specified time in millis and nanos |
Object clone() | Returns a shallow copy of this object (used with Cloneable ) |
All classes in Java implicitly inherit these methods from Object
unless overridden.
9. What is Data hiding and Method hiding?
1. Data Hiding (Encapsulation)
Data Hiding means keeping class data (variables) private, so it can’t be accessed directly from outside the class.
🎯 Purpose:
- To protect data from unwanted access or modification.
- To control access using
getters
andsetters
.
🧠 Example:
class Person {
private String name; // Data is hidden
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
2. Method Hiding
Method Hiding happens when a static method in a subclass has the same signature as a static method in the parent class.
It’s not overriding because static methods belong to the class, not the object.
🧠 Example:
class Parent {
static void show() {
System.out.println("Parent");
}
}
class Child extends Parent {
static void show() { // Method hiding
System.out.println("Child");
}
}
public class Main {
public static void main(String[] args) {
Parent p = new Child();
p.show(); // Output: Parent (not Child)
}
}
10. What is “Has-a” and “Is-a” Relationships?
1. “Is-a” Relationship (Inheritance)
It represents inheritance between classes when one class is a type of another.
Keyword Used: extends
(for classes) or implements
(for interfaces)
Example of Is-a:
class Animal {
void eat() {}
}
class Dog extends Animal {
void bark() {}
}
Dog is an Animal
→ “Is-a” relationship
2. “Has-a” Relationship (Composition/Aggregation)
It represents composition when one class has an object of another class.
Keyword Used: Just use the object reference as a member (no specific keyword)
Example of Has-a:
class Engine {
void start() {}
}
class Car {
Engine engine = new Engine(); // Car has-a Engine
}
Car has an Engine
→ “Has-a” relationship
🆚 Key Differences
Feature | “Is-a” Relationship | “Has-a” Relationship |
---|---|---|
Concept | Inheritance | Composition/Aggregation |
Example | Dog is-a Animal | Car has-a Engine |
Implementation | extends / implements | Create object reference inside class |
Usage | Reuse methods and polymorphism | Code modularity and reuse |
11. What is Association , Aggregation and Composition in Java?
1. Association – “Uses-a” relationship
General connection between two classes where one object uses or interacts with another.
Example:
class Person {
void use(Car car) {
car.drive();
}
}
class Car {
void drive() {
System.out.println("Car is driving");
}
}
A Person uses a Car → This is Association.
2. Aggregation – “Has-a” relationship (Weak Ownership)
A special form of Association where one class has-a reference to another class, but both can exist independently.
Example:
class Department {
String name;
}
class University {
Department department; // Aggregation
}
A University has-a Department, but if University is deleted, Department can still exist independently.
3. Composition – “Owns-a” relationship (Strong Ownership)
A stronger form of Aggregation. One class completely owns the lifecycle of another.
If the parent is destroyed, so is the child.
Example:
class Heart {
void beat() {
System.out.println("Heart beats...");
}
}
class Human {
private Heart heart = new Heart(); // Composition
}
A Human has-a Heart, but a Heart doesn’t exist independently from the Human → This is Composition.
12. Does Java support multiple inheritance? If not then why?
Multiple inheritance means a class inherits from more than one class.
🎯 Java supports multiple inheritance through:
- ✅ Interfaces
- ❌ Not via classes (to avoid complexity like the Diamond Problem)
Java Doesn’t Support Multiple Inheritance with Classes Because of the Diamond Problem.
13. What is the Diamond Problem?
14. Can class be abstract and final both?
No, a class cannot be both abstract
and final
in Java.
abstract
means:
The class is incomplete and meant to be extended. It may contain abstract methods that must be implemented by child classes.
final
means:
The class cannot be extended no other class can inherit from it.
If you write:
final abstract class MyClass {
// Compilation error
}
Compile-time Error:
“Illegal combination of modifiers: ‘abstract’ and ‘final'”.
15. What happens if two interfaces have default methods with same signature?
Java will throw a compile-time error unless the implementing class overrides the method and explicitly resolves the conflict. Java doesn’t know which default implementation to use, so it forces you to resolve the ambiguity manually.
Example
interface A {
default void show() {
System.out.println("A's show");
}
}
interface B {
default void show() {
System.out.println("B's show");
}
}
class C implements A, B {
// Must override to resolve the conflict
public void show() {
System.out.println("C's own show method");
}
}
Output
C's own show method
❌ What if you don’t override?
class C implements A, B {
// No override here!
}
🔴 Compiler Error:
class C inherits unrelated defaults for show() from types A and B
How to Call a Specific Interface’s Default Method:
If needed, you can call a specific interface’s method inside your override.
class C implements A, B {
public void show() {
A.super.show(); // or B.super.show();
}
}
Java Collection Interview Questions
1. Collections VS Collection
Collection
is an interface that defines a group of objects, while Collections
is a utility class with static methods to operate on those collections like sort, reverse, or shuffle.
✅Collection (Interface)
Collection
is a root interface in the Java Collection Framework.- It represents a group of objects (called elements).
- Subinterfaces:
List
,Set
,Queue
, etc.
Example
Collection<String> names = new ArrayList<>();
names.add("John");
names.add("Alice");
✅Collections (Utility Class)
Collections
is a helper/utility class in java.util
package. It contains static methods to work with collections like:
- Sorting
- Searching
- Reversing
- Synchronizing, etc.
Example
List<String> names = new ArrayList<>();
names.add("Z");
names.add("A");
Collections.sort(names); // sorts the list
2. Tell me something about HashMap in Java.
HashMap is a Collection class in Java whose implementation is based on the hash table data structure. HashMap class extends AbstractMap class and AbstractMap class implements the Map interface.
Some Important points about Hashmap:
- HashMap allows only one null key and multiple null values.
- HashMap does not maintain the order of insertion into the map.
- The initial size of the hashmap bucket is 16 which grows to 32 when the map entries cross 75%.
- HashMap uses hashCode() and equals() methods on keys to perform put and get operations. It internally checks whether the key already exists or not.
- Hashmap is not a thread-safe. This is the reason it won’t use in a multithreaded environment.
3. What are load Factor, Threshold, Rehashing, and Collision in hashmap?
Load factor: Load factor is the measure to decide when to increase the size of the Map. By default, the load factor is 75% of the capacity. Initial capacity of HashMap is 16.
Threshold: The threshold can be calculated by multiplying the current capacity and load factor.
Rehashing: Rehashing means, re-calculating the hash code of already stored entries. When entries in the hash table exceed the threshold, the Map is rehashed and increases the size of buckets.
Collision: Collision is a condition when a hash function returns the same bucket location for two different keys.
For example:
Suppose Our HashMap has an initial capacity is 16 and a load factor is 0.75.
Threshold will be 16 * 0.75 = 12,
It means after the 12th entry in the hashmap, the capacity of the hashmap will increase from 16 to 32. Its getting just double.
3. How does HashMap Internally work in Java?
A HashMap
in Java is a data structure that stores date in key-value pairs. It is useful for the fast retrieval and insertion of values based on their keys. It internally uses an array of buckets to store these key-value entries. In hashmap one key can have only one hash output. Two key can also result same output but one key can’t result two output.
General steps for how a HashMap works internally?
Hashing: When you insert a key-value pair into a HashMap
, the hash code of the key is computed using the hashCode()
method of the key object. This hash code is used to determine the index or bucket in the array where the entry will be stored.
Example
for Large Key :- hashcode(key) = key % m
For String key :- Weighted sum
Suppose key is “cat” then hashcode() will be (s[0]*x^0 + s[1]*x^1 + s[2]*x^2)%m
Indexing: The computed hash code is then used to find the index within the array of buckets. The index is typically obtained by performing a modulo operation on the hash code to ensure it fits within the array size.
Suppose if we have a hashtable of size 7 then
hashcode(key) = key%7
Collisions: Collisions occur when two or more keys produce the same hash code and hence map to the same bucket. To handle collisions, Java’s HashMap
uses a concept called chaining. Each bucket contains a linked list (or a balanced tree in later Java 8 versions) of key-value pairs that have the same hash code.
Insertion and Retrieval: When you insert a key-value pair, the HashMap
checks if there is already an entry in the bucket corresponding to the computed index or not. If there is not entry then add a new entry to the bucket. If collision occurs, the newly given entry will be added to the existing linked list (or tree) within the bucket. When you retrieve a value by its key, the HashMap
uses the hash code to find the correct bucket. If bucket found then it searches through the linked list (or tree) within that bucket to locate the desired key-value pair.
Resizing: To maintain efficiency, the HashMap
dynamically resizes its array of buckets when the number of entries crosses a certain threshold. This helps prevent too many collisions and ensures that the average number of entries per bucket remains relatively low.
Example of Hashmap
import java.util.HashMap;
public class HashMapExample {
public static void main(String[] args) {
HashMap<String, Integer> hashMap = new HashMap<>();
hashMap.put("java", 1);
hashMap.put("python", 2);
hashMap.put("html", 3);
int javaValue = hashMap.get("java");
System.out.println("Value of 'java': " + javaValue);
boolean containsKey = hashMap.containsKey("python");
System.out.println("Contains 'python': " + containsKey);
hashMap.remove("html");
System.out.println("Size of HashMap: " + hashMap.size());
}
}
Output
Value of 'java': 1
Contains 'python': true
Size of HashMap: 2
4. Explain how hashcode will be calculated for String Key.
Hashmap has hashCode() method. This method is used to calculate the hashcode value of the String as an Integer.
Syntax of hascode
public int hashCode()
Hashcode calculation code
import java.io.*;
class Main {
public static void main(String[] args) {
String str = "Name";
System.out.println(str);
int hashCode = str.hashCode();
System.out.println(hashCode);
}
}
Output
Name
2420395
Here we got Output 2420395 for String “Name”. Below is the way to calculate the hashcode of the string.
How hashcode is calculated for string key?
Below is the formula to calculate the hashcode value of a String:
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
Here:
- s[i] is the ith character of the string
- ^ is a the exponential operator
- n shows the length of the string
5. What is the Difference between HashMap and LinkedHashMap?
- The difference between HashMap and LinkedHashMap is that LinkedHashMap maintains the insertion order of keys but HashMap doesn’t maintain the insertion order of keys.
- LinkedHashMap requires more memory than HashMap to keep the record of the insertion order. Basically, LinkedHashMap uses doubly LinkedList to keep track of the insertion order of keys.
- LinkedHashMap extends HashMap and implements the Map interface and Hashmap extends AbstractMap class and implements the Map interface.
- Hashmap was introduced in JDK 2.0 but LinkedHashMap was introduced in JDK 4.0.
Here is the Program of HashMap and LinkedHashMap.
Program of HashMap
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
HashMap < String, String > map = new HashMap < > ();
map.put("firstName", "Interview");
map.put("lastName", "Expert");
map.put("rollNo", "1");
System.out.println(map.size());
if (map.containsKey("firstName")) {
System.out.println(map.get("firstName"));
}
if (Integer.parseInt(map.get("rollNo")) < 20) {
System.out.println(map.get("lastName"));
}
}
}
Output
3
Interview
Expert
Program Of LinkedHashMap
import java.util.LinkedHashMap;
public class Main {
public static void main(String[] args) {
LinkedHashMap < String, String > map = new LinkedHashMap < > ();
map.put("firstName", "Interview");
map.put("lastName", "Expert");
map.put("rollNo", "1");
System.out.println(map.size());
if (map.containsKey("firstName")) {
System.out.println(map.get("firstName"));
}
if (Integer.parseInt(map.get("rollNo")) < 10) {
System.out.println(map.get("lastName"));
}
}
}
Output
3
Interview
Expert
6. What is Concurrent HashMap?
ConcurrentHashMap is a class of Collection Framework that provides a concurrent version of HashMap. It is a thread-safe i.e. multiple threads can access the single object of the map.
At a time any number of threads can perform the read operations without locking. But for write/update operation at a single time thread must lock the particular segment in which the thread wants to operate.
Functionality wise HashMap and ConcurrentHashMap are the same, except ConcurrentHashMap maintains concurrency. Hashmap Can have a null key and value but concurrentHashMap Cannot have null key and value pair. ConcurrentHashMap uses a Hashtable data structure.
ConcurrentHashMap Example
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map < String, String > chm = new ConcurrentHashMap < > ();
chm.put(null, null);
}
}
Output
Exception in thread "main" java.lang.NullPointerException
at java.base/java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1011)
at java.base/java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1006)
at Main.main(Main.java:7)
Here we will get a NullPointerException because we are trying to insert a null key value in the concurrent hashmap which is not allowed.
7. What is the Time Complexity of HashMap?
Usually, Time complexity of the Hashmap is O(1). But in some cases, it might change. Let’s assume a scenario in the hashmap when the hashcode will return the same index value for each entries. In that case at a single index of the hashtable, a singly linked list will create to store the key-value pair of the hashmap.
This is a worst-case scenario in HashMap which will be O(n) complexity to lookup. Here we have to walk through all entries in the same hash bucket. This situation is very rare.
There are some improvements done in Java 8 to improve the time complexity from O(n) to O(log n). Instead of using singly linked list, Hashmap in Java 8 uses self-balancing tree like RB tree and AVL tree.
8. Tell me some differences between ArrayList and LinkedList.
- ArrayList uses a dynamic array/Array object data structure to store the elements but LinkedList internally uses a doubly linked list to store the elements.
- ArrayList Manipulation is slow because it uses an array and if we remove any element from the array we have to shift other elements in memory. But LinkedList Manipulation is faster because it uses a doubly linked list so no shift is required when we remove any element.
- ArrayList is better for storing and accessing elements but LinkedList is better for manipulating elements.
- ArrayList takes memory in a contiguous fashion but the linked list does not takes memory in a contiguous.
9. When you will prefer ArrayList and when LinkedList in your RealTime projects?
As We know Accessing an element in ArrayList takes constant time [O(1)] and adding an element takes O(n) in the worst case. And in LinkedList inserting an element takes O(n) time and accessing also takes O(n) time but LinkedList needs some more memory than ArrayList.
Accessing elements in ArrayList is faster because it is index based. But Accessing elements in LinkedList is slower because we need to traverse each node. Insertion and deletion in Linked List are much faster because here we need to change the pointer of the node only. We need not shift the element as we do in ArrayList.
So here we are at the conclusion, if we need to perform more manipulation work on the element and memory is not a problem then we can prefer Linked List over ArrayList.
But When we require more searching of the element then we can choose ArrayList over LinkedList.
Time complexity with operations
For ArrayList<E>
- get(int index) takes O(1).
- add(E element) operation takes O(1) amortized, but in the worst case it will be O(n) since the array must be resized and copied.
- add(int index, E element) takes O(n) Complexity (with n/2 steps on average).
- remove(int index) operation takes O(n) (with n/2 steps on average).
- Iterator.remove() have O(n) Complexity (with n/2 steps on average).
- ListIterator.add(E element) operation takes O(n) (with n/2 steps on average)
For LinkedList<E>
- get(int index) operation takes O(n) , It will take O(1) when index = 0 or index = list.size() -1 .
- add(int index, E element) take O(n) and It will take O(1) when index = 0 or index = list.size() – 1
- remove(int index) takes O(n) and It will take O(1) when index = 0 or index = list.size() – 1.
10. What is the difference between HashSet and LinkedHashSet?
- HashSet uses Hashtable to store the elements. LinkedHashSet uses HashTable and a doubly linked list to store and maintain the insertion order of the elements.
- HashSet extends AbstractSet class but LinkedHashSet extends HashSet.
- HashSet won’t maintain the insertion order of the element but LinkedHashSet maintains the insertion order of an element.
- HashSet takes less memory in comparison to LinkedHashSet because HashSet won’t maintain the insertion order but LinkedHashSet maintains the insertion order.
- HashSet provides faster performance than LinkedHashSet.
11. Differences between HashMap and HashSet.
- Hashmap is the implementation of the Map interface But HashSet is the implementation of the Set interface.
- HashSet internally uses Hashmap for its implementation But HashMap Won’t.
- HashMap stores value in key-value pair But HashSet Stores only object no key-value pair.
- HashMap uses the put(k,v) method to add elements to the map whereas HashSet uses add(K) method.
- HashMap Allows duplicate values not key But HashSet won’t allow duplicate values.
- HashMap allows a single null key and multiple null values but HashSet allow only one null value.
12. What is the purpose of the equals() and hashCode() methods? Why should they be overridden together?
In Java, the equals() method is used to determine if two objects are equal based on their content, not their reference. The hashCode() method provides a way of computing a small integer signature from an object’s data that helps in efficient lookup in collections like HashMap.
When you override equals(), you should also override hashCode() because objects considered equal (by equals()
) must produce the same hash code. If they don’t, it can cause problems when using hash-based collections like HashMap
or HashSet
.
Example
class Student {
String name;
int age;
// Constructor
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// Override equals method
@Override
public boolean equals(Object obj) {
if (this == obj) return true; // Check for reference equality
if (obj == null || getClass() != obj.getClass()) return false; // Check for null and ensure exact class match
Student student = (Student) obj; // Cast the object to Student
return age == student.age && Objects.equals(name, student.name); // Check if values are equal
}
// Override hashCode method
@Override
public int hashCode() {
return Objects.hash(name, age); // Calculate hash code based on name and age
}
}
Why Override Both?
It’s crucial to override both equals()
and hashCode()
because failing to do so can lead to inconsistent behavior when your objects are placed in any hash-based collection, such as HashMap
or HashSet
. If two objects are considered equal by the equals()
method but generate different hash codes, they might be mistakenly added as separate entries in a HashSet
, which contradicts the set’s properties.
13. What is the difference between a shallow copy and a deep copy?
A shallow copy of an object copies the values of the object’s properties. However, if the property values are references to other objects, the references are copied but not the objects they point to.
Conversely, a deep copy copies all fields, and if the fields contain references to other objects, it copies those objects as well. A deep copy is fully independent of the original object.
Example
class Rectangle {
Point origin;
int width;
int height;
// Shallow copy constructor
Rectangle(Rectangle other) {
this.origin = other.origin; // copies the reference
this.width = other.width;
this.height = other.height;
}
// Deep copy method
Rectangle deepCopy() {
Rectangle newRect = new Rectangle();
newRect.origin = new Point(this.origin.x, this.origin.y); // copies the object
newRect.width = this.width;
newRect.height = this.height;
return newRect;
}
}
14. What is the difference between a HashSet and a TreeSet?
A HashSet stores its elements in a hash table, which means it uses the hashCode()
method to retrieve its elements efficiently. Elements in a HashSet are unordered, and it provides faster operations like add, remove, and contains.
A TreeSet stores its elements in a red-black tree, maintaining them in sorted order according to their natural ordering or a specified comparator. TreeSet offers several methods to deal with the sorted nature of its elements, such as first()
, last()
, headSet()
, and tailSet()
.
Example of HashSet and a TreeSet
#HashSet Example
Set<Integer> hashSet = new HashSet<>();
hashSet.add(2);
hashSet.add(3);
hashSet.add(1);
System.out.println(hashSet); // Outputs [1, 2, 3] it could be in any order
#TreeSet Example
Set<Integer> treeSet = new TreeSet<>();
treeSet.add(2);
treeSet.add(3);
treeSet.add(1);
System.out.println(treeSet); // Outputs [1, 2, 3] in sorted order
15. How does Java handle concurrent modification exceptions? Explain the use of iterators and ConcurrentModificationException.
A ConcurrentModificationException is thrown when a collection is modified while iterating over it, except through its own iterator’s remove method. To safely modify a collection during iteration, use the iterator’s own methods for modifications.
Example 1: It will give ConcurrentModificationException
import java.util.*;
class Main {
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String item = it.next();
System.out.println(item);
if (item.equals("b")) {
// Trying to modify the collection directly while iterating over it
list.remove(item); // This line will throw ConcurrentModificationException
}
}
System.out.println(list);
}
}
<strong>Output</strong>
a
b
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1042)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:996)
at Main.main(Main.java:7)
Example 2: It will not give ConcurrentModificationException
import java.util.*;
class Main {
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String value = it.next();
if ("c".equals(value)) {
it.remove();
}
}
System.out.println(list);
}
}
Output
[a, b, d]
16. What is the difference between HashMap and HashTable?
HashMap is a part of Java’s collection since Java 1.2. It allows storing key-value pairs in hash tables. HashMap is not synchronized, which means it is not safe for multithreaded use without external synchronization. It allows one null key and multiple null values.
HashTable is a legacy class similar to HashMap, but it is synchronized, which makes it thread-safe for concurrent access without additional synchronization. However, its synchronized nature means it’s generally slower than HashMap. HashTable does not allow null keys or null values.
Example
Map<String, String> hashMap = new HashMap<>();
hashMap.put(null, "value"); // allowed
Map<String, String> hashTable = new Hashtable<>();
hashTable.put(null, "value"); // It will Throws NullPointerException
17. What is the equals() and hashCode() contract?
In Java, the equals()
and hashCode()
methods are used to compare objects and store them efficiently in collections like HashMap, HashSet, etc.
Contract Rules:
- If two objects are equal according to
equals()
, then they must have the samehashCode()
.- ✅
a.equals(b)
→a.hashCode() == b.hashCode()
- ✅
- If two objects have the same
hashCode()
, they may or may not be equal.- ✅ Good for performance in hash-based collections.
- If
equals()
is overridden,hashCode()
must also be overridden to maintain the contract.
If the contract is violated:
- You may face unexpected behavior in collections like
HashMap
,HashSet
. - Equal objects may end up in different hash buckets, breaking search logic.
18. Synchronized vs Concurrent collections
Synchronized collections lock the entire structure and are slower, while concurrent collections allow multiple threads to work efficiently with fine-grained control and better performance
Synchronized Collections
- These are legacy thread-safe collections.
- Only one thread can access them at a time.
- Use synchronized blocks internally → slower performance under heavy load.
Examples
Collections.synchronizedList(new ArrayList<>())
Vector
Hashtable
Concurrent Collections
- Designed for high-performance multi-threaded environments.
- Allow concurrent reads and controlled writes without full locking.
- More scalable and efficient.
Examples
ConcurrentHashMap
CopyOnWriteArrayList
ConcurrentLinkedQueue
19. Do you know about the Unmodifiable collections?
Unmodifiable collections are read-only views that prevent modification of the data structure. They’re useful for ensuring safety and immutability in code.
Unmodifiable collections are collections that cannot be changed after they are created.
- ❌ You can’t add, remove, or modify elements.
- ✅ They are read-only views of existing collections.
20. How to Create Unmodifiable Collections?
Using Collections.unmodifiableX()
(Java 8 and below):
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
List<String> unmodifiableList = Collections.unmodifiableList(list);
unmodifiableList.add("C++"); // ❌ Throws UnsupportedOperationException
Using List.of()
or Set.of()
(Java 9+):
List<String> list = List.of("Java", "Python"); // Immutable list
list.add("C++"); // ❌ Throws UnsupportedOperationException
21. How can covert a given collection to synchronized collection.
You can convert a normal collection into a synchronized (thread-safe) collection using the Collections.synchronizedXXX()
methods provided by the Java Collections utility class, like Collections.synchronizedList()
or synchronizedMap()
, and for safe iteration.
Example of Synchronized List
List<String> list = new ArrayList<>();
List<String> syncList = Collections.synchronizedList(list);
22. Collision Handling in Java (HashMap), Chaining via LinkedList or Tree.
When two keys hash to the same bucket in a HashMap
, it causes a collision. Java handles this using chaining, where multiple entries are stored in the same bucket.
HashMap handles collisions using chaining. Initially, it uses a LinkedList, but from Java 8 onward, if collisions in a bucket exceed a threshold, it switches to a Red-Black Tree to maintain O(log n) performance.
1. Chaining with LinkedList (Before Java 8)
- Each bucket stores a LinkedList of entries (
Map.Entry
objects). - On collision, new entries are simply added to the list.
- Drawback: If too many collisions happen, the list becomes long → performance degrades to O(n).
Bucket 5 → [Entry1] → [Entry2] → [Entry3]
2. Chaining with Tree (Java 8 and above)
- From Java 8 onwards, if the number of entries in a bucket exceeds a threshold (usually 8), the LinkedList is converted to a balanced Red-Black Tree.
- This improves lookup time from O(n) to O(log n).
Bucket 5 → Red-Black Tree (sorted by hash & equals)
Multi-threading Questions
1). What is the lifecycle of Thread?
The lifecycle of a thread in Java consists of five main states:
- New – When a thread is created using the
Thread
class but not started yet. - Runnable – After calling
start()
, the thread is ready to run and waiting for CPU time. - Running – The thread is currently executing.
- Blocked/Waiting – The thread is paused, either waiting for a resource or waiting for another thread’s action.
- Terminated (Dead) – The thread has finished execution or has been stopped.
2). What are the Different ways to create threads in Java?
There are two ways to create threads in Java:
- By extending the Thread class.
- By implementing a Runnable interface.
1). By Extending the Thread Class
class Main extends Thread{
public void run(){
System.out.println("thread is running");
}
public static void main(String args[]){
Main thread1 = new Main();
thread1.start();
}
}
Output:
Thread is running
2). By implementing a Runnable interface
class Main implements Runnable {
public void run() {
System.out.println("thread is running");
}
public static void main(String args[])
Main obj = new Main(); // Here we are Using the constructor Thread(Runnable r)
Thread tobj = new Thread(obj);
tobj.start();
}
}
Output:
Thread is running
3. Difference between Thread and Runnable.
Aspect | Thread Class | Runnable Interface |
Type | It’s a class (extends java.lang.Thread) | It’s a functional interface (has a single method run()) |
Inheritance | Cannot extend another class if you extend Thread (Java supports only single inheritance) | Allows extending another class as it only implements an interface |
Memory Usage | Each Thread object creates a separate memory stack | Less memory as multiple threads can share a single Runnable object |
Object Sharing | Each thread has its own object | Same Runnable object can be shared across multiple threads |
Method Availability | Inherits methods like start(), sleep(), join(), getName(), etc. | Only the run() method is defined — you must pass it to a Thread object to start execution |
Preferred Use | Suitable for small, simple applications with limited inheritance needs | Preferred for large applications and when working with thread pools or shared resources |
Thread Creation | Subclass Thread and override run() method | Implement Runnable and pass it to a Thread object |
Java 8 Support | Not functional interface — cannot use lambda expressions | Is a functional interface — supports lambda syntax for cleaner code |
Summary:
- Use Runnable when your class needs to extend something else or you want to use thread pooling.
- Use Thread when you need full control over thread behavior and you don’t need to extend any other class.
4. Tell some Differences between Callable & Runnable in Java.
Aspect | Callable Interface | Runnable Interface |
Package | java.util.concurrent | java.lang |
Method | Has call() method | Has run() method |
Return Type | Returns a result (V) from call() | run() returns void (no result) |
Exception Handling | Can throw checked exceptions | Cannot throw checked exceptions (only unchecked) |
Thread Creation | Cannot directly pass to Thread constructor | Can be passed directly to Thread constructor |
Use With ExecutorService | Used with ExecutorService.submit() to get a Future object | Used with Executor.execute() for fire-and-forget tasks |
Result Retrieval | Returns result via Future.get() | No result returned |
Bulk Execution Support | Supports bulk execution using invokeAll() | Not supported for bulk execution via ExecutorService |
Java Version | Introduced in Java 5 | Available since Java 1.0 |
5. What is the difference between sleep() & wait() methods in Java.
- Sleep() method belongs to the Thread class whereas Wait() method belongs to the Object class.
- Sleep() method will not release the lock on the object during Synchronization but Wait() method can release the lock on the object during Synchronization.
- Sleep() method is used to pause the execution of the current thread for a given time in Milliseconds. But the wait() method tells the thread to wait until another thread invoke’s the notify() or notifyAll() method for this object.
- Sleep() is a static method but wait() is not a static method.
- Sleep() has two overloaded methods sleep(long millis) and sleep(long millis, int nanos), Whereas Wait() has Three Overloaded methods wait(), wait(long timeout), wait(long timeout, int nanos).
Example of Sleep method
import java.lang.Thread;
class Main {
public static void main(String[] args)
{
try {
for (int i = 1; i <=10; i++) {
Thread.sleep(1000);
System.out.println(i);
}
}
catch (Exception e) {
System.out.println(e);
}
}
}
Output
1
2
3
4
5
6
7
8
9
10
6. What is the difference between the final, finally, and finalize keywords in Java?
final
The final keyword can be used with classes, methods, or variables. When used with a variable, it means the variable cannot be changed once initialized. When used with a method, it prevents the method from being overridden in a subclass. When used with a class, it prevents the class from being subclassed.
Example:
final int NUMBER = 9;
// NUMBER = 10; // This would cause a compiler error because NUMBER is fina
finally
The finally block is part of Java’s exception handling. It always executes when the try block exits, regardless of whether an exception was thrown or caught. It’s typically used to close or release resources like files or databases.
Example:
try {
// code that might throw an exception
} catch (Exception e) {
// handle exception
} finally {
// code that will always run
}
finalize
The finalize method is called by the garbage collector on an object when garbage collection determines that there are no more references to the object. It’s generally used to clean up resources before the object is destroyed, though its use is discouraged in favor of other resource-management techniques.
Example:
protected void finalize() {
// cleanup code before garbage collection
}
7. Explain Synchronization mechanisms, thread pools, and the ForkJoinPool framework.
Synchronization mechanisms
Synchronization in Java is used to control access to shared resources when multiple threads are running. It makes sure that only one thread can use a method or code block at a time.
The synchronized
keyword helps avoid problems like data inconsistency or unexpected behavior caused by concurrent access.
Example:
public synchronized void accessResource() {
// only one thread can execute this at a time
}
Thread pools
A Thread Pool is a group of ready-to-use threads that wait to perform tasks. Instead of creating a new thread for every task (which is slow and costly), tasks are given to these pre-created threads, improving performance.
Java provides ExecutorService
, which is a high-level tool to manage thread pools. It allows you to:
- Track task progress using
Future
objects for asynchronous operations. - Submit tasks easily
- Shut down threads properly
Example:
ExecutorService executor = Executors.newFixedThreadPool(3);
executor.submit(() -> {
System.out.println("Running in a thread pool!");
});
executor.shutdown();
ForkJoinPool framework
ForkJoinPool
is a special implementation of ExecutorService
in Java that helps take advantage of multi-core processors. It works by:
- Forking (splitting) a big task into smaller subtasks
- Processing them in parallel
- Then joining the results together
It’s ideal for recursive and parallelizable tasks, like sorting large arrays or processing big data sets
Example:
ForkJoinPool forkJoinPool = new ForkJoinPool();
forkJoinPool.submit(() -> {
// perform some parallel computation
});
forkJoinPool.shutdown();
8. What is the purpose of the volatile keyword in Java?
The volatile
keyword in Java tells the program that a variable is shared between threads, and its value can change at any time.
When a variable is marked volatile
:
- Every read gets the latest value from main memory.
- Every write goes directly to main memory.
- It prevents threads from using cached or outdated values.
It ensures visibility, but not atomicity — so it’s not a replacement for full synchronization when complex operations are involved.
Example:
volatile boolean running = true;
public void stopRunning() {
running = false; // Changes made here will be visible to other threads immediately
}
9. Explain the concept of Serialization and Deserialization in Java.
Serialization is the process of converting an object’s state to a byte stream, thus making it easy to save it to a file, send it over a network, or store in a database. Deserialization is the reverse process where the byte stream is converted back into a original object.
Example:
import java.io.*;
public class SerializeExample {
// Serialize an object to a file
public static void serialize(Object obj, String filename) throws IOException {
try (FileOutputStream fileOut = new FileOutputStream(filename);
ObjectOutputStream out = new ObjectOutputStream(fileOut)) {
out.writeObject(obj);
}
}
// Deserialize an object from a file
public static Object deserialize(String filename) throws IOException, ClassNotFoundException {
try (FileInputStream fileIn = new FileInputStream(filename);
ObjectInputStream in = new ObjectInputStream(fileIn)) {
return in.readObject();
}
}
}
10. What is Executor Framework?
The Executor Framework in Java is a part of the java.util.concurrent package that provides a powerful and flexible way to manage and run multiple threads easily. It decouples task submission from thread management by using Executor, ExecutorService, and ThreadPoolExecutor. Instead of manually creating and managing threads, you can simply submit tasks (Runnable or Callable) and let the framework handle thread reuse, scheduling, and pooling that make your code cleaner, more efficient, and scalable.
11. How do you create a thread pool using Executor Framework?
A thread pool is a group of worker threads that are ready to perform tasks. Instead of creating a new thread every time you have a job, you take one from the pool. This saves time and resources.
You can create a pool of threads using:
ExecutorService executor = Executors.newFixedThreadPool(5);
This creates a thread pool with 5 threads.
You can also use other types of pools:
Executors.newCachedThreadPool(); // Dynamically creates new threads as needed
Executors.newSingleThreadExecutor(); // Only one thread executes at a time
In Simple Words:
Think of it like a group of 5 workers sitting in a room (thread pool). You give them 10 jobs (tasks), and they complete them one by one. Instead of hiring a new person for every job, you reuse the same 5 workers.
12. How do you create threads using Executor Framework?
When you use Executor Framework You don’t create threads directly like this anymore:
Thread t = new Thread(new MyTask());
t.start();
Instead, you let the Executor Framework handle it. It will create threads for you and run your tasks.
Steps to create threads using Executor Framework:
- Create a task using Runnable or Callable.
- Create an ExecutorService using Executors class.
- Submit the task to ExecutorService.
- It will automatically create and manage threads to run the tasks.
Example using Runnable:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {// Step 1: Create ExecutorService
ExecutorService executor = Executors.newFixedThreadPool(3);// Step 2: Create and submit tasks (threads are created internally)
executor.submit(new MyTask());
executor.submit(new MyTask());
executor.submit(new MyTask());// Step 3: Shutdown executor
executor.shutdown();
}
}
class MyTask implements Runnable {
public void run() {
System.out.println("Running in thread: " + Thread.currentThread().getName());
}
}
Output
Running in thread: pool-1-thread-1
Running in thread: pool-1-thread-3
Running in thread: pool-1-thread-2
13. How do you use Executor Framework? What is the purpose of Executor Framework?
Steps to Use Executor Framework:
- Create a task – using
Runnable
orCallable
. - Create an ExecutorService – using methods like:
Executors.newFixedThreadPool(int n)
Executors.newCachedThreadPool()
Executors.newSingleThreadExecutor()
- Submit the task – using
submit()
orexecute()
method. - Shut down the executor – using
shutdown()
after tasks are done.
Java Code of Executor Framework
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
executor.submit(new MyTask());
executor.submit(new MyTask());
executor.submit(new MyTask());
executor.shutdown(); // important to release resources
}
}
class MyTask implements Runnable {
public void run() {
System.out.println("Running task in: " + Thread.currentThread().getName());
}
}
🎯 What is the purpose of Executor Framework?
The Executor Framework in Java is used to manage and control multiple threads efficiently.
Main Purpose:
- To simplify thread management
- To reuse threads using thread pools
- To avoid creating a new thread for every task (which is slow and wasteful)
- To manage task execution (start, pause, shut down)
14. Have you used Future and CompletableFuture?
Yes, I have used both Future and CompletableFuture in Java for handling asynchronous tasks.
Future is used when I want to execute a task in a separate thread and get the result later, but it has some limitations.
CompletableFuture is more powerful — it supports chaining, callbacks, and non-blocking programming, which makes it very useful in real-world async operations like API calls or file processing.
What is Future?
- Introduced in Java 5.
- Represents a result that will be available in the future.
- Used with ExecutorService.
Example:
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(() -> {
Thread.sleep(1000);
return 10;
});
Integer result = future.get(); // blocks until result is ready
But Future has drawbacks:
- get() is blocking
- No easy way to chain multiple tasks
- No support for callbacks
What is CompletableFuture?
- Introduced in Java 8.
- Supports non-blocking, asynchronous, and functional-style programming.
- Can run tasks in the background, chain them, and add callbacks when done.
Example:
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
return 10;
});
future.thenApply(result -> result * 2)
.thenAccept(finalResult -> System.out.println("Final Result: " + finalResult));
Benefits:
- Non-blocking
- Supports task chaining (thenApply, thenAccept)
- Exception handling (exceptionally, handle)
- Parallel execution (thenCombine, allOf, anyOf)
15. What is the purpose of the ThreadLocal class in Java?
ThreadLocal provides variables that are local to each thread. Each thread gets its own independent value. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread.
Use case: Store user session data, database connections, or any data that should be kept separate per thread.
16. What is the Parent class of Throwable?
The parent class of Throwable
in Java is Object
.
java.lang.Object
└── java.lang.Throwable
├── java.lang.Error
└── java.lang.Exception
17. What is Thread Priority?
Thread Priority in Java is a number that indicates the importance of a thread to the CPU scheduler. It helps the JVM decide which thread to run first when multiple threads are ready to run.
Priority Range:
- Minimum:
Thread.MIN_PRIORITY
= 1 - Default:
Thread.NORM_PRIORITY
= 5 - Maximum:
Thread.MAX_PRIORITY
= 10
How to Set Thread Priority?
Thread t1 = new Thread();
t1.setPriority(8); // Set priority between 1 and 10
18. What is a Daemon Thread in Java?
A Daemon Thread in Java is a background thread that runs in support of user threads, performing low-priority tasks like garbage collection and monitoring. It does not prevent the JVM from exiting—the JVM automatically shuts down once all user (non-daemon) threads finish, even if daemon threads are still running.
How to Create a Daemon Thread?
Thread t = new Thread(() -> {
while (true) {
System.out.println("Daemon running...");
}
});
t.setDaemon(true); // Must be set before start()
t.start();
19. What is the purpose of sleep(), yield(), and join() methods?
sleep()
- Purpose: Pauses the current thread for a specified time.
- Used for: Delaying execution without releasing the lock.
- Syntax:
Thread.sleep(milliseconds)
- Throws:
InterruptedException
Example: Delay for 1 second
Thread.sleep(1000);
yield()
- Purpose: Gives a hint to the thread scheduler to pause the current thread and allow others of equal priority to run.
- Used for: Improving fairness in thread execution.
- Note: It does not guarantee any specific behavior.
Example
Thread.yield();
join()
- Purpose: Allows one thread to wait for another thread to finish.
- Used for: Thread coordination and sequencing.
- Syntax:
thread.join()
- Throws:
InterruptedException
Example: Wait for t1
to complete
t1.join();
Exceptional Handling in Java
1. What is Exceptional Handling in Java?
In Java, Exception Handling is a mechanism to handle runtime errors and protect the program from abnormal termination. It helps to maintain the normal flow of the application in case any exceptions come.
There are multiple types of Exceptions such as ClassNotFoundException, IOException, and SQLException.
java.lang.Throwable class is the root class of the Java Exception hierarchy. It is inherited by two subclasses Exception and Error. The hierarchy of Java Exception classes is given below:
2. Difference between Checked and Unchecked Exceptions.
Aspect | Checked Exception | Unchecked Exception |
Definition | Exceptions that are checked at compile-time. | Exceptions that occur at runtime and are not checked at compile-time. |
Class Hierarchy | Subclasses of Exception (excluding RuntimeException) | Subclasses of RuntimeException |
Compile-Time Checking | Yes – compiler forces handling using try-catch or throws. | No – compiler does not force handling. |
When It Occurs | During compile-time | During runtime |
Handling Requirement | Must be handled or declared in method signature. | Handling is optional. |
Examples | IOException, SQLException, ClassNotFoundException | NullPointerException, ArrayIndexOutOfBoundsException, ArithmeticException |
Used For | Situations that are outside the program’s control (e.g., file not found) | Programming errors or logic mistakes |
Program Behavior if Not Handled | Compile-time error | Program will compile but may crash at runtime |
3). What is the difference between an Error and an Exception?
In Java, both Error and Exception are types of Throwable, which means they can be thrown during the execution of a program. But they are very different in terms of what they represent and how they are handled.
🔴 1. Error
- Represents serious problems that a program should not try to catch.
- Usually caused by the Java Virtual Machine (JVM).
- These are not meant to be handled in code.
- Examples: OutOfMemoryError, StackOverflowError.
Example
public class ErrorExample {
public static void main(String[] args) {
causeStackOverflow();
}
static void causeStackOverflow() {
causeStackOverflow(); // Infinite recursion
}
}
Output
Exception in thread "main" java.lang.StackOverflowError
🟢 2. Exception
- Represents issues that can be handled in your code.
- Caused by program errors or external factors.
- There are two types:
- Checked Exceptions (e.g., IOException)
- Unchecked Exceptions (e.g., NullPointerException)
- Represents issues that can be handled in your code.
Example
public class ExceptionExample {
public static void main(String[] args) {
try {
int result = 10 / 0; // ArithmeticException
} catch (ArithmeticException e) {
System.out.println("Cannot divide by zero.");
}
}
}
Output
Cannot divide by zero.
Error VS Exception
Aspect | Error | Exception |
Represents | Serious system-level problems | Problems in program logic |
Caused by | JVM (hardware failure, memory, etc.) | Program or external issues (e.g., I/O) |
Can be caught? | Not usually | Yes, using try-catch |
Should be handled? | No – let JVM handle it | Yes – developer should handle it |
Examples | OutOfMemoryError, StackOverflowError | NullPointerException, IOException |
4. How many ways you can print exceptions in Java?
There are multiple ways to print the exceptions in Java. Here we will see 3 methods to print the exceptions.
- printStackTrace()
- toString()
- getMessage()
1). printStackTrace()
printStackTrace method prints exception information printStackTrace() is a method of Java.lang.Throwable class. It is used to print the details of exceptions like class name and line number where the exception occurred.
Program to demonstrate printStackTrace() method
import java.io.*;
class Main {
public static void main (String[] args) {
int a=5, b=0;
try{
System.out.println(a/b);
}
catch(ArithmeticException e){
e.printStackTrace();
}
}
}
Output:
java.lang.ArithmeticException: / by zero
at Main.main(Main.java:6)
2). toString()
toString() method is also used to print exceptions. It prints exception information in the format of Name of the exception along with a description of the exception.
Program to demonstrate toString() method
import java.io.*;
class Main {
public static void main (String[] args) {
int a=5, b=0;
try{
System.out.println(a/b);
}
catch(ArithmeticException e){
System.out.println(e.toString());
}
}
}
Output:
java.lang.ArithmeticException: / by zero
3). getMessage()
getMessage() method prints only the description of the exception message.
Program to demonstrate getMessage() method
import java.io.*;
class Main {
public static void main (String[] args) {
int a=5, b=0;
try{
System.out.println(a/b);
}
catch(ArithmeticException e){
System.out.println(e.getMessage());
}
}
}
Output:
/ by zero
5. What is the process to create custom Exception in Java?
Here we are showing two examples to create your own custom exception class in Java.
Program 1: Create Custom Exception
class CustomException extends Exception{
public CustomException(String ex){
super(ex);
}
}
public class Main{
public static void main(String ...a){
int age = 13;
try{
if(age<18){
throw new CustomException("no valid Age");
}else{
System.out.println("Age is valid");
}
}
catch(CustomException e){
System.out.println("Exception occured: " + e);
}
System.out.println("Rest Code is running");
}
}
Output
Exception occured: CustomException: no valid Age
Rest Code is running
Program 2: Create Custom Exception
class CustomException extends Exception {
String message;
CustomException(String str) {
message = str;
}
public String toString() {
return ("Custom Exception Occurred : " + message);
}
}
public class Main {
public static void main(String args[]) {
try {
throw new CustomException("This is CustomException");
} catch(CustomException e) {
System.out.println(e);
}
}
}
Output
Custom Exception Occurred : This is CustomException
6. Write the difference between Throw and Throws.
Throw vs Throws
- throw keyword is used inside the function or the block of code to throw an exception explicitly when an exception occurs. Whereas throws keyword is used with method signature with exception means exception might be thrown by the function during the execution if an exception occurs.
- throw keyword is followed by an instance of Exception that will be thrown whereas throws keyword is followed by class names of Exceptions to be thrown.
- throw keyword can be used to throw only one exception at a time but in throws keywords, we can use multiple Exception classes separated by a comma.
- throw keyword is used to propagate unchecked Exceptions whereas the throws keyword is used to propagate checked Exceptions.
Program of using throw keyword
public class Main{
public void checkAge(int age){
if(age<18)
throw new ArithmeticException("Age is not valid for voting");
else
System.out.println("Age is valid for voting");
}
public static void main(String args[]){
Main obj = new Main();
obj.checkAge(10);
System.out.println("Execution continue");
}
}
Output
Exception in thread "main" java.lang.ArithmeticException: Age is not valid for voting
at Main.checkAge(Main.java:4)
at Main.main(Main.java:10)
Program of using throws keywords
public class Main{
public int division(int a, int b) throws ArithmeticException{
int result = a/b;
return result;
}
public static void main(String args[]){
Main obj = new Main();
try{
System.out.println(obj.division(15,0));
}
catch(ArithmeticException e){
System.out.println("We can't divide number by zero");
}
}
}
Output
We can't divide number by zero
7. What is Serialization and why do we use it?
Serialization is the process of converting a Java object into a byte stream (sequence of bytes).
This is useful because:
- You can send objects over a network (e.g., to another computer or server).
- You can save the object to a file or database, and later restore it.
Why is Serialization Needed?
Java objects are complex and exist only in memory (RAM).
But hardware like:
- Network cables
- Hard disks
- External devices
So, we serialize an object (translate it into bytes) to:
- Send it over the internet
- Store it permanently on disk
- Cache it or transfer between programs
What Is serialVersionUID?
serialVersionUID is a unique version identifier for a Serializable class.
Java uses this ID during deserialization to ensure that:
- The sender’s class (who serialized the object)
- And the receiver’s class (who is deserializing the object)
…are compatible.
SerialVersionUID is used here for identity purposes. It must be static, final, and of type long.
🚨 When Should You Change the serialVersionUID?
Change Type | Need to Update serialVersionUID? |
✅ Add a new field | ❌ No |
✅ Add a new method | ❌ No |
❌ Remove a field | ✅ Yes |
❌ Change field type (int → String) | ✅ Yes |
❌ Rename a field | ✅ Yes |
❌ Rename the class or package | ✅ Yes |
❌ Change class from public → private | ✅ Yes |
Java Program for Serialization and Deserialization
import java.io.*;
public class Main {
public static void main(String[] args) {
Student student = new Student("quescol", 2);
byte[] bytes = convertObjectToBytes(student);
System.out.println(bytes);
Student s = (Student) convertBytesToObject(bytes);
System.out.println(s);
}
public static byte[] convertObjectToBytes(Object obj) {
ByteArrayOutputStream boas = new ByteArrayOutputStream();
try (ObjectOutputStream ois = new ObjectOutputStream(boas)) {
ois.writeObject(obj);
return boas.toByteArray();
} catch (IOException ioe) {
ioe.printStackTrace();
}
throw new RuntimeException();
}
public static Object convertBytesToObject(byte[] bytes) {
InputStream is = new ByteArrayInputStream(bytes);
try (ObjectInputStream ois = new ObjectInputStream(is)) {
return ois.readObject();
} catch (IOException | ClassNotFoundException ioe) {
ioe.printStackTrace();
}
throw new RuntimeException();
}
}
class Student implements Serializable {
private String name;
private int age;
public Student( String name, int id) {
this.age = age;
this.name = name;
}
}
Output
[B@7d4793a8
Student@721e0f4f
8. What is the Volatile and Transient keyword in Java?
- A volatile keyword is used in a multithreading environment. When two threads are performing reading and writing operations on the same variable, a volatile keyword is used. It flushes the changes directly to the main memory instead of the CPU cache. A transient keyword is used in serialization. Variable where transient is used, cannot be part of the serialization and deserialization.
- Volatile variable is not initialized with any default value whereas transient variables are initialized with a default value during deserialization.
- Volatile variable can be used with the final keyword whereas Transient cannot be used with the final keyword.
- Volatile variable can be used with static keyword whereas Transient cannot be used with the static keyword.
9. How you will create an Immutable class in Java?
In Java, an Immutable class means once the object is created in the class, its fields cannot be changed or modified.
All the wrapper classes like Boolean, Byte, Long, Short, Integer, Float, Double, Char, and Stringare immutable.
Steps to create an immutable class in Java
- First Declare the class with the final keyword so it can’t be extended.
- Make all the classes private. It will not allow direct access.
- Don’t make any setter methods for variables.
- Make all mutable fields final so that their value can be assigned only once.
- Initialize all fields value using the constructor.
Program of Immutable Java Class
final class Student{
private String name;
private int roll;
Student(String name, int roll) {
this.name = name;
this.roll = roll;
}
public String getName() {
return name;
}
public int getRoll() {
return roll;
}
}
class Main {
public static void main(String[] args) {
Student obj = new Student("quescol", 1);
System.out.println("Name: " + obj.getName());
System.out.println("Roll: " + obj.getRoll());
}
}
Output
Name: quescol
Roll: 1
10. How you will Create a Singleton class in Java.
Singleton class is a class in oops that can have only one object at a time and can be globally accessible.
It Saves memory because Only a single instance is created and reused again and again.
Singleton Class is mostly used in the multi-threaded system, database application, logging, caching, configuration settings, etc.
There are two forms of singleton design pattern
Early Instantiation: In this Singleton design pattern object will create at the load time.
Lazy Instantiation: In this Singleton design pattern object will create according to the requirement.
Early Instantiation Program
class Singlton {
//Instance will be created at load time
private static Singlton obj=new Singlton();
private Singlton(){}
public static Singlton getSinglton(){
return obj;
}
}
class Main {
public static void main(String[] args) {
Singlton s1;
s1= Singlton.getSinglton();
Singlton s2;
s2= Singlton.getSinglton();
System.out.println(s1);
System.out.println(s2);
}
}
Output
Singlton@1e81f4dc
Singlton@1e81f4dc
Lazy Instantiation example
class Singlton {
private static Singlton singlton;
privae Singlton(){
}
public static Singlton getInstance() {
if(null == singlton){
singlton = new Singlton();
}
return singlton;
}
}
class Main {
public static void main(String[] args) {
Singlton s1;
s1= Singlton.getInstance();
Singlton s2;
s2= Singlton.getInstance();
System.out.println(s1);
System.out.println(s2);
}
}
Output
Singlton@3b22cdd0
Singlton@3b22cdd0
11. Runtime and Compile-Time Exceptions.
Compile-Time Exceptions (Checked Exceptions)
- These are exceptions checked by the compiler during compilation.
- You must handle them using
try-catch
or declare withthrows
. - Mostly occur due to external factors (like file I/O, network).
Examples:IOException
, SQLException
, FileNotFoundException
try {
FileReader file = new FileReader("data.txt"); // Checked exception
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Runtime Exceptions (Unchecked Exceptions)
- These occur during program execution (runtime).
- Not checked by the compiler.
- Usually caused by programming errors like bad logic or invalid input.
Examples:NullPointerException
, ArrayIndexOutOfBoundsException
, ArithmeticException
int x = 10 / 0; // Runtime exception (ArithmeticException)
12. How return
statement behaves in catch
and finally
blocks in Java?
If a finally
block contains a return
statement, it overrides the return from try
or catch
. If only try
or catch
has return
, that value is returned after finally
completes execution.”
1. Return in catch
block
You can return from a catch
block. It returns the value unless finally
also has a return, which will override it.
public int test() {
try {
int a = 5 / 0; // will throw ArithmeticException
} catch (Exception e) {
return 10;
} finally {
System.out.println("In finally block");
}
return 0;
}
Output
In finally block
Returned value: 10
(because finally
does not return anything)
2. Return in finally
block
If a finally
block has a return
statement, it overrides any return from try
or catch
.
public int test() {
try {
return 1;
} catch (Exception e) {
return 2;
} finally {
return 3; // this overrides everything
}
}
Returned value: 3
Even though try
and catch
have return
, the finally
return takes priority.
Java 8 Questions
1. Explain the concept of lambda expressions in Java 8.
A lambda expression is a complete syntax structure introduced in Java 8 to represent an anonymous function (a method without a name).
🔹Syntax of lambda expressions
(parameters) -> expression
(parameters) -> { statements }
🔹Example
(int a, int b) -> a + b
This entire syntax is a lambda expression:
- It takes parameters (int a, int b)
- Performs an operation: a + b
- Returns the result
2. Explain the concept of anonymous classes in Java.
Anonymous classes in Java are a form of inner class without a named identifier. They are typically used for instantiating simple objects needed for a short duration, often to implement an interface or extend a class without creating a separate, named class.
The syntax of an anonymous class
new superclass_or_interface() {
// class body
};
3. What is the difference between the Comparable and Comparator interfaces?
In Java, both the Comparable and Comparator interfaces are used to define the order of objects, but they serve different purposes and are used in different contexts.
Comparable Interface
- Purpose: The Comparable interface is used to define the default natural ordering of objects of a particular class.
- Method: It requires the implementation of a single method, compareTo(Object obj).
Comparable Example
public class Employee implements Comparable<Employee> {
private int id;
public int compareTo(Employee other) {
return Integer.compare(this.id, other.id);
}
}
Comparator Interface
- Purpose: The Comparator interface is used to define an ordering, but not the natural ordering. It allows the definition of multiple different orderings for a class.
- Method: It requires the implementation of the compare(Object obj1, Object obj2) method.
Comparator Example
public class AgeComparator implements Comparator<Employee> {
public int compare(Employee e1, Employee e2) {
return Integer.compare(e1.getAge(), e2.getAge());
}
}
4. Explain the concept of method references in Java 8.
Method References are a shorthand notation of Lambda Expressions to call methods (either static or instance) directly by its name, without invoking it. They are used to make the code cleaner and more readable when a lambda expression does nothing but call an existing method.
In simple way we can define it as A method reference is a short and simple way to call a method that already exists, instead of writing a full lambda expression.
Think of it like this
Instead of writing this:
name -> System.out.println(name)
You just write this:
System.out::println
Syntax Types
Type | Example | Use When… |
Static method | ClassName::method | You are calling a static method |
Instance method | object::method | You are calling a method on object |
Any instance method | ClassName::method | Method is called on each element |
Constructor reference | ClassName::new | You are creating new objects |
5. What is the purpose of the default keyword in Java interfaces?
The default keyword in Java interfaces allows you to define methods with a body in interfaces. Before Java 8, interfaces could only have method signatures without bodies. The default method can be executed if the classes that implement the interface do not provide their own implementation.
Example of Interface with a Default Method
interface Vehicle {
void start();
// Default method with body
default void stop() {
System.out.println("Vehicle stopped.");
}
}
Class implements the interface
class Car implements Vehicle {
public void start() {
System.out.println("Car started.");
}
}
Main Method
public class Main {
public static void main(String[] args) {
Car c = new Car();
c.start(); // Car started.
c.stop(); // Vehicle stopped.
}
}
What Happened?
- Car did not implement stop()
- So, it used the default version from the Vehicle interface
6. Explain the concept of functional interfaces in Java 8. Give examples.
A Functional Interface is an interface in Java that has exactly one abstract method.
That’s it! It may have:
- One abstract method ✅ (Required)
- Any number of default or static methods ✅ (Optional)
Example of Functional interface
@FunctionalInterface
public interface Operation{
// An abstract method that takes an integer and returns an integer.
int operation(int x);
}
TestSimpleFunction.java
public class TestSimpleFunction {
public static void main(String[] args) {
// Create an instance of Operation using a lambda expression.
Operations cube = x -> x * x * x;
// Using the functional interface to perform an operation.
int result = cube.operation(5);
System.out.println("Cube of 5 is: " + result);
}
}
List of built-in functional interface in java 8.
(From java.util.function
package)
1. Predicate<T>
- Takes one input, returns a boolean.
- Used for filtering.
Predicate<String> isEmpty = str -> str.isEmpty();
2. Function<T, R>
- Takes one input, returns one output.
- Used for transformation.
Function<String, Integer> length = str -> str.length();
3. Consumer<T>
- Takes one input, returns nothing.
- Used for performing operations like printing, saving.
Consumer<String> printer = str -> System.out.println(str);
4. Supplier<T>
- Takes no input, returns an output.
- Used for lazy value generation.
Supplier<Double> random = () -> Math.random();
5. BiFunction<T, U, R>
- Takes two inputs, returns one output.
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
6. BiPredicate<T, U>
- Takes two inputs, returns boolean.
BiPredicate<String, String> isEqual = (a, b) -> a.equals(b);
7. BiConsumer<T, U>
- Takes two inputs, returns nothing.
BiConsumer<String, Integer> printer = (name, age) -> System.out.println(name + " is " + age);
8. UnaryOperator<T>
- A
Function<T, T>
— same input and output type.
UnaryOperator<Integer> square = x -> x * x;
9. BinaryOperator<T>
- A
BiFunction<T, T, T>
— two inputs, same output type.
BinaryOperator<Integer> sum = (a, b) -> a + b;
7. Explain the concept of CompletableFuture.
The CompletableFuture class in Java is part of the java.util.concurrent package, introduced in Java 8 as an enhancement to its Future API. It implements the Future and CompletionStage interfaces and offers a rich, flexible, and asynchronous programming framework.
CompletableFuture is designed to represent a future result of an asynchronous computation. This means it acts as a placeholder for the result, which will become available at a future point in time.
CompletableFuture provides methods for composing and chaining multiple stages of asynchronous tasks. This allows you to perform further operations once the initial computation is done, or even combine multiple asynchronous computations.
8. Filter Concept in Streams
The filter()
method in Java Streams is used to process elements based on a condition (predicate). It returns a new stream consisting only of elements that match the condition. It’s commonly used for filtering collections in a functional and readable way.
Example of filter()
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> even = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(even); // Output: [2, 4]
Here, filter(n -> n % 2 == 0)
filters out only even numbers from the list.
9. What is Intermediate and Terminal Operators in Streams?
Intermediate Operators
These are operations that return a Stream and are lazy, meaning they don’t process data until a terminal operation is invoked.
Examples: filter()
, map()
, sorted()
, distinct()
, limit()
.
🔹 Used for transforming or filtering data.
Terminal Operators
These operations trigger the stream processing and produce a result or a side-effect (like printing, collecting, etc.). After this, the stream is consumed and cannot be reused.
Examples: collect()
, forEach()
, reduce()
, count()
, anyMatch()
.
🔹 Used to produce a final output from the stream.
Example of Intermediate and Terminal Operators
List<String> names = Arrays.asList("Alice", "Bob", "Alex");
names.stream() // Stream created
.filter(n -> n.startsWith("A")) // Intermediate
.forEach(System.out::println); // Terminal
Output
Alice
Alex
List of Intermediate and Terminal Operators in Stream
🔁 Intermediate Operators
(These return a new Stream and are lazy.)
Operator | Description |
---|---|
filter(Predicate) | Filters elements based on a condition |
map(Function) | Transforms each element |
flatMap(Function) | Flattens nested structures |
distinct() | Removes duplicate elements |
sorted() | Sorts elements in natural order |
sorted(Comparator) | Sorts using a custom comparator |
limit(long n) | Limits the stream to first n elements |
skip(long n) | Skips first n elements |
peek(Consumer) | Performs an action for debugging |
mapToInt() , mapToDouble() , mapToLong() | Maps to primitive streams |
✅ Terminal Operators (These trigger the stream processing and produce a result.)
Operator | Description |
---|---|
forEach(Consumer) | Performs an action for each element (no result) |
collect(Collector) | Collects results into a collection or string |
toArray() | Converts stream to array |
reduce() | Reduces elements to a single value |
count() | Returns the number of elements |
min(Comparator) | Finds the minimum element |
max(Comparator) | Finds the maximum element |
anyMatch(Predicate) | Checks if any element matches condition |
allMatch(Predicate) | Checks if all elements match condition |
noneMatch(Predicate) | Checks if no element matches condition |
findFirst() | Returns the first element (if present) |
findAny() | Returns any one element (useful in parallel streams) |
10. What is stream api and why we use it.
Stream API is a feature introduced in Java 8 that allows us to process collections of data in a functional and declarative style. It helps to perform operations like filtering, mapping, sorting, and reducing on large datasets efficiently using streams.
Why Do We Use Stream API?
- Simplifies complex data processing (like filtering, transforming, grouping).
- Improves readability using method chaining (like
filter().map().collect()
). - Supports parallel processing to improve performance.
- Avoids boilerplate code like loops and if-conditions.
- Immutable and safe – doesn’t modify the original collection.
11. What is Optional in Java?
Optional
is a class in Java (java.util.Optional
), and it acts as a container object. We can also explain it as Optional
is a final class in java.util
that acts as a container object to represent a value that may or may not be present, helping avoid null pointer exceptions.
Term | Meaning in Context |
---|---|
Class | A blueprint in Java used to create objects. Optional is a final class in the java.util package. |
Container Object | Describes how the class behaves — it holds a value or not (like a box that may be empty). |
Creating Optional
Optional<String> name = Optional.of("Java"); // value present
Optional<String> empty = Optional.empty(); // no value
Optional<String> nullable = Optional.ofNullable(null); // may be null
Optional Methods
Method | Description |
---|---|
isPresent() | Returns true if value is present |
get() | Returns the value (⚠️ unsafe without check) |
orElse("default") | Returns value or default |
orElseGet(Supplier) | Lazy version of orElse |
orElseThrow() | Throws exception if value is missing |
ifPresent(Consumer) | Executes code if value is present |
map(Function) | Transforms value if present |
filter(Predicate) | Filters value based on condition |
Example:
Optional<String> name = Optional.ofNullable(getName());
name.ifPresent(n -> System.out.println(n.toUpperCase()));
12. What was the problems with java.util.Date and java.util.Calendar?
java.util.Date
and Calendar
are mutable, error-prone, and poorly designed. They lack thread safety and have confusing APIs, which is why Java 8 introduced the improved java.time
package.
Problems with java.util.Date
and Calendar
1. Mutability
- Both
Date
andCalendar
objects are mutable, meaning their state can be changed after creation. - This makes them not thread-safe and can lead to bugs in multi-threaded applications.
Date date = new Date();
date.setTime(0); // Modifies original object
2. Poor API Design
- Method names are confusing and inconsistent, e.g.:
getYear()
returns year – 1900getMonth()
is 0-based (January = 0)
- Requires a lot of manual adjustments.
3. Complex and Error-Prone
Calendar
has a complex and bloated API.- Simple operations like adding days or formatting time zones can be unnecessarily difficult.
4. Time Zone Handling is Clumsy
- Managing time zones is unintuitive and difficult to get right using
Date
andCalendar
.
5. Mix of Date and Time
Date
includes both date and time, but you can’t easily separate them.
6. Thread Safety Issues
- Neither
Date
norCalendar
is thread-safe → developers must handle synchronization manually.
Solution: Java 8 java.time
Package (JSR-310)
Introduced modern, immutable, and fluent classes like:
LocalDate
,LocalTime
,LocalDateTime
ZonedDateTime
,Instant
,Period
,Duration
- All are immutable, thread-safe, and much easier to use.
13. Benefits of java.time
(New Date/Time API in Java 8)
The java.time
API in Java 8 offers a modern, immutable, and thread-safe approach to handling dates and times with clear APIs, better time zone support, and safer calculations compared to Date
and Calendar
.
Benefits of java.time
(New Date/Time API in Java 8):
1. Immutability
- All classes like
LocalDate
,LocalTime
, andZonedDateTime
are immutable and thread-safe. - Ideal for use in multi-threaded environments.
2. Clear and Consistent API
- Easy to read and use.
- Uses well-named methods and fluent APIs, such as: javaCopyEdit
LocalDate.now().plusDays(5).getDayOfWeek();
3. No Confusing Offsets
- No more 0-based months (
Calendar
) or years offset from 1900 (Date
). - Everything is logically represented.
4. Better Time Zone Handling
- Includes classes like
ZonedDateTime
andZoneId
to clearly manage time zones.
5. Separation of Concerns
- Date and time are handled separately:
LocalDate
for date-onlyLocalTime
for time-onlyLocalDateTime
for both
6. Easy Adjustments and Calculations
- Simple and readable methods for adding/subtracting dates: javaCopyEdit
date.plusDays(7); date.minusMonths(1);
7. Safer and More Predictable
- No risk of accidental state changes like in mutable
Date
andCalendar
.
8. Support for Durations and Periods
Duration
for time-based calculationsPeriod
for date-based differences (years, months, days)
Here is an interview clearing guide to help you prepare effectively
- Review Core Concepts: Ensure you have a strong understanding of core Java concepts, including object-oriented programming, exception handling, collections, multithreading, and I/O operations.
- Data Structures and Algorithms: Refresh your knowledge of data structures like arrays, linked lists, stacks, queues, trees, graphs, and algorithms like searching, sorting, and dynamic programming.
- Java Libraries and Frameworks: Familiarize yourself with commonly used Java libraries and frameworks such as JDBC, Servlets, JSP, Spring, Spring Boot, Hibernate, and JavaFX. Understand their key features and usage.
- Design Patterns: Study commonly used design patterns in Java and understand their implementation, advantages, and use cases.
- Practice Coding: Solve coding problems related to Java on platforms like LeetCode or HackerRank. Practice implementing data structures, algorithms, and solving real-world programming challenges.
- Mock Interviews: Arrange mock interviews with friends or colleagues to simulate the interview environment. Receive feedback on your performance and work on improving weak areas.
- Stay Updated: Keep up with the latest trends and updates in the Java ecosystem. Stay informed about new Java versions, features, frameworks, and libraries.
- Prepare Questions: Be prepared to ask relevant questions to the interviewer about the company, project, or team to show your interest and engagement.
- Communication and Confidence: Practice presenting your ideas confidently and be prepared to explain your thought process during technical discussions. Pay attention to your communication skills and body language.
Remember, interview success not only depends on your technical knowledge but also on your problem-solving skills, ability to think critically, and your attitude towards learning and growth. Approach the interview with confidence, be enthusiastic, and showcase your ability to adapt and collaborate.