Java Interview Questions for Experienced, Mostly Asked in MNCs!

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 string s. Instead, it returns a new String object which is pointed to by modified.
  • The original string s remains unchanged as “Hello”, demonstrating its immutabilit.

✅ Good for constants and read-only data.

🔹 StringBuilderMutable & 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.

🔹 StringBufferMutable & 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 or age once the object is created.
  • There are no setter methods.
  • Fields are private and final.
  • 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)

TermSimple Meaning
AspectThe common thing you want to apply (e.g., logging, security, transactions)
Join PointA point in your program where extra code can be added (like before/after a method runs)
PointcutA rule that selects which methods or classes should get the aspect
AdviceThe actual code that runs (e.g., what should happen before or after the method)
WeavingThe 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 and Cat must implement makeSound().

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:

  1. Default methods (with body)
  2. 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 avoid CloneNotSupportedException.
  • The clone() method should be overridden as public 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 SignatureDescription
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 and setters.

🧠 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
ConceptInheritanceComposition/Aggregation
ExampleDog is-a AnimalCar has-a Engine
Implementationextends / implementsCreate object reference inside class
UsageReuse methods and polymorphismCode 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:

  1. If two objects are equal according to equals(), then they must have the same hashCode().
    • a.equals(b)a.hashCode() == b.hashCode()
  2. If two objects have the same hashCode(), they may or may not be equal.
    • ✅ Good for performance in hash-based collections.
  3. 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:

  1. New – When a thread is created using the Thread class but not started yet.
  2. Runnable – After calling start(), the thread is ready to run and waiting for CPU time.
  3. Running – The thread is currently executing.
  4. Blocked/Waiting – The thread is paused, either waiting for a resource or waiting for another thread’s action.
  5. 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:

  1. By extending the Thread class.
  2. 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.

AspectThread ClassRunnable Interface
TypeIt’s a class (extends java.lang.Thread)It’s a functional interface (has a single method run())
InheritanceCannot extend another class if you extend Thread (Java supports only single inheritance)Allows extending another class as it only implements an interface
Memory UsageEach Thread object creates a separate memory stackLess memory as multiple threads can share a single Runnable object
Object SharingEach thread has its own objectSame Runnable object can be shared across multiple threads
Method AvailabilityInherits 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 UseSuitable for small, simple applications with limited inheritance needsPreferred for large applications and when working with thread pools or shared resources
Thread CreationSubclass Thread and override run() methodImplement Runnable and pass it to a Thread object
Java 8 SupportNot functional interface — cannot use lambda expressionsIs 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.

AspectCallable InterfaceRunnable Interface
Packagejava.util.concurrentjava.lang
MethodHas call() methodHas run() method
Return TypeReturns a result (V) from call()run() returns void (no result)
Exception HandlingCan throw checked exceptionsCannot throw checked exceptions (only unchecked)
Thread CreationCannot directly pass to Thread constructorCan be passed directly to Thread constructor
Use With ExecutorServiceUsed with ExecutorService.submit() to get a Future objectUsed with Executor.execute() for fire-and-forget tasks
Result RetrievalReturns result via Future.get()No result returned
Bulk Execution SupportSupports bulk execution using invokeAll()Not supported for bulk execution via ExecutorService
Java VersionIntroduced in Java 5Available 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:

  1. Create a task using Runnable or Callable.
  2. Create an ExecutorService using Executors class.
  3. Submit the task to ExecutorService.
  4. 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:

  1. Create a task – using Runnable or Callable.
  2. Create an ExecutorService – using methods like:
    • Executors.newFixedThreadPool(int n)
    • Executors.newCachedThreadPool()
    • Executors.newSingleThreadExecutor()
  3. Submit the task – using submit() or execute() method.
  4. 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.

AspectChecked ExceptionUnchecked Exception
DefinitionExceptions that are checked at compile-time.Exceptions that occur at runtime and are not checked at compile-time.
Class HierarchySubclasses of Exception (excluding RuntimeException)Subclasses of RuntimeException
Compile-Time CheckingYes – compiler forces handling using try-catch or throws.No – compiler does not force handling.
When It OccursDuring compile-timeDuring runtime
Handling RequirementMust be handled or declared in method signature.Handling is optional.
ExamplesIOException, SQLException, ClassNotFoundExceptionNullPointerException, ArrayIndexOutOfBoundsException, ArithmeticException
Used ForSituations that are outside the program’s control (e.g., file not found)Programming errors or logic mistakes
Program Behavior if Not HandledCompile-time errorProgram 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

AspectErrorException
RepresentsSerious system-level problemsProblems in program logic
Caused byJVM (hardware failure, memory, etc.)Program or external issues (e.g., I/O)
Can be caught?Not usuallyYes, using try-catch
Should be handled?No – let JVM handle itYes – developer should handle it
ExamplesOutOfMemoryError, StackOverflowErrorNullPointerException, 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.

  1. printStackTrace()
  2. toString()
  3. 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 purposesIt must be static, final, and of type long.

🚨 When Should You Change the serialVersionUID?

Change TypeNeed 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 with throws.
  • 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

TypeExampleUse When…
Static methodClassName::methodYou are calling a static method
Instance methodobject::methodYou are calling a method on object
Any instance methodClassName::methodMethod is called on each element
Constructor referenceClassName::newYou 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.)

OperatorDescription
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.)

OperatorDescription
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.

TermMeaning in Context
ClassA blueprint in Java used to create objects. Optional is a final class in the java.util package.
Container ObjectDescribes 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

MethodDescription
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 and Calendar 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 – 1900
    • getMonth() 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 and Calendar.

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 nor Calendar 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, and ZonedDateTime 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: javaCopyEditLocalDate.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 and ZoneId to clearly manage time zones.

5. Separation of Concerns

  • Date and time are handled separately:
    • LocalDate for date-only
    • LocalTime for time-only
    • LocalDateTime for both

6. Easy Adjustments and Calculations

  • Simple and readable methods for adding/subtracting dates: javaCopyEditdate.plusDays(7); date.minusMonths(1);

7. Safer and More Predictable

  • No risk of accidental state changes like in mutable Date and Calendar.

8. Support for Durations and Periods

  • Duration for time-based calculations
  • Period 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.

What did you think?

Similar Reads

Hi, Welcome back!
Forgot Password?
Don't have an account?  Register Now