Prepare with real Java interview questions asked at TCS, Infosys, Wipro, EPAM, Mphasis, startups and service-based companies.
The most important OOP questions asked in almost every Java interview.
The four pillars of Object-Oriented Programming are:
extends.Interview Tip: Always explain each pillar with a real-world example — interviewers want understanding, not definitions.
== compares references (memory addresses) for objects; for primitives it compares values. .equals() compares content. String, Integer and other wrapper classes override it to compare values.
String a = new String("hello");
String b = new String("hello");
System.out.println(a == b); // false (different objects)
System.out.println(a.equals(b)); // true (same content)Interview Tip: Always use .equals() to compare object contents, especially Strings.
| Feature | Abstract Class | Interface |
|---|---|---|
| Methods | Abstract + concrete | Default + abstract (Java 8+) |
| Variables | Instance variables allowed | Only public static final |
| Constructor | Yes | No |
| Multiple inheritance | No (single only) | Yes (implement many) |
| Use case | IS-A with shared state | Contract / capability |
Interview Tip: Rule of thumb: use an interface for a capability (HAS-A), an abstract class for shared base state (IS-A).
Overloading — same method name, different parameters, in the same class. Resolved at compile time.
Overriding — a subclass provides a new implementation for a parent method with the same signature. Resolved at runtime.
// Overloading
void print(String s) {}
void print(int n) {}
// Overriding
class Animal { void speak() { System.out.println("..."); } }
class Dog extends Animal {
@Override
void speak() { System.out.println("Woof"); }
}final variable — value cannot be changed (a constant).final method — cannot be overridden in a subclass.final class — cannot be subclassed (e.g. String, Integer).this — refers to the current object; used to access current-class fields, methods and constructors.super — refers to the immediate parent class; used to call the parent constructor or access overridden parent members.class Animal {
Animal() { System.out.println("Animal created"); }
}
class Dog extends Animal {
String name;
Dog(String name) {
super(); // parent constructor
this.name = name; // current object's field
}
}A constructor is a special method used to initialise an object. It has the same name as the class and no return type. Yes — constructors can be overloaded by varying the parameter list.
class Student {
String name; int age;
Student() { this("Unknown", 0); } // no-arg
Student(String name) { this(name, 18); } // one-arg
Student(String name, int age) { // two-arg
this.name = name; this.age = age;
}
}Interview Tip: Constructor chaining with this(...) avoids duplicate initialisation code.
static members belong to the class, not to any instance — shared across all objects.
class Counter {
static int count = 0; // shared
Counter() { count++; }
static void show() { // no object needed
System.out.println(count);
}
}Important collection questions every fresher should know before interviews.
| Feature | ArrayList | LinkedList |
|---|---|---|
| Backed by | Dynamic array | Doubly linked list |
| Random access (get) | O(1) | O(n) |
| Insert/delete (middle) | O(n) | O(1) |
| Use case | Frequent reads | Frequent insertions |
Interview Tip: Never say one is just 'faster' — always qualify it with the operation type.
| Feature | HashMap | HashTable |
|---|---|---|
| Thread safety | Not thread-safe | Thread-safe (synchronized) |
| Null keys | One null key allowed | No null keys |
| Performance | Faster | Slower |
| Modern use | Preferred | Legacy — use ConcurrentHashMap |
Comparator, O(log n) get/put.A HashMap stores entries in an array of buckets. The key's hashCode() is hashed to compute a bucket index. Collisions (multiple keys in one bucket) are stored as a linked list, which converts to a balanced tree when a bucket exceeds 8 entries (Java 8+) for O(log n) lookups.
// Simplified flow on put(key, value): int hash = hash(key.hashCode()); int index = (n - 1) & hash; // bucket index // equals() resolves collisions within the bucket
Interview Tip: Mention the treeify threshold (8) — it impresses interviewers and shows Java 8 awareness.
Collection types.List, and can add/set elements during iteration.You get a ConcurrentModificationException from fail-fast iterators. Use Iterator.remove() instead of collection.remove() during iteration, or use a fail-safe collection like CopyOnWriteArrayList.
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if (s.equals("x")) it.remove(); // safe
}compareTo() method, implemented by the class itself.compare() method, defined externally.// Comparable
class Student implements Comparable<Student> {
int marks;
public int compareTo(Student o) { return this.marks - o.marks; }
}
// Comparator
Comparator<Student> byName = (a, b) -> a.name.compareTo(b.name);
list.sort(byName);ArrayList, LinkedList).HashSet, TreeSet).HashMap, TreeMap).Join CodeBegun Java Full Stack Training and boost your career in top IT companies.
String immutability, pooling and the StringBuilder family.
Once created, a String's value cannot change — any modification creates a new object. Reasons:
A special region in the JVM heap where string literals are stored. When you write a literal, Java checks the pool first and reuses the existing reference. new String("hello") always creates a new object, bypassing the pool.
String a = "hello"; // pooled
String b = "hello"; // same reference
String c = new String("hello"); // new object
a == b; // true
a == c; // false
a == c.intern(); // true| Feature | String | StringBuilder | StringBuffer |
|---|---|---|---|
| Mutability | Immutable | Mutable | Mutable |
| Thread safe | Yes | No | Yes |
| Performance | Slowest concat | Fastest | Slower |
Interview Tip: Use StringBuilder for string building inside loops — it's the most common follow-up question.
intern() returns the canonical (pooled) reference of a string. If the pool already contains an equal string, that reference is returned; otherwise the string is added to the pool.
String a = new String("cb");
String b = a.intern();
System.out.println(b == "cb"); // trueBecause String is immutable, each += creates a brand new object — O(n²) work and lots of garbage. Use a StringBuilder instead.
// Bad — creates n new String objects String s = ""; for (int i = 0; i < n; i++) s += i; // Good — O(n) StringBuilder sb = new StringBuilder(); for (int i = 0; i < n; i++) sb.append(i); String result = sb.toString();
Checked vs unchecked, try-with-resources and the final/finally/finalize trap.
Checked — must be handled with try-catch or declared with throws (e.g. IOException, SQLException). Checked at compile time.
Unchecked — extend RuntimeException, no mandatory handling (e.g. NullPointerException, ArrayIndexOutOfBoundsException).
throw — used inside a method to actually throw an exception object.throws — used in the method signature to declare that it might throw an exception.void readFile(String path) throws IOException {
if (path == null)
throw new IllegalArgumentException("Path is null");
}Interview Tip: Confusing throw and throws is one of the most common fresher mistakes — be crisp here.
The finally block normally always runs after try-catch (used for cleanup). It does not execute when System.exit() is called, or when the JVM crashes (e.g. OutOfMemoryError or the thread is killed).
Yes, since Java 7 with the multi-catch syntax using |.
try {
// risky code
} catch (IOException | SQLException e) {
// handle both the same way
log.error(e.getMessage());
}A Java 7 feature that automatically closes resources implementing AutoCloseable at the end of the block — no manual finally needed.
try (BufferedReader br = new BufferedReader(new FileReader("f.txt"))) {
return br.readLine();
} // br.close() called automaticallyExtend Exception (checked) or RuntimeException (unchecked) and pass a message to the parent constructor.
class InsufficientBalanceException extends RuntimeException {
InsufficientBalanceException(String msg) {
super(msg);
}
}
throw new InsufficientBalanceException("Balance too low");final — a keyword for constants / non-overridable methods / non-extendable classes.finally — a block that always runs after try-catch for cleanup.finalize() — a method called by the GC before object destruction (deprecated; avoid it).Interview Tip: This trio is a classic trick question — keep the three meanings completely separate in your head.
Lambdas, streams, Optional and functional interfaces.
Anonymous functions that implement a functional interface. Syntax: (parameters) -> expression.
List<String> names = Arrays.asList("Priya", "Ravi", "Sai");
names.forEach(name -> System.out.println(name));
// method reference form:
names.forEach(System.out::println);A sequence of elements supporting sequential and parallel aggregate operations. Streams do not store data — they pipeline operations on a source (filter, map, reduce, collect).
List<Integer> result = numbers.stream()
.filter(n -> n > 10)
.map(n -> n * 2)
.collect(Collectors.toList());Interview Tip: Be ready to write a filter→map→collect pipeline on a whiteboard from memory.
A container that may or may not hold a non-null value — it helps avoid NullPointerException.
Optional<String> name = Optional.ofNullable(getName());
name.ifPresent(System.out::println);
String value = name.orElse("Unknown");Methods with a body inside an interface, added in Java 8 so interfaces can evolve without breaking existing implementations.
interface Greet {
default void hello() {
System.out.println("Hello from interface");
}
}An interface with exactly one abstract method — the target type for a lambda. Annotated with @FunctionalInterface (optional but recommended). Examples: Predicate<T>, Function<T,R>, Consumer<T>, Supplier<T>.
@FunctionalInterface
interface Calculator {
int operate(int a, int b);
}
Calculator add = (a, b) -> a + b;A shorthand for a lambda that just calls an existing method. Four kinds: static, instance of a particular object, instance of an arbitrary object, and constructor.
names.stream().map(String::toUpperCase); // instance method list.forEach(System.out::println); // bound instance Stream.generate(ArrayList::new); // constructor
map() — transforms each element 1:1, producing a stream of values.flatMap() — transforms each element into a stream and flattens all of them into one stream.// map -> Stream<List<Integer>> lists.stream().map(list -> list); // flatMap -> Stream<Integer> (flattened) lists.stream().flatMap(List::stream).collect(Collectors.toList());
Threads, synchronisation, deadlocks and thread pools.
Implementing Runnable is preferred because:
Thread blocks you from extending another class.Runnable separates the task from the execution mechanism.ExecutorService accepts Runnable).It ensures only one thread executes a synchronized method or block at a time, using the object's intrinsic lock (monitor) — preventing race conditions.
class Counter {
private int count = 0;
synchronized void increment() { count++; }
}A deadlock occurs when two or more threads are blocked forever, each waiting for a lock the other holds.
Prevention: always acquire locks in a consistent global order, use timeouts (tryLock), and keep lock scopes small.
wait() — releases the lock and pauses the thread until notified.notify() — wakes one waiting thread.notifyAll() — wakes all waiting threads.All three must be called inside a synchronized block and are used for inter-thread communication (e.g. producer-consumer).
volatile guarantees that reads and writes of a variable go directly to main memory, so all threads see the latest value (visibility). It does not provide atomicity for compound operations like count++.
Interview Tip: Clarify: volatile solves visibility, not atomicity — use AtomicInteger or synchronized for count++.
run() returns nothing and cannot throw checked exceptions.call() returns a result (via Future) and can throw checked exceptions.Callable<Integer> task = () -> 2 + 2; Future<Integer> f = executor.submit(task); Integer result = f.get(); // 4
A managed pool of reusable threads that runs submitted tasks — avoiding the cost of creating a new thread per task.
ExecutorService pool = Executors.newFixedThreadPool(4); pool.submit(() -> doWork()); pool.shutdown();
equals, hashCode, toString and clone.
a.equals(b) is true, then a.hashCode() == b.hashCode() must be true.a.equals(b) is false, the hash codes may or may not differ.Because hash-based collections (HashMap, HashSet) use both. If you override only equals(), two "equal" objects may land in different buckets and break lookups.
@Override public boolean equals(Object o) { /* compare fields */ }
@Override public int hashCode() { return Objects.hash(id, name); }Interview Tip: Mention that IDEs and Lombok (@EqualsAndHashCode) can generate both consistently.
Without an override, toString() returns ClassName@hashHex — useless for debugging. Overriding it gives a human-readable representation, which is standard practice for entity and DTO classes.
clone() creates a copy of an object. The class must implement the Cloneable marker interface, otherwise CloneNotSupportedException is thrown. In modern Java, prefer copy constructors or copy factory methods over clone().
instanceof — true for the type and its subclasses.getClass() — checks the exact runtime class only.Object o = new Dog(); o instanceof Animal; // true (subclass) o.getClass() == Animal.class; // false (exact type is Dog)
Autoboxing, JVM internals and memory.
Java caches Integer objects in the range -128 to 127 (autoboxing cache). Inside that range, == can be true; outside it, each boxed value is a new object.
Integer a = 127, b = 127; System.out.println(a == b); // true (cached) Integer x = 200, y = 200; System.out.println(x == y); // false (new objects)
Interview Tip: Always use .equals() for wrapper types — this caching quirk is a favourite gotcha.
Relationship: JDK ⊃ JRE ⊃ JVM.
Garbage collection automatically reclaims memory from objects that are no longer reachable. System.gc() is only a suggestion. G1 is the default collector since Java 9.
Avoid these and you are already ahead of most freshers.
== compares value for Strings — it compares references.throw and throws.StringBuilder vs String.HashMap allows one null key.equals() without overriding hashCode().A simple plan our placed students actually follow.
CodeBegun's Java Full Stack program includes 10 mock interviews before placement.