Top 60 Java Interview Questions & Answers for Freshers & Experienced (2026)
Java remains one of the most widely used programming languages across Indian IT companies — TCS, Infosys, Wipro, Cognizant, HCL, and every BFSI organisation. Whether you are a fresher attending your first campus drive or an experienced developer switching companies, Java questions are almost certain to appear in your technical interview. This comprehensive guide covers 60 of the most important Java interview questions organised by topic — with clear, interview-ready answers you can study and adapt.
📋 Contents
S1 Core Java & OOP Concepts
Inheritance: A child class acquires the properties and behaviours of a parent class using the
extends keyword, enabling code reuse and hierarchical relationships.Polymorphism: One interface, many implementations. Achieved via method overloading (compile-time / static polymorphism) and method overriding (runtime / dynamic polymorphism).
Abstraction: Hiding implementation complexity and exposing only essential functionality. Implemented using abstract classes and interfaces.
Interface: Since Java 8, can have abstract methods, default methods, and static methods. All fields are implicitly
public static final. A class can implement multiple interfaces.When to use: Use an abstract class when related classes share common code. Use an interface to define a contract for unrelated classes (e.g.,
Comparable, Serializable).
== compares references (memory addresses) for objects — checks if both variables point to the exact same object in heap memory. For primitives, it compares values..equals() compares content/value. The default Object.equals() is the same as ==, but classes like String, Integer override it to compare actual values.Example:
String a = new String("hello");String b = new String("hello");a == b → false (different objects in heap)a.equals(b) → true (same character content)
Overriding (Runtime polymorphism): A subclass provides its own implementation of a method already defined in the parent class — same name, same parameters, same return type. Requires inheritance. Use
@Override annotation.Key rule: You cannot override static, final, or private methods.
super(). However, you cannot create an instance of an abstract class directly using new. The constructor in the abstract class is used to initialise common fields shared across all subclasses.final — keyword: a final variable cannot be reassigned; a final method cannot be overridden; a final class cannot be subclassed.finally — block in try-catch-finally: always executes regardless of whether an exception occurs or is caught. Used for cleanup (closing DB connections, file handles).finalize() — method in Object class called by the Garbage Collector before destroying an object. Deprecated in Java 9 and removed in Java 18 — do not rely on it for resource cleanup.
ClassName.method()). Cannot access instance variables or instance methods directly. Common use: utility/helper methods, factory methods.Instance methods belong to an object. Called on an object reference. Can access both instance and static members. Most regular methods are instance methods.
this refers to the current class instance. Used to differentiate instance variables from local variables with the same name, and to call the current class's constructor via this().super refers to the parent class. Used to call the parent class constructor via super() (must be the first statement in a subclass constructor), access parent class methods (super.methodName()), and access parent class fields.
static keyword inside a class. It executes once when the class is first loaded into the JVM — before any object is created or any static method is called. Static blocks are used to initialise static fields or perform one-time setup operations (e.g., loading a driver class).Deep copy: Creates a new object and recursively copies all nested objects. The copy is completely independent of the original. Implemented by overriding
clone(), copy constructors, or serialisation.
S2 String Handling
1. Security: Strings are used to pass sensitive data (DB passwords, class names for loading). If they were mutable, another thread or code could modify them after validation.
2. Thread safety: Immutable objects are inherently thread-safe — no synchronisation required when sharing String references across threads.
3. String Pool efficiency: The JVM maintains a String pool in the heap. Multiple variables can safely reference the same literal. If Strings were mutable, changing one reference would corrupt all others pointing to the same literal.
String s = "hello", the JVM checks if "hello" already exists in the pool. If yes, it returns the existing reference — no new object is created. If no, it creates a new object in the pool.Using
new String("hello") always creates a new object in the heap (outside the pool). The intern() method can be used to explicitly add a String to the pool.StringBuilder: Mutable. Modifies the same object. Not thread-safe. Best for single-threaded string manipulation (e.g., building a string inside a loop). Faster than StringBuffer.
StringBuffer: Mutable and thread-safe (all methods are synchronised). Slower than StringBuilder due to synchronisation overhead. Use only when string manipulation needs to be thread-safe.
equals() performs a case-sensitive comparison — "Hello" and "hello" are not equal. equalsIgnoreCase() ignores case differences — "Hello" and "hello" are considered equal. Both methods compare character content, not references.intern() returns a canonical representation of the String. If a String with the same value already exists in the String pool, it returns that reference. Otherwise, it adds the String to the pool and returns its reference. This allows strings created with new String() to participate in reference equality checks with pooled literals.public static String reverse(String s) {
char[] chars = s.toCharArray();
int left = 0, right = chars.length - 1;
while (left < right) {
char temp = chars[left];
chars[left] = chars[right];
chars[right] = temp;
left++; right--;
}
return new String(chars);
}
Time complexity: O(n). Space complexity: O(n) for the char array.
S3 Collections Framework
java.util package. It eliminates the need to write custom data structures for most common use cases.LinkedList: Doubly-linked list. Random access is O(n). Insertion/deletion at head or tail is O(1). Implements both List and Deque. Use when frequent insertions/deletions at arbitrary positions are needed.
TreeMap: Maintains keys in natural sorted order (or custom Comparator order). No null keys allowed. O(log n) for get/put. Backed by a Red-Black tree. Use when you need sorted key traversal.
Hashtable: Synchronised (thread-safe). No null keys or values. Slower due to overhead. Legacy class from Java 1.0. In modern code, prefer
ConcurrentHashMap for thread-safe map operations — it is more efficient than Hashtable.
LinkedHashSet: Maintains insertion order. No duplicates. Slightly slower than HashSet. Use when order of insertion matters.
TreeSet: Maintains natural sorted order. No null elements. O(log n) for operations. Use when sorted unique elements are needed.
remove() but not add() or set().ListIterator: Available only for List implementations. Can traverse both forward (
next()) and backward (previous()). Supports add(), remove(), and set() during iteration.
1. Use
Iterator.remove() instead of collection.remove() while iterating.2. Collect elements to remove in a separate list and remove them after the loop.
3. Use
CopyOnWriteArrayList or ConcurrentHashMap which allow concurrent modification.4. Use Java 8's
removeIf() method which handles this internally.
Node[] table). When you call put(key, value):1.
hashCode() of the key is computed and compressed to a bucket index using a bitwise AND.2. If the bucket is empty, a new Node is placed there.
3. If occupied (collision), Java checks equality using
equals(). If keys are equal, the value is updated. If different, the new entry is added to a linked list (Java 7) or a balanced tree (Java 8+, when the list exceeds 8 entries — TREEIFY_THRESHOLD).When the load factor (default 0.75) is exceeded, the map rehashes — the table doubles in size and all entries are redistributed.
S4 Exception Handling
throws. They represent recoverable conditions. Examples: IOException, SQLException, FileNotFoundException.Unchecked exceptions (RuntimeExceptions): Not checked at compile time. Represent programming bugs that should be fixed, not caught. Examples:
NullPointerException, ArrayIndexOutOfBoundsException, ClassCastException, IllegalArgumentException.
throw is used to explicitly throw an exception object from inside a method: throw new IllegalArgumentException("Invalid input");throws is used in a method signature to declare that the method might throw certain checked exceptions, delegating handling to the caller: public void readFile(String path) throws IOException {...}
Since Java 7, multi-catch allows handling multiple exceptions in one block:
catch (IOException | SQLException e) { ... }This reduces code duplication when the same handling logic applies to different exception types. The caught exception variable is implicitly final in multi-catch.
finally block always executes after the try-catch block — whether an exception occurred or not. Used to release resources (close streams, database connections).Cases where finally does NOT execute:
1.
System.exit() is called — JVM terminates before finally runs.2. The JVM itself crashes (power failure, OutOfMemoryError killing the process).
3. An infinite loop or deadlock in the try block.
AutoCloseable (like streams, connections) after the try block ends — no explicit finally needed:try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
String line = br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
// br is automatically closed here
This eliminates resource leaks and is the recommended way to handle I/O in modern Java.
Exception (for checked) or RuntimeException (for unchecked):public class InsufficientFundsException extends Exception {
private double amount;
public InsufficientFundsException(double amount) {
super("Insufficient funds: required " + amount);
this.amount = amount;
}
public double getAmount() { return amount; }
}
Custom exceptions give meaningful names to domain-specific error conditions and allow callers to handle them specifically.
S5 Multithreading & Concurrency
run() and call start(). Limitation: the class cannot extend any other class.2. Implement Runnable interface: Implement
run(), pass to a Thread constructor. Preferred approach.3. Implement Callable interface: Like Runnable but returns a value and can throw checked exceptions. Used with
ExecutorService and Future<T>.4. Using ExecutorService (modern approach):
ExecutorService executor = Executors.newFixedThreadPool(5); executor.submit(runnableTask); — manages a thread pool for better resource management.
start() creates a new thread and then calls run() in that new thread. This is the correct way to start a thread.Calling
run() directly does NOT create a new thread — it executes run() in the current (calling) thread, like a normal method call. No multithreading happens.
synchronized keyword can be applied to:Instance method: Locks the current object instance.
Static method: Locks the Class object (shared across all instances).
Code block:
synchronized(lockObject) { ... } — locks on a specific object, allowing finer-grained control than method-level synchronisation.
Four conditions (all must hold): Mutual exclusion, hold and wait, no preemption, circular wait.
Prevention strategies:
1. Always acquire locks in the same consistent order across all threads.
2. Use
tryLock(timeout) from java.util.concurrent.locks.Lock.3. Minimise the scope and duration of locks.
4. Use higher-level concurrency utilities (
ConcurrentHashMap, BlockingQueue) that manage locking internally.
volatile keyword ensures that a variable's value is always read from and written to main memory, not from a thread's local CPU cache. This guarantees visibility — when one thread updates a volatile variable, all other threads immediately see the updated value.However,
volatile does NOT guarantee atomicity for compound operations (like i++). For atomic operations, use AtomicInteger, AtomicLong, etc. from java.util.concurrent.atomic.
Object class and must be called within a synchronised block/method:wait() — causes the current thread to release the lock and wait until another thread calls notify() or notifyAll() on the same object.notify() — wakes up one (arbitrary) thread waiting on that object's lock.notifyAll() — wakes up all threads waiting on that object's lock. All wake up but only one gets the lock at a time. Preferred in most cases to avoid missed signals.
Thread.sleep(ms) pauses the current thread for the specified duration. Does not release any locks held. Is a static method of the Thread class.object.wait() pauses the current thread AND releases the lock on the object. The thread waits until notify()/notifyAll() wakes it. Is an instance method of Object. Must be called inside a synchronised block.
java.util.concurrent) provides a higher-level abstraction for managing threads than creating threads manually. Key components:ExecutorService — manages a thread pool. Submit tasks with submit() or execute().Executors — factory class: newFixedThreadPool(n), newCachedThreadPool(), newSingleThreadExecutor().Future<T> — represents the result of an asynchronous computation. Call future.get() to retrieve the result (blocking).CompletableFuture (Java 8+) — for non-blocking asynchronous programming with chaining.
S6 Java 8+ Features
Syntax:
(parameters) -> expression or (parameters) -> { statements; }Example — without lambda:
Runnable r = new Runnable() { public void run() { System.out.println("Hello"); } };With lambda:
Runnable r = () -> System.out.println("Hello");With parameters:
Comparator<String> c = (a, b) -> a.compareTo(b);
@FunctionalInterface (optional but recommended).Built-in functional interfaces in java.util.function:
Predicate<T> — takes T, returns boolean: test(T t)Function<T,R> — takes T, returns R: apply(T t)Consumer<T> — takes T, returns void: accept(T t)Supplier<T> — takes nothing, returns T: get()BiFunction<T,U,R> — takes T and U, returns R
List<String> names = Arrays.asList("Alice","Bob","Anita","Charlie");
List<String> result = names.stream()
.filter(n -> n.startsWith("A")) // intermediate
.map(String::toUpperCase) // intermediate
.sorted() // intermediate
.collect(Collectors.toList()); // terminal
// result: ["ALICE", "ANITA"]
map() transforms each element to another element (one-to-one mapping). Returns a Stream of the transformed values.flatMap() transforms each element to a stream of values and then flattens all those streams into a single stream (one-to-many mapping). Used when each element maps to multiple values.Example:
List<List<Integer>> nested = Arrays.asList(Arrays.asList(1,2), Arrays.asList(3,4));nested.stream().flatMap(Collection::stream).collect(...) → [1, 2, 3, 4]
ClassName::methodNameFour types:
Static method:
Integer::parseInt (same as s -> Integer.parseInt(s))Instance method of a specific object:
System.out::printlnInstance method of arbitrary object:
String::toUpperCase (same as s -> s.toUpperCase())Constructor reference:
ArrayList::new (same as () -> new ArrayList<>())
Optional<T> is a container that may or may not contain a non-null value. It was introduced to reduce NullPointerExceptions and make null-handling more explicit.Optional<String> opt = Optional.ofNullable(getName());
// Safe retrieval
String name = opt.orElse("Unknown");
String upper = opt.map(String::toUpperCase).orElse("N/A");
// Check presence
if (opt.isPresent()) { ... }
// Java 9+
opt.ifPresentOrElse(
name -> System.out.println("Hello, " + name),
() -> System.out.println("No name found")
);
interface Vehicle {
void start();
default void honk() {
System.out.println("Beep beep!");
}
}
If a class implements two interfaces that both define the same default method, the class must override the method to resolve the conflict.
java.lang): Used to define the natural ordering of a class. Implement compareTo(T other) inside the class itself. E.g., String, Integer implement Comparable.Comparator (
java.util): External comparison logic. Implement compare(T o1, T o2) in a separate class or lambda. Useful when you need multiple sort orders or can't modify the class.Collections.sort(list) uses Comparable. Collections.sort(list, comparator) uses Comparator.
S7 Spring Boot Basics
Spring Boot is an opinionated extension of Spring that eliminates most configuration boilerplate. Key features:
Auto-configuration: Automatically configures beans based on classpath (if Spring MVC is on the classpath, a DispatcherServlet is auto-configured).
Embedded server: Tomcat/Jetty/Undertow built-in — no WAR deployment needed.
Starter POMs:
spring-boot-starter-web pulls in Spring MVC, embedded Tomcat, Jackson, and validation in one dependency.Actuator: Production-ready monitoring endpoints (health, metrics, info) out of the box.
@SpringBootApplication — main class annotation; combines @Configuration + @EnableAutoConfiguration + @ComponentScan@RestController — combines @Controller + @ResponseBody; every method returns JSON/XML directly@GetMapping, @PostMapping, @PutMapping, @DeleteMapping — HTTP method mappings@RequestBody — binds HTTP request body to a method parameter@PathVariable — extracts values from URI path: /users/{id}@RequestParam — extracts query parameters: /users?page=1@Autowired — injects dependencies automatically@Service, @Repository, @Component — stereotype annotations for auto-detection@Entity, @Table, @Id, @GeneratedValue — JPA entity mapping
new, the container creates and manages them.Dependency Injection (DI): The mechanism IoC uses to provide dependencies to an object. Spring supports three types:
1. Constructor injection (recommended): Dependencies passed via constructor — immutable, testable.
2. Setter injection: Dependencies set via setter methods — flexible for optional dependencies.
3. Field injection: Using
@Autowired directly on a field — convenient but harder to test.
The difference is semantic and tooling-related:
@Component — generic Spring component@Service — marks business logic layer classes (service layer)@Repository — marks data access layer classes; additionally enables automatic exception translation (JPA exceptions → Spring DataAccessException)
singleton (default) — one instance per Spring container. All requests for the bean return the same instance.prototype — new instance created every time the bean is requested.request — (web) one instance per HTTP request.session — (web) one instance per HTTP session.application — (web) one instance per ServletContext.
@ControllerAdvice (or @RestControllerAdvice) with @ExceptionHandler:@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(
ResourceNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse(ex.getMessage()));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneral(Exception ex) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse("Something went wrong"));
}
}
This centralises exception handling across all controllers.
@RequestBody — binds the entire HTTP request body to a Java object. Used for POST/PUT requests where the client sends JSON/XML. Spring uses Jackson to deserialise the body automatically.@RequestParam — extracts a single query parameter from the URL. Example: for a GET request to /search?keyword=java&page=1, use @RequestParam String keyword, @RequestParam int page.@PathVariable extracts values from the URL path itself: /users/{id} → @PathVariable Long id.
S8 Output-Based & Tricky Questions
These questions are extremely common at TCS, Infosys, Wipro, and Cognizant technical interviews. The interviewer shows code and asks "what is the output?"
Both
s1 and s2 are string literals, so they both point to the same object in the String Pool. == compares references — they are the same reference, so the result is true. If either were new String("Java"), the result would be false.Java caches Integer objects in the range -128 to 127 (Integer Cache). So
a and b both reference the same cached Integer(127) object → true.128 is outside the cache range, so
c and d are two different Integer objects in the heap → false. Always use .equals() to compare Integer values.Even though there is a
return statement in the try block, the finally block executes before the method actually returns. The return in the try block is deferred until the finally block completes. Output: try then finally.i++ (post-increment): returns the current value (5), then increments i to 6.++i (pre-increment): increments i to 7 first, then returns 7. So output is 5, then 7.Static A (once, when class is loaded)
Instance A (for first new A())
Constructor A
Instance A (for second new A())
Constructor A
Order of execution: static blocks run once when the class is first loaded. For each new object: instance initialiser block runs before the constructor body.
Modifying a collection while iterating over it with a for-each loop (which uses an Iterator internally) triggers a
ConcurrentModificationException. Fix: use Iterator.remove(), list.removeIf(i -> i == 2), or collect elements to remove separately and remove after the loop.class Animal { void sound() { System.out.println("Animal"); } }
class Dog extends Animal { void sound() { System.out.println("Dog"); } }
Animal a = new Dog();
a.sound(); // Output: "Dog"
Even though the reference type is Animal, the JVM calls the Dog version of sound() because the actual object is a Dog. This is the essence of runtime polymorphism (dynamic dispatch).
💡 Final Preparation Tip: For each question in this guide, close the page and try writing the answer from memory. For code questions, write the code on paper (not an IDE). Interviewers at TCS, Infosys, and Wipro often ask you to write code on a whiteboard or Google Docs without autocomplete. The ability to write correct syntax from memory demonstrates genuine understanding.
Java interviews in India are highly structured and predictable — the same 50–60 questions rotate across companies with slight variations. Master these thoroughly, understand the underlying concepts (not just the answers), and you will be well-prepared for any Java technical round.
Codeloop