Java Interview Questions and Answers for 2 to 3 years Experienced: Top 50

Java-Interview-questions-and-answers

Preparing for Java interview questions for 2 to 3 years of experience requires more than recalling definitions. At this stage, interviewers may ask you to explain why a particular collection, design approach or concurrency mechanism was used, identify limitations and describe how you handled technical problems in your projects.

This guide covers 50 Java interview questions and answers across object-oriented programming, the Collections Framework, strings, exception handling, multithreading, JVM fundamentals and Java 8 features. The answers include comparisons, code examples and practical considerations relevant to developers with approximately two to three years of experience.

Java Interview Preparation: Key Highlights

  • Core Java: Revise inheritance, polymorphism, interfaces, method overriding, immutability and exception handling.
  • Collections: Understand the differences between lists, sets and maps, including their ordering, equality and thread-safety characteristics.
  • Concurrency: Be prepared to explain volatile, synchronisation, thread coordination and common race conditions.
  • Practical coding: Review short Java programs and explain both their output and the reasoning behind your implementation.
  • Project discussions: Prepare real examples of debugging, collection selection, exception handling and performance trade-offs from your development experience.

Top 50 Java Interview Questions for 2 to 3 Years of Experience

The following questions move from core language concepts to collections, concurrency and application-level scenarios. Use the answers to revise technical fundamentals, then connect them to projects you have actually worked on. Examples use standard Java syntax unless a specific Java version is mentioned.

1. What Is the Difference Between Method Overloading and Method Overriding in Java?

Method overloading means declaring methods with the same name but different parameter lists. The compiler selects an applicable overload using the argument types and other language rules. Overloading can occur within a class or across inherited methods.

Method overriding occurs when a subclass provides an implementation of an inherited instance method with an override-compatible signature and return type. For an overridden instance method, runtime dispatch determines which implementation executes based on the actual object.

Method OverloadingMethod Overriding
Same method name with different parameter listsOverride-compatible instance method in a subclass
Overload selection occurs at compile timeOverridden implementation is selected at runtime
Does not require inheritanceRequires inheritance or interface implementation
Return type alone cannot distinguish overloadsReturn type must meet overriding rules, including covariant returns
Static methods can be overloadedStatic methods are hidden, not overridden

Java example:

class Calculator  

    double add(double a, double b)  
}

class Animal  
}

class Dog extends Animal  
}

The two add() methods demonstrate overloading, while Dog.speak() demonstrates overriding. If Animal animal = new Dog(); is followed by animal.speak();, the output is Bark because the object is a Dog.

2. What Is a ClassLoader in Java?

A ClassLoader is responsible for locating and defining classes for the Java runtime. Java can load class definitions when needed, rather than requiring every application class to be loaded at startup.

The standard Java runtime includes these built-in class loaders:

  • Bootstrap ClassLoader: The JVM’s built-in loader, responsible for fundamental Java runtime classes, including core classes from java.base.
  • Platform ClassLoader: Loads platform classes and modules assigned to it by the runtime.
  • Application or System ClassLoader: Commonly loads application classes and dependencies from the application classpath and applicable module path.

Java class loaders normally use a parent-delegation model: before attempting to find a class themselves, they delegate loading to their parent. Custom class loaders may implement different arrangements for specific requirements.

For example, application servers and plugin systems may use separate class loaders to isolate dependencies or load components dynamically. Class identity in Java depends on both the class’s binary name and its defining class loader.

Version note: References to the Extension ClassLoader and rt.jar describe older Java environments. Java 9 introduced the module system and a revised built-in class-loading arrangement. The Oracle ClassLoader API documentation explains the modern structure.

3. What Is Inheritance in Java?

Inheritance allows a class to extend another class, gaining access to its inheritable methods and fields. The parent is called the superclass and the extending class is the subclass.

Java supports single, multilevel and hierarchical class inheritance. A class cannot extend multiple classes, although it can implement multiple interfaces.

  • Single inheritance: One class extends another class.
  • Multilevel inheritance: A subclass is extended by another subclass.
  • Hierarchical inheritance: Multiple subclasses extend the same superclass.
  • Multiple interface implementation: One class implements several interfaces, subject to Java’s method-conflict rules.

Practical consideration: Inheritance is appropriate when a genuine “is-a” relationship exists. Composition is often preferable when a class simply needs to use another component’s functionality, because it can reduce coupling between implementations.

4. Why Can’t We Override Static Methods in Java?

Static methods belong to a class rather than an object instance. They are resolved according to the class or reference type used in the call, so they do not participate in runtime overriding.

When a subclass declares a static method with the same signature as an accessible static method in its superclass, the subclass method hides the superclass method.

class Parent  
}

class Child extends Parent  
}

public class Main  
}

The first call prints Parent because the reference type is Parent. The second call explicitly invokes Child.show(). This is method hiding, not runtime polymorphism.

5. What Is Dynamic Method Dispatch in Java?

Dynamic method dispatch is the process Java uses to select an overridden instance method at runtime according to the actual object type.

class Vehicle  
}

class Car extends Vehicle  
}

Vehicle vehicle = new Car();
vehicle.move(); // Car moves

Although the reference is declared as Vehicle, the object is a Car, so the overridden method in Car executes.

Dynamic dispatch supports polymorphic designs in which application code works with a superclass or interface while different implementations provide specialised behaviour. Static, private and final methods do not participate in overriding in the same way.

6. What Is the Classpath in Java?

The Java classpath identifies directories and JAR files where Java tools and class loaders can find application classes and dependencies.

Classpath entries can be supplied through the -cp or -classpath command-line option or through the CLASSPATH environment variable. Build tools such as Maven and Gradle commonly manage the required dependencies when compiling and running applications.

Example: On systems using a colon as the path separator, a command might look like this:

java -cp "app.jar:libs/*" com.example.Main

On Windows, classpath entries are separated with a semicolon rather than a colon. Modern Java also has a module path for named modules, which is distinct from the traditional classpath.

7. What Is the volatile Keyword in Java?

The volatile keyword is used for fields shared between threads when visibility and ordering guarantees are required. Under the Java Memory Model, a write to a volatile field happens-before a subsequent read of that field.

For example, a volatile status flag can communicate that a background task should stop:

class Worker implements Runnable  

    @Override
    public void run()  
    }
}

When another thread calls stop(), the worker can observe the updated flag. The example illustrates visibility; a real application should also consider interruption and avoid unnecessary busy-waiting.

Important limitation: volatile does not make compound operations such as count++ atomic. Use appropriate synchronisation or classes such as AtomicInteger when several threads must update shared counters safely.

8. When Does a finally Block Not Execute in Java?

A finally block normally executes when control leaves the associated try statement, whether execution completes normally, returns from a method or throws an exception.

However, execution is not guaranteed if the JVM or process terminates before the block can run. Calling System.exit(), forcibly terminating the process or experiencing a JVM crash can prevent pending finally blocks from executing.

A thread that becomes permanently blocked before leaving the try block may also never reach the associated finally block.

Interview tip: Distinguish a method returning from a try block, where finally normally executes, from JVM termination, where it may not. For resources such as files and database connections, use try-with-resources when appropriate.

9. What Is the Difference Between StringBuffer and StringBuilder in Java?

Both StringBuffer and StringBuilder are mutable character-sequence classes. They allow applications to append, insert or modify characters without creating a new String object for every operation.

StringBufferStringBuilder
Provides synchronised methodsDoes not synchronise its methods
Supports thread-safe individual operationsRequires external coordination when shared mutable access is possible
Can introduce synchronisation overheadUsually preferable for local, single-threaded string building
Legacy class available since early Java versionsIntroduced in Java 5

Example:

StringBuilder builder = new StringBuilder();
builder.append("Java");
builder.append(" Interview");

System.out.println(builder.toString());
// Java Interview

For assembling text within one method or thread, StringBuilder is generally the simpler choice. Synchronised methods in StringBuffer do not automatically make a sequence of several operations atomic.

10. What Is the Difference Between Vector and ArrayList in Java?

Vector and ArrayList are resizable-array implementations of the List interface. Both preserve element order, allow duplicates and support indexed access.

VectorArrayList
Legacy collection implementationWidely used modern List implementation
Synchronises many public operationsNot synchronised by default
Individual method calls use synchronisationRequires a suitable strategy for concurrent modification
May incur additional synchronisation overheadGenerally preferred when built-in synchronisation is unnecessary

When should you use ArrayList? For a list managed by one thread or protected by another appropriate concurrency mechanism, ArrayList is generally a good default.

When several threads need to access or modify a collection, choose a design that matches the operations being performed. Depending on the workload, alternatives may include synchronised wrappers, CopyOnWriteArrayList or explicit locking.

11. Why Is String Immutable in Java?

String immutability means that the sequence of characters represented by a String object cannot be changed after the object is created. Operations such as concatenation or replacement return another string value rather than modifying the original object.

String message = "Java";
message.concat(" Interview");

System.out.println(message); // Java

message = message.concat(" Interview");
System.out.println(message); // Java Interview

In the first operation, the result of concat() is discarded. In the second, the variable is reassigned to reference the resulting string. Neither operation changes the original string object.

Immutability provides several practical benefits:

  • Safe sharing: Different parts of an application can reference the same string without the risk that one changes its contents.
  • String pooling: Equal string literals can share interned string instances safely because their values do not change.
  • Reliable map keys: Stable string values support consistent hashCode() and equals() behaviour when strings are used as hash-based collection keys.
  • Predictable API behaviour: Strings used for identifiers, class names and other parameters cannot have their character contents modified through another reference.

Technical distinction: Immutability does not mean a String variable cannot be reassigned. It means the existing object cannot be modified. Oracle’s String API documentation describes this behaviour.

12. What Is the Difference Between HashMap and HashSet in Java?

HashMap stores key-value mappings, while HashSet stores a set of unique elements. Both use hashing, but they solve different problems.

HashMapHashSet
Implements the Map interfaceImplements the Set interface
Stores keys associated with valuesStores unique elements
Each key occurs at most once; values may repeatDuplicate elements are not retained
Permits one null key and multiple null valuesPermits a null element
Does not guarantee iteration orderDoes not guarantee iteration order
Not synchronised by defaultNot synchronised by default

Correct Java example:

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

Map<Integer, String> students = new HashMap<>();

students.put(1, "Anushka");
students.put(2, "Akansha");
students.put(1, "Anjana");

System.out.println(students.get(1)); // Anjana

Set<String> names = new HashSet<>();

names.add("Anushka");
names.add("Akansha");
names.add("Anushka");

System.out.println(names.size()); // 2

In the HashMap, adding a value for the existing key 1 replaces the previous mapping. In the HashSet, adding "Anushka" twice does not increase the number of elements.

Interview scenario: Use HashMap when you need to look up a value using a unique key, such as an employee ID. Use HashSet when you need to track unique values, such as distinct email addresses, without storing an additional mapped value.

The behaviour of hash-based collections also depends on consistent equality and hashing. Custom key or element classes should implement equals() and hashCode() correctly, and fields involved in equality should not be mutated in ways that invalidate their use in a hash-based collection.

For the formal API contract, see Oracle’s HashMap documentation.

13. What Is the Producer-Consumer Problem in Java?

The producer-consumer problem is a concurrency scenario in which one or more producer threads generate data while consumer threads process it. The threads communicate through a shared buffer or queue.

Two situations must be handled correctly: a producer should wait or apply an appropriate backpressure strategy when a bounded queue is full, and a consumer should wait when there is no item available to process.

Java provides the BlockingQueue interface for coordinating this behaviour. Its put() method waits for space when used with a full bounded blocking queue, while take() waits until an element becomes available.

Example using ArrayBlockingQueue:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class ProducerConsumerExample  
            } catch (InterruptedException e)  
        });

        Thread consumer = new Thread(() ->  
            } catch (InterruptedException e)  
        });

        producer.start();
        consumer.start();

        producer.join();
        consumer.join();
    }
}

This example produces and consumes five integers. The bounded queue coordinates the threads without requiring the application to implement low-level wait() and notify() logic. A real application also needs an appropriate shutdown mechanism, error handling and a strategy for consumers to know when production has finished.

Advantages: Producers and consumers can operate at different speeds, their implementations remain loosely coupled, and a bounded queue can help control the amount of work waiting to be processed.

Interview consideration: Explain why queue capacity matters. An unbounded queue may allow pending work to accumulate, while a bounded queue can apply backpressure when consumers cannot keep up.

14. How Do You Create an Immutable Class in Java?

An immutable class represents objects whose observable state cannot change after construction. The class must prevent callers from modifying its internal state, including through references to mutable objects.

Common techniques include:

  • Prevent subclass behaviour from breaking immutability, commonly by declaring the class final.
  • Declare instance fields private and, where appropriate, final.
  • Initialise the object’s state during construction and do not provide mutating methods.
  • Make defensive copies of mutable input objects when necessary.
  • Do not expose internal mutable objects directly through getters.

Example with a mutable List input:

import java.util.List;

public final class Employee  

    public String getName()  

    public List<String> getSkills()  
}

List.copyOf(), available from Java 10, creates an unmodifiable list that does not reflect later structural changes to the supplied list. Because the elements in this example are immutable strings, callers cannot change the employee’s skills through mutable element objects.

Important: A final reference prevents reassignment of the field, but it does not automatically make the referenced object immutable. If a field contains mutable elements, an unmodifiable collection alone may not be sufficient; element-level defensive copying may also be required.

15. What Is the Difference Between Fail-Fast and Fail-Safe Iterators in Java?

The terms fail-fast and fail-safe are frequently used in Java interviews, but they require careful explanation. Java’s Collections Framework documentation uses more precise descriptions such as snapshot and weakly consistent iteration for several concurrent collections.

A fail-fast iterator may throw ConcurrentModificationException if a collection is structurally modified outside the iterator’s permitted modification operations during traversal. This detection is generally best-effort and must not be relied upon for thread safety.

For example, iterators from ordinary ArrayList and HashMap are typically fail-fast.

Other collections provide different iteration semantics:

Iterator BehaviourExampleWhat Happens During Modification?
Fail-fastArrayListMay throw ConcurrentModificationException after an unsupported structural modification; detection is best-effort
SnapshotCopyOnWriteArrayListTraverses a snapshot from iterator creation and does not reflect later changes
Weakly consistentConcurrentHashMapCan tolerate concurrent updates and may reflect some modifications made during traversal

Example of snapshot iteration:

import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;

CopyOnWriteArrayList<String> names =
        new CopyOnWriteArrayList<>();

names.add("A");
names.add("B");

Iterator<String> iterator = names.iterator();

names.add("C");

while (iterator.hasNext())  

// Output:
// A
// B

The iterator does not include C because the snapshot was created before that element was added.

Interview tip: Do not state that every iterator described as fail-safe works on a cloned collection. Snapshot and weakly consistent iterators have different contracts. Oracle’s CopyOnWriteArrayList documentation describes snapshot iteration explicitly.

Illustration comparing fail-fast and fail-safe iteration terminology in Java collections

16. What Is an Interface in Java, and What Features Does It Support?

A Java interface defines a reference type that can declare abstract methods, constants and several kinds of methods with implementations. Interfaces help separate an API contract from its implementations and support polymorphism.

Important features include:

  • Abstract methods: Declare behaviour that an implementing concrete class normally needs to provide unless an implementation is inherited.
  • Default methods: Provide inherited method implementations, introduced in Java 8.
  • Static methods: Belong to the interface itself and can be called using the interface name.
  • Private methods: Supported since Java 9, allowing code reuse inside interface method implementations.
  • Constants: Interface fields are implicitly public static final.
  • Multiple interface implementation: A class can implement more than one interface, subject to the language’s inheritance and conflict rules.

Interfaces do not have instance fields or constructors. They may also declare nested types and can extend other interfaces.

Example:

interface PaymentProcessor  
}

class CardPayment implements PaymentProcessor  
}

Here, CardPayment implements the abstract process() method and inherits the default printReceipt() implementation.

Practical application: In a payment-processing system, multiple implementations can follow the same interface while using different underlying services. This allows calling code to depend on a common contract rather than on one concrete provider.

Diagram illustrating Java interfaces, multiple interface implementation and abstract, default and static methods

17. What Are the Five SOLID Principles in Java?

SOLID refers to five object-oriented design principles intended to improve maintainability, flexibility and separation of responsibilities. They are design guidelines rather than Java-specific language features or rules that must always be applied mechanically.

PrincipleMeaningPractical Java Example
Single Responsibility Principle (SRP)A module or class should have one primary reason to changeSeparate invoice calculations from invoice email delivery
Open/Closed Principle (OCP)Software components should support extension without unnecessary modification of existing codeAdd another payment implementation through a common interface
Liskov Substitution Principle (LSP)Subtypes should preserve the behavioural expectations of their base typesA replacement implementation must honour the interface’s contractual behaviour
Interface Segregation Principle (ISP)Clients should not depend on operations they do not useUse focused interfaces instead of a large unrelated API
Dependency Inversion Principle (DIP)High-level policy and implementation details should depend on suitable abstractionsInject a PaymentProcessor dependency rather than constructing a specific implementation internally

Interview scenario: Suppose an order service calculates totals, sends emails and writes directly to a particular database. You could explain how separating responsibilities and depending on interfaces may make the service easier to test and change.

Also explain the trade-off: introducing too many abstractions can make a small application unnecessarily complicated. Good design depends on the problem, change requirements and maintenance needs.

Infographic showing the five SOLID software design principles: single responsibility, open-closed, Liskov substitution, interface segregation and dependency inversion

18. What Is a Package in Java?

A Java package groups related classes, interfaces and other types within a namespace. Packages help organise source code, reduce naming conflicts and support access control.

Java provides built-in packages such as java.lang, java.util and java.time. Developers also create packages for application features and components.

Example:

package com.example.orders;

public class OrderService  
}

In this example, OrderService belongs to the com.example.orders package. Other classes can import it when access rules permit.

Important distinction: Packages organise types, while Java modules introduced in Java 9 add mechanisms for declaring dependencies and controlling which packages are exported or opened.

19. What Is the Difference Between List and Set in Java?

Both List and Set are interfaces in the Java Collections Framework, but they have different contracts for duplicates and element organisation.

ListSet
Ordered collection with positional accessCollection containing no duplicate elements
Allows duplicate elementsDoes not allow duplicate elements under its equality rules
Supports positional operations such as get(index)Does not provide general positional access by index
Examples: ArrayList, LinkedListExamples: HashSet, LinkedHashSet, TreeSet

A Set is not necessarily unordered. HashSet does not guarantee iteration order, LinkedHashSet maintains a defined encounter order, and TreeSet sorts elements according to their natural ordering or a supplied comparator.

Practical example: Use a List to preserve an ordered sequence of transactions where duplicates are meaningful. Use a Set to track distinct identifiers when duplicates should be excluded.

20. What Are Generics in Java?

Generics allow classes, interfaces and methods to work with type parameters. They improve compile-time type checking and make reusable APIs more expressive.

For example, List<String> communicates that the list is intended to hold strings, and the compiler rejects attempts to add an incompatible value through a correctly typed reference.

import java.util.ArrayList;
import java.util.List;

List<String> names = new ArrayList<>();

names.add("Anushka");
names.add("Akansha");

// names.add(100); // Compile-time error

String firstName = names.get(0);
System.out.println(firstName);

Java generics use type erasure: most generic type arguments are not retained as distinct runtime class identities. For example, List<String> and List<Integer> do not represent two different runtime implementations of ArrayList.

Interview consideration: Be prepared to explain why List<Integer> cannot be assigned to List<Number>. Java generic types are ordinarily invariant. Wildcards such as ? extends Number and ? super Integer can express appropriate relationships for reading and writing values.

21. What Does the synchronized Keyword Do in Java?

The synchronized keyword provides mutual exclusion using an object’s monitor lock. A thread must acquire the relevant monitor before entering a synchronised instance method or block protected by that monitor.

Synchronisation also establishes memory visibility and ordering guarantees through monitor release and subsequent acquisition of the same lock.

Example of a synchronised counter:

class Counter  

    public synchronized int getCount()  
}

When multiple threads use the same Counter instance and access its state through these methods, the monitor protects the increment operation and establishes the required visibility.

Important distinction: Synchronisation only provides the intended protection when every relevant operation follows a consistent locking strategy. It does not automatically protect unrelated access to the same variable.

22. What Are Lambda Expressions in Java?

Lambda expressions, introduced in Java 8, provide a concise way to implement the single abstract method of a functional interface. They are commonly used with collection operations, streams and callback-style APIs.

Example:

import java.util.function.Predicate;

Predicate<String> isLongName =
        name -> name.length() > 5;

System.out.println(isLongName.test("Anushka")); // true
System.out.println(isLongName.test("Ravi"));    // false

The lambda supplies the implementation of the Predicate.test() method. Its target type is the functional interface Predicate<String>.

In interviews, explain both how a lambda works and when it improves readability. A complex lambda containing extensive branching may be clearer as a named method.

23. What Is a Functional Interface in Java?

A functional interface has exactly one abstract method for the purpose of functional-interface compatibility. Abstract methods corresponding to public methods of Object are not counted in the usual way.

Functional interfaces can also contain default, static and, in supported Java versions, private methods. The @FunctionalInterface annotation is optional but asks the compiler to verify that the interface meets the requirements.

@FunctionalInterface
interface Calculator  

Calculator addition = (a, b) -> a + b;

System.out.println(addition.calculate(4, 5)); // 9

Common built-in functional interfaces include Predicate, Function, Consumer, Supplier and Runnable.

Practical distinction: A lambda expression needs a compatible target type. It is not an independent named method declaration.

24. What Is the Difference Between throw and throws in Java?

The throw statement explicitly throws an exception object, while the throws clause declares exception types that a method or constructor can propagate.

throwthrows
Used within executable codeAppears in a method or constructor declaration
Throws a particular exception objectLists exception types that may be propagated
Example: throw new IllegalArgumentException();Example: void read() throws IOException

Example:

import java.io.IOException;

class FileService  
}

The method declaration includes throws IOException, while the method body contains the throw statement that creates and throws an exception.

Checked exceptions generally need to be caught or declared by callers. Unchecked exceptions, such as subclasses of RuntimeException, do not carry the same compile-time catch-or-declare requirement.

25. What Is the Map Interface in Java?

The Map interface represents a collection of key-value mappings. Each key maps to at most one value, so adding a new value for an existing key replaces the previous mapping.

Common implementations include:

  • HashMap: Does not guarantee iteration order and generally provides expected constant-time basic operations with suitably distributed hashes.
  • LinkedHashMap: Maintains a defined encounter order, normally insertion order, with an option for access ordering.
  • TreeMap: Keeps keys sorted by their natural ordering or a supplied comparator.
  • ConcurrentHashMap: Supports concurrent retrievals and updates without requiring one global lock for ordinary operations.

Practical scenario: A service may use a map to associate customer IDs with account information. The appropriate implementation depends on whether ordering, concurrency or other specific behaviour is needed.

Remember: Map is part of the Java Collections Framework but does not extend the Collection interface.

26. What Is the Difference Between HashMap and Hashtable in Java?

HashMap and Hashtable both store key-value mappings, but they differ in synchronisation, null handling and historical usage.

HashMapHashtable
Not synchronised by defaultSynchronises many public methods
Permits one null key and null valuesDoes not permit null keys or values
Widely used when external synchronisation is unnecessaryLegacy collection class
Compound operations require suitable coordination when sharedSynchronised methods do not automatically make sequences of operations atomic

When is ConcurrentHashMap preferable? In applications with multiple threads that need frequent concurrent access, ConcurrentHashMap is often more suitable than using a legacy Hashtable, depending on the required operations.

Unlike HashMap, ConcurrentHashMap does not permit null keys or null values. Its concurrency guarantees also differ from those of ordinary collections and should be understood before use.

27. What Is a Default Method in a Java Interface?

A default method is an interface instance method declared with the default keyword and supplied with an implementation. Default methods were introduced in Java 8 to allow interfaces to evolve without necessarily requiring every existing implementing class to implement a new method.

interface Notification  
}

class EmailNotification implements Notification  
}

The EmailNotification class implements the abstract send() method and inherits the default log() implementation.

If a class inherits conflicting default methods from multiple interfaces, it may need to override the method and resolve the conflict explicitly. Class methods also take precedence over conflicting interface defaults under Java’s inheritance rules.

Interview tip: Be able to explain the difference between abstract, default, static and private interface methods. Also explain why adding a default method can help maintain source compatibility, while acknowledging that interface evolution still requires attention to possible method conflicts.

28. What Are the Types of Nested Classes in Java?

A nested class is a class declared within another class or interface. Java supports static nested classes and several forms of inner classes. Inner classes are nested classes that are not explicitly or implicitly static.

TypeCharacteristics
Static nested classDeclared with static; does not require an instance of the enclosing class
Member inner classNon-static member class associated with an enclosing object
Local classDeclared within a block, such as a method body
Anonymous classDeclared and instantiated in an expression without an explicit class name

Example:

class Outer  
    }

    class Inner  
    }
}

The static nested class can be instantiated as new Outer.Nested(). The non-static inner class requires an enclosing instance, such as Outer outer = new Outer(); followed by outer.new Inner().

Interview consideration: Explain how a non-static inner class has access to members of its enclosing instance, whereas a static nested class does not have an implicit reference to an enclosing object.

29. What Are Method References in Java?

Method references, introduced in Java 8, provide a concise way to refer to an existing method or constructor when a compatible functional interface is expected. They can replace certain lambda expressions without changing the intended operation.

Method Reference TypeSyntax
Static methodClassName::staticMethod
Instance method of a particular objectobject::instanceMethod
Instance method of an arbitrary object of a particular typeClassName::instanceMethod
ConstructorClassName::new

Example:

import java.util.Arrays;
import java.util.List;

List<String> names = Arrays.asList("Ravi", "Ananya", "Priya");

names.forEach(System.out::println);

Here, System.out::println is a method reference compatible with the Consumer expected by forEach(). It is equivalent in purpose to name -> System.out.println(name).

Method references are useful when the referenced operation is already expressed clearly by a named method. A lambda may be more appropriate when additional logic or argument transformation is needed.

30. What Are the Main Features Introduced in Java 8?

Java 8 introduced language and library features that remain important in modern Java development and technical interviews.

  • Lambda expressions: Provide concise implementations of functional interfaces.
  • Stream API: Supports declarative operations such as filtering, mapping and aggregation.
  • Default interface methods: Allow interfaces to provide inherited method implementations.
  • Method references: Refer to existing methods or constructors using the :: operator.
  • Optional: Represents a value that may be present or absent.
  • Modern Date and Time API: Introduced types such as LocalDate, Instant and ZonedDateTime.

Example using a stream:

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

List<String> names =
        Arrays.asList("Ravi", "Ananya", "Priya");

List<String> result = names.stream()
        .filter(name -> name.length() > 4)
        .collect(Collectors.toList());

System.out.println(result); // [Ananya, Priya]

Interview tip: Be prepared to explain intermediate and terminal stream operations, the purpose of functional interfaces and situations where an ordinary loop is clearer than a stream pipeline.

31. What Is the try-with-resources Statement in Java?

The try-with-resources statement automatically closes resources that implement AutoCloseable. Introduced in Java 7, it is commonly used with files, streams and database resources.

Resources declared in the statement are closed in reverse order of their initialisation, including when the code inside the try block throws an exception.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

class FileExample  
    }
}

The reader is automatically closed when the statement completes. If both the main operation and resource closing throw exceptions, the closing exception may be recorded as a suppressed exception rather than replacing the primary exception.

Try-with-resources is usually preferable to manually closing resources in a finally block because it makes ownership and cleanup behaviour clearer.

32. What Is the Difference Between Comparable and Comparator in Java?

Comparable defines a type’s natural ordering through the compareTo() method. Comparator defines an external comparison strategy through compare(), allowing different ordering rules for the same type.

ComparableComparator
Implemented by the class being comparedCan be supplied separately from the class
Defines natural ordering using compareTo()Defines an ordering using compare()
Typically provides one natural orderSupports multiple ordering strategies

Example:

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

List<String> names =
        Arrays.asList("Priya", "Al", "Ananya");

names.sort(Comparator.comparingInt(String::length));

System.out.println(names); // [Al, Priya, Ananya]

The comparator sorts strings by length rather than their natural alphabetical order. In production code, consider how ties should be handled and use a secondary comparator when deterministic tie-breaking matters.

33. What Is the instanceof Operator in Java?

The instanceof operator checks whether a reference refers to an object compatible with a specified type. It returns false for a null reference.

Java also supports pattern matching for instanceof, finalised in Java 16, which combines type testing with a pattern variable.

Object value = "Java Interview";

if (value instanceof String text)  

Inside the applicable scope, text is treated as a String without requiring an explicit cast.

Version note: The pattern-matching syntax above requires Java 16 or later. For Java 8, use a conventional instanceof test followed by an explicit cast.

34. What Is the super Keyword in Java?

The super keyword refers to the immediate superclass context from a subclass. It is commonly used to call a superclass constructor, access a hidden superclass field or invoke an overridden superclass method.

class Employee  

    void display()  
}

class Developer extends Employee  

    @Override
    void display()  
}

Here, super(name) invokes the superclass constructor, while super.display() invokes the superclass implementation of display().

In traditional Java constructor rules, an explicit superclass constructor invocation must appear at the beginning of the constructor body. Later Java versions have expanded certain constructor rules, so the exact behaviour should be considered in the context of the Java version being used.

35. What Is the finalize() Method in Java, and Should It Be Used?

The finalize() method was historically intended to allow cleanup work before an object was reclaimed. However, Java never guaranteed that finalisation would occur promptly or even at all.

Finalisation is deprecated for removal. It creates reliability, performance and security concerns and should not be used as the normal mechanism for releasing files, network connections or other resources.

Prefer explicit cleanup through AutoCloseable, close() and try-with-resources. Specialised cleanup mechanisms such as Cleaner may be appropriate for particular cases, but they do not replace predictable resource ownership.

Oracle’s Object API documentation identifies finalize() as deprecated for removal and explains the alternatives.

36. What Are the Main Interfaces in the Java Collections Framework?

The Java Collections Framework provides interfaces and implementations for storing and manipulating groups of objects.

InterfaceMain PurposeCommon Implementations
ListOrdered collections allowing duplicatesArrayList, LinkedList
SetCollections without duplicate elementsHashSet, TreeSet
QueueHolding elements before processingArrayDeque, PriorityQueue
DequeInsertion and removal at both endsArrayDeque, LinkedList
MapAssociating keys with valuesHashMap, TreeMap

Collection is the root interface for types such as List, Set and Queue. Although Map belongs to the Collections Framework, it does not extend Collection.

Interview scenario: Choose a collection according to the required behaviour. Use a list when positional ordering matters, a set for uniqueness, a map for key-based lookup and a queue for task processing.

37. What Is a Thread in Java?

A thread is an execution path within a Java application. Multiple threads can execute tasks concurrently, allowing applications to handle overlapping work such as network requests, background processing and scheduled operations.

Modern Java supports both traditional platform threads and virtual threads. Virtual threads became a permanent feature in Java 21 and are particularly useful for many concurrent tasks that spend significant time waiting for blocking I/O.

Threads introduce coordination requirements when they share mutable state. Concurrency does not automatically improve throughput: contention, scheduling and blocking can still affect performance.

Interview tip: Be able to distinguish concurrency from parallelism. Concurrent tasks can make progress over overlapping periods, while parallel execution means work is executing simultaneously on multiple processing resources.

38. How Do You Create and Start a Thread in Java?

Traditional approaches include extending Thread or implementing Runnable. In many applications, implementing Runnable or submitting tasks to an executor provides better separation between the task and its execution mechanism.

Example using Runnable:

public class ThreadExample  
}

start() arranges for a new thread to execute the task. Calling run() directly would execute the method on the calling thread rather than starting a separate thread.

Modern Java example: Java 21 and later support Thread.startVirtualThread(task) for starting a virtual thread. The choice between platform threads, virtual threads and executors depends on workload and lifecycle requirements.

39. What Is a Deadlock in Java?

A deadlock occurs when threads are blocked in a cycle of resource dependencies and none can make progress.

For example, Thread A holds Lock 1 and waits for Lock 2, while Thread B holds Lock 2 and waits for Lock 1. Both remain blocked because neither can acquire the lock required to continue.

Common prevention and mitigation strategies include:

  • Consistent lock ordering: Acquire multiple locks in the same predefined order.
  • Smaller critical sections: Avoid holding locks longer than necessary.
  • Timed lock acquisition: Consider mechanisms such as ReentrantLock.tryLock() when suitable.
  • Diagnostics: Inspect thread dumps and lock ownership to identify blocked threads and dependency cycles.

Practical interview scenario: Describe a situation in which two services acquire shared locks in different orders. Explain how standardising lock acquisition order can prevent a circular wait.

40. What Is the clone() Method in Java?

The clone() method, inherited from Object, provides a mechanism for copying objects. The conventional implementation of Object.clone() performs a shallow copy, meaning reference fields in the copy generally refer to the same objects as the original.

Classes using the conventional cloning mechanism normally implement Cloneable. Otherwise, calling Object.clone() typically results in CloneNotSupportedException.

Example of a shallow-copy limitation: If an employee object contains a mutable list, cloning the employee without copying that list can leave the original and clone referring to the same list.

Copy constructors, static factory methods or explicit copying operations are often clearer alternatives because they allow developers to specify precisely which fields and nested objects must be copied.

41. What Is a Queue in Java?

The Queue interface represents a collection intended to hold elements before processing. Many queue implementations follow First-In-First-Out (FIFO) ordering, but not every queue uses FIFO.

For example, ArrayDeque supports FIFO operations, whereas PriorityQueue retrieves elements according to their priority ordering.

Example using ArrayDeque:

import java.util.ArrayDeque;
import java.util.Queue;

Queue<String> tasks = new ArrayDeque<>();

tasks.offer("Task A");
tasks.offer("Task B");

System.out.println(tasks.poll()); // Task A
System.out.println(tasks.peek()); // Task B

The offer() method attempts to add an element, poll() retrieves and removes the head or returns null when empty, and peek() retrieves the head without removing it.

For communication between threads, a suitable BlockingQueue implementation can provide waiting behaviour and coordination that ordinary ArrayDeque does not provide.

42. What Is Synchronisation in Java?

Synchronisation coordinates access to shared mutable data between threads. It can prevent race conditions and establish memory visibility guarantees when all relevant operations follow a correct synchronisation strategy.

Java provides several mechanisms, including:

  • synchronized methods and blocks: Protect code using monitor locks.
  • ReentrantLock: Provides explicit locking with features such as timed acquisition and interruptible lock waits.
  • Atomic classes: Support atomic operations on selected shared values.
  • Concurrent collections: Provide documented thread-safe operations for common data structures.

Practical distinction: Synchronisation is broader than simply adding the synchronized keyword. A correct concurrency design must identify shared state, define the operations that must be atomic and apply the appropriate coordination mechanism consistently.

43. What Is the Difference Between Shallow Copy and Deep Copy?

A shallow copy creates a separate outer object but generally retains references to the same nested objects. Modifications to shared mutable nested objects may therefore be visible through both copies.

A deep copy creates independent copies of the relevant nested mutable state, so changes to one copied object graph do not affect the corresponding objects in the other.

Shallow CopyDeep Copy
Creates a new outer objectCreates a new outer object and independently copies relevant nested state
Nested mutable objects may remain sharedRelevant nested mutable objects are not shared
Often simpler and less expensiveMay require more memory and copying work
Appropriate when sharing referenced state is safeUseful when the copy must be independent

Interview example: A copied order object with a shared mutable item list is not independent. If the new order must be modified without changing the original, the list and any relevant mutable item objects also need suitable copying.

44. What Are Annotations in Java?

Annotations provide metadata associated with Java program elements such as classes, methods, fields and parameters. Compilers, frameworks and other tools can use this metadata for validation, configuration or code processing.

Examples include:

  • @Override: Helps the compiler check that a method overrides or implements an appropriate inherited method.
  • @Deprecated: Indicates that an API is discouraged for continued use.
  • @FunctionalInterface: Checks that an interface satisfies functional-interface requirements.
  • Framework annotations: Tools such as Spring can inspect annotations for dependency injection and application configuration.

The @Retention meta-annotation controls whether an annotation is retained in source code, class files or at runtime. Not every annotation is available for reflection during execution.

45. What Is the Java Memory Model?

The Java Memory Model (JMM) defines the rules governing how threads interact through shared memory, including visibility and ordering of reads and writes.

A central concept is the happens-before relationship, which helps determine when effects from one action must be visible to another.

Examples of happens-before relationships include:

  • A monitor unlock happens-before a subsequent lock of the same monitor.
  • A write to a volatile field happens-before a subsequent read of that field.
  • Calling Thread.start() happens-before actions in the started thread.
  • Actions in a thread happen-before another thread successfully returns from a join() waiting for its termination.

Practical scenario: If one thread writes a normal shared field and another reads it without suitable coordination, the second thread may not observe the update as expected. Correct synchronisation, volatile fields or appropriate concurrency utilities can establish the necessary guarantees.

46. What Is Reflection in Java?

Reflection allows Java programs to inspect classes, constructors, methods, fields and annotations at runtime using APIs including java.lang.reflect.

Frameworks use reflection for tasks such as dependency injection, object mapping, test discovery and dynamic invocation.

Example:

class Employee  
}

Class<?> type = Employee.class;

for (java.lang.reflect.Method method :
        type.getDeclaredMethods())  

The code lists methods declared directly by Employee. Reflection can also access constructors and fields, subject to Java’s access-control and module-encapsulation rules.

Interview consideration: Discuss how reflection provides flexibility but can complicate refactoring, impose additional access restrictions and introduce overhead compared with direct method calls in some scenarios.

47. What Is the Purpose of the join() Method in Java?

The join() method allows one thread to wait until another thread terminates. It is useful when a task must finish before the calling thread continues with dependent work.

public class JoinExample  
}

The main thread waits for worker to terminate before printing the second message. The join() method can throw InterruptedException if the waiting thread is interrupted.

Practical distinction: join() waits for thread termination; it does not itself return a result from the task. For tasks that produce results, abstractions such as Future or CompletableFuture may be more suitable.

48. What Are the Benefits and Limitations of the Java Stream API?

The Java Stream API supports declarative processing of data using operations such as filtering, mapping, sorting and aggregation. It was introduced in Java 8.

Example:

import java.util.Arrays;
import java.util.List;

List<Integer> numbers =
        Arrays.asList(2, 5, 8, 11, 14);

int sum = numbers.stream()
        .filter(number -> number % 2 == 0)
        .mapToInt(Integer::intValue)
        .sum();

System.out.println(sum); // 24

The stream selects even numbers and calculates their sum.

  • Clear data-processing pipelines: Operations such as filter(), map() and collect() can express transformations concisely.
  • Lazy intermediate operations: Intermediate operations generally execute when a terminal operation consumes the stream.
  • Parallel processing support: Parallel streams can distribute suitable operations across processing resources.
  • Trade-offs: Stream pipelines with side effects, shared mutable state or complex debugging requirements may be harder to reason about.

Interview tip: Explain why parallel streams do not always improve performance. Workload size, splitting overhead, contention, ordering constraints and the cost of individual operations all matter.

49. What Is the Difference Between == and equals() in Java?

When used with object references, == checks whether both references identify the same object. The equals() method evaluates equality according to the class’s implementation.

For primitive values, == compares the values after any applicable numeric promotion or other language conversion.

String a = new String("Java");
String b = new String("Java");

System.out.println(a == b);      // false
System.out.println(a.equals(b)); // true

The references identify different objects, so == returns false. The strings contain the same character sequence, so equals() returns true.

Classes that override equals() should also implement hashCode() consistently. Equal objects must have equal hash codes for correct operation in hash-based collections.

Null-handling tip: Calling a.equals(b) throws NullPointerException when a is null. Objects.equals(a, b) supports null-safe equality comparisons.

50. What Is the Difference Between HashMap and ConcurrentHashMap in Java?

HashMap is a general-purpose map implementation that is not synchronised by default. ConcurrentHashMap is designed to support concurrent access and updates by multiple threads.

The distinction becomes important when several threads share a map and may update it at the same time.

HashMapConcurrentHashMap
Not thread-safe for unsynchronised concurrent modificationSupports thread-safe concurrent retrievals and updates
Allows one null key and null valuesDoes not allow null keys or null values
Iterators are generally fail-fast on structural modification, with best-effort detectionIterators are weakly consistent and can tolerate concurrent modification
Compound updates require appropriate external coordinationProvides atomic operations such as compute() and merge() for suitable updates
Suitable when concurrency control is unnecessary or handled elsewhereSuitable for shared maps that require concurrent access

Practical example: Counting events from concurrent tasks

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

ConcurrentMap<String, Integer> counts =
        new ConcurrentHashMap<>();

counts.merge("login", 1, Integer::sum);
counts.merge("login", 1, Integer::sum);

System.out.println(counts.get("login")); // 2

merge() performs the update atomically for the specified key. This avoids an unsafe read-modify-write sequence such as retrieving a value, incrementing it and writing it back without synchronisation.

Interview consideration: ConcurrentHashMap does not make an entire sequence of unrelated map operations one atomic transaction. When several updates must succeed together, additional coordination or a different design may be required.

Oracle’s ConcurrentHashMap API documentation describes its concurrency guarantees, restrictions on null values and atomic update methods.

Key Takeaways for Java Interviews with 2 to 3 Years of Experience

Java developers with 2 to 3 years of experience should be prepared to explain how Java concepts work and where they apply in actual development. Knowing a definition is useful, but interviewers may also ask you to interpret code, compare implementation choices and describe problems you have solved.

Use the following revision checklist to identify areas that deserve additional practice.

TopicWhat to Practise
Core Java and OOPOverloading versus overriding, inheritance, interfaces, immutability and exception handling
Collections FrameworkChoosing between List, Set and Map implementations; equality, hashing and iteration behaviour
MultithreadingThread creation, synchronisation, volatile, race conditions, deadlocks and concurrent collections
Java 8 and laterLambda expressions, streams, functional interfaces, method references and relevant newer language features
Practical codingString and array operations, collection-based problems, debugging and explaining code output
Project experienceExplaining design decisions, exception handling, performance trade-offs, testing and production issues you have encountered

For project discussions, prepare specific examples using a simple sequence: describe the problem, explain your technical approach, identify the relevant Java features and discuss the result. Mention outcomes only when they are accurate and supported by your experience.

The questions in this guide are a revision resource rather than a prediction of a particular employer’s interview. Backend development positions may also involve SQL, REST APIs, Spring, testing and deployment practices, depending on the vacancy.

FAQs

Prioritise object-oriented programming, collections, exception handling, strings, concurrency, JVM fundamentals and Java 8 features such as streams and lambda expressions. For backend roles, you may also need SQL, REST APIs, Spring and testing knowledge. Review the job description and prepare examples from projects you have actually worked on.

Yes. Lambda expressions, functional interfaces, method references, streams and Optional remain relevant in modern Java applications. Interviewers may ask you to explain stream operations, identify side effects or compare a stream solution with an ordinary loop. You should also be familiar with newer Java features relevant to the employer's technology stack.

Coding exercises may cover strings, arrays, maps, sets, sorting, frequency counting and basic algorithms. Some interviews include debugging exercises or questions involving streams and collections. Practise explaining time complexity, edge cases and why you selected a particular solution rather than memorising code alone.

It is useful to understand common patterns such as Factory, Strategy, Builder and Observer, especially if they relate to your projects. Be prepared to explain the problem a pattern solves and when a simpler design may be preferable. The expected depth depends on the role, company and development responsibilities.

Review checked and unchecked exceptions, exception propagation, try-catch-finally, try-with-resources and custom exception types. Practise scenarios involving resource cleanup, logging, error recovery and when to propagate an exception. Be able to explain why catching every exception without appropriate handling can hide failures.

Multithreading is particularly relevant for backend and enterprise Java roles. Revise Thread and Runnable, executors, synchronisation, volatile, race conditions, deadlocks and concurrent collections. Understand how BlockingQueue supports producer-consumer coordination and why shared mutable state requires careful handling. For modern Java roles, familiarity with virtual threads may also be useful.

Kishan Mohan

Kishan Mohan

Kishan is a digital content strategist passionate about helping professionals land their next great role. With extensive experience in SEO and career-focused content, Kishan creates actionable resources that guide job seekers through every stage of their career journey.

beware-of-scammers-icon Beware of Scammers

We don't charge money for job offers

Know More