OOP Questions
OOP Interview Questions
Q1: What is polymorphism?
Polymorphism means "many forms". It allows objects of different types to be treated as objects of a common parent type.
// Compile-time polymorphism (method overloading)
class Calculator {
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
String add(String a, String b) { return a + b; }
}
// Runtime polymorphism (method overriding)
class Animal {
void speak() { System.out.println("Animal speaks"); }
}
class Dog extends Animal {
@Override
void speak() { System.out.println("Dog barks"); }
}
Animal animal = new Dog();
animal.speak(); // "Dog barks" - runtime polymorphism
Q2: Abstract class vs interface?
| Feature | Abstract Class | Interface |
|---|---|---|
| Methods | Abstract + concrete | Abstract (default/static in Java 8+) |
| Variables | Any type | Only public static final |
| Inheritance | Single | Multiple |
| Constructor | Yes | No |
| Access modifiers | Any | public |
// Abstract class
abstract class Shape {
abstract double area();
void display() { System.out.println("Area: " + area()); }
}
// Interface
interface Drawable {
void draw();
default void fill() { System.out.println("Filling"); }
}
class Circle extends Shape implements Drawable {
double radius;
double area() { return Math.PI * radius * radius; }
void draw() { System.out.println("Drawing circle"); }
}
Q3: Explain SOLID principles.
- Single Responsibility: One class, one job
- Open/Closed: Open for extension, closed for modification
- Liskov Substitution: Subtypes must be substitutable
- Interface Segregation: Many specific interfaces > one general
- Dependency Inversion: Depend on abstractions, not concretions
// Single Responsibility
class UserValidator {
boolean validate(User user) { ... }
}
class UserRepository {
void save(User user) { ... }
}
// Open/Closed
interface DiscountStrategy {
double calculate(double amount);
}
class PercentageDiscount implements DiscountStrategy {
public double calculate(double amount) { return amount * 0.9; }
}
Q4: Composition vs Inheritance?
// Inheritance (IS-A)
class Car extends Vehicle { }
// Composition (HAS-A)
class Car {
private Engine engine; // Car HAS-A Engine
private List<Wheel> wheels;
Car() {
this.engine = new Engine();
this.wheels = Arrays.asList(new Wheel(), new Wheel(), new Wheel(), new Wheel());
}
}
When to use:
- Inheritance: Clear IS-A relationship, reuse parent implementation
- Composition: Flexible, avoids tight coupling, easier to test
Collections Questions
Collections Interview Questions
Q1: How does HashMap work internally?
// HashMap structure:
// Array of buckets → Each bucket is a linked list (or tree)
//
// put(key, value):
// 1. Compute hash: key.hashCode() ^ (h >>> 16)
// 2. Find bucket: hash & (n - 1)
// 3. If bucket empty: create new Entry
// 4. If key exists: update value
// 5. If collision: add to linked list (or tree if > 8 entries)
// 6. If load factor > 0.75: resize (double capacity)
// Key details:
// - Initial capacity: 16
// - Load factor: 0.75
// - Treeify threshold: 8 (linked list → tree)
// - Untreeify threshold: 6 (tree → linked list)
Q2: When to use TreeMap?
// HashMap: O(1) get/put, no ordering
Map<String, Integer> map = new HashMap<>();
// TreeMap: O(log n) get/put, sorted by key
Map<String, Integer> treeMap = new TreeMap<>();
// LinkedHashMap: O(1) get/put, insertion order
Map<String, Integer> linkedMap = new LinkedHashMap<>();
// When to use TreeMap:
// - Need sorted keys
// - Need range queries (subMap, headMap, tailMap)
// - Need nearest key (lowerKey, higherKey)
Q3: ArrayList vs LinkedList?
| Operation | ArrayList | LinkedList |
|---|---|---|
| get(index) | O(1) | O(n) |
| add(end) | O(1) amortized | O(1) |
| add(index) | O(n) | O(n) |
| remove(index) | O(n) | O(n) |
| contains | O(n) | O(n) |
| Memory | Compact | More (pointers) |
// ArrayList: best for random access, iteration
List<String> list = new ArrayList<>();
list.get(1000); // O(1) - fast
// LinkedList: best for frequent add/remove at head/tail
LinkedList<String> linked = new LinkedList<>();
linked.addFirst("a"); // O(1)
linked.removeFirst(); // O(1)
// In practice, ArrayList is almost always better
// LinkedList has poor cache locality
Q4: ConcurrentHashMap vs Collections.synchronizedMap?
// SynchronizedMap: locks entire map
Map<String, String> syncMap = Collections.synchronizedMap(new HashMap<>());
// All operations synchronized → poor concurrency
// ConcurrentHashMap: segment locking
ConcurrentHashMap<String, String> concurrentMap = new ConcurrentHashMap<>();
// Different buckets can be accessed simultaneously
// Better performance under high concurrency
Memory Questions
Memory Interview Questions
Q1: Stack vs Heap?
public void example() {
int x = 10; // Stack: primitive
int[] arr = {1, 2}; // Stack: reference, Heap: array
String s = "hello"; // Stack: reference, Heap: String
}
// Stack: fast, thread-private, limited (512KB-1MB)
// Heap: slower, shared, large (GB), GC managed
Q2: What is garbage collection?
// GC automatically reclaims memory from unreachable objects
Object obj = new Object(); // Object on heap
obj = null; // Object becomes eligible for GC
// JVM will eventually reclaim this memory
// Types:
// - Minor GC: Young generation (fast)
// - Major GC: Old generation (slower)
// - Full GC: Entire heap (pause all threads)
// You cannot force GC, but can suggest:
System.gc(); // Advisory only
Q3: equals/hashCode contract?
// Contract:
// 1. If x.equals(y) is true, then x.hashCode() == y.hashCode()
// 2. If x.hashCode() == y.hashCode(), x.equals(y) may be false
// 3. equals() and hashCode() must be consistent
// Why it matters:
Map<Person, String> map = new HashMap<>();
person1 and person2 are equal (same name, age)
map.put(person1, "Engineer");
map.get(person2); // Must return "Engineer"
// Only works if hashCode() is consistent with equals()
// Always override both together!
Q4: String pool?
String s1 = "hello"; // String pool
String s2 = "hello"; // Same object in pool
String s3 = new String("hello"); // New object on heap
s1 == s2; // true (same pool object)
s1 == s3; // false (different objects)
s1.equals(s3); // true (same content)
// intern() returns pool reference
String s4 = s3.intern(); // Returns pool reference
s1 == s4; // true
Q5: Memory leaks in Java?
// Can happen even with GC:
// 1. Static集合 growing forever
static List<Object> cache = new ArrayList<>();
// 2. Unclosed resources
InputStream is = new FileInputStream("file.txt");
// is.close() never called
// 3. Inner classes holding outer reference
public class Outer {
private int data = 10;
class Inner {
void print() { System.out.println(data); }
}
}
// 4. ThreadLocal variables
ThreadLocal<List<Object>> threadLocal = new ThreadLocal<>();
// Never removed → memory leak
Fix: Use try-with-resources, WeakHashMap, remove ThreadLocal, avoid static collections.
String Questions
String Interview Questions
Q1: Why are Strings immutable?
// Strings are immutable (final char[] in Java 7+)
String s = "hello";
s = s + " world"; // Creates new String, doesn't modify original
// Reasons:
// 1. String pool - multiple references share same string
// 2. Security - class loading, network connections
// 3. Thread safety - no synchronization needed
// 4. Hashing - hashCode can be cached
// If Strings were mutable:
String s1 = "hello";
String s2 = s1;
s1 = "modified"; // If mutable, s2 would also change!
// This would break HashMap, security, etc.
Q2: StringBuilder vs String?
// String concatenation in loops - BAD
String result = "";
for (int i = 0; i < 1000; i++) {
result += i; // Creates new String each iteration!
// O(n²) time and memory
}
// StringBuilder - GOOD
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i); // Modifies buffer in place
}
String result = sb.toString(); // O(n) time
// When to use StringBuilder:
// - String concatenation in loops
// - Building strings from multiple parts
// - Any scenario with > 2 concatenations
// When String is fine:
// - Single concatenation: s1 + s2
// - Compiler optimizes constant expressions
Q3: String pool explained?
// String pool is a special memory area in heap
// Stores unique string literals
String s1 = "hello"; // Created in pool
String s2 = "hello"; // Reuses pool object
String s3 = new String("hello"); // New heap object
// == checks reference (pool vs heap)
s1 == s2; // true (same pool object)
s1 == s3; // false (different objects)
// intern() returns pool reference
String s4 = s3.intern(); // Returns pool reference
s1 == s4; // true
// Java 7+: Pool is part of heap (not PermGen)
// Java 8+: PermGen replaced by Metaspace
Q4: String comparison best practices?
// NEVER use == for content comparison
String a = new String("hello");
String b = new String("hello");
a == b; // false (different objects)
// ALWAYS use equals()
a.equals(b); // true (same content)
// Null-safe comparison
Objects.equals(a, b); // false if either null
// Case-insensitive
a.equalsIgnoreCase(b); // true
// For constants, use == (interned)
String STATUS = "ACTIVE";
if (status == STATUS) { ... } // OK for interned strings
Q5: String, StringBuilder, StringBuffer?
| Feature | String | StringBuilder | StringBuffer |
|---|---|---|---|
| Mutable | No | Yes | Yes |
| Thread-safe | Immutable | No | Yes |
| Performance | Slow (concat) | Fast | Slower (sync) |
| Use case | Constants | Single-thread | Multi-thread |
Exception Questions
Exception Interview Questions
Q1: Checked vs unchecked exceptions?
// Checked: must handle or declare (compile-time)
public void readFile() throws IOException { // Must declare
FileInputStream fis = new FileInputStream("file.txt");
// Compiler forces you to handle IOException
}
// Unchecked: RuntimeException (runtime)
public void divide(int a, int b) {
int result = a / b; // ArithmeticException possible
// Compiler doesn't force handling
}
// Hierarchy:
// Throwable
// ├── Error (OutOfMemoryError, StackOverflowError)
// └── Exception
// ├── IOException, SQLException (checked)
// └── RuntimeException
// ├── NullPointerException
// ├── ArrayIndexOutOfBoundsException
// └── IllegalArgumentException
Q2: try-with-resources?
// Old way - manual close
FileInputStream fis = null;
try {
fis = new FileInputStream("file.txt");
// use resource
} finally {
if (fis != null) fis.close();
}
// Modern way - try-with-resources
try (FileInputStream fis = new FileInputStream("file.txt")) {
// use resource
} // Automatically closed!
// Multiple resources
try (FileInputStream fis = new FileInputStream("in.txt");
FileOutputStream fos = new FileOutputStream("out.txt")) {
// use both resources
} // Both closed in reverse order
// Custom AutoCloseable
class MyResource implements AutoCloseable {
@Override
public void close() {
System.out.println("Resource closed");
}
}
try (MyResource r = new MyResource()) {
// use resource
}
Q3: finally block behavior?
// finally ALWAYS executes (except System.exit())
try {
System.out.println("try");
return;
} finally {
System.out.println("finally");
}
// Output: try, finally
// finally runs even after return!
try {
System.exit(0); // finally does NOT run
} finally {
System.out.println("finally");
}
// Exception in try + exception in finally
try {
throw new RuntimeException("try");
} finally {
throw new RuntimeException("finally");
}
// RuntimeException("finally") is thrown (try exception lost)
Q4: Common exception interview questions?
// Q: Can you have try without catch?
try (Resource r = new Resource()) {
// Yes, with try-with-resources
}
// Q: Can you have finally without catch?
try {
// code
} finally {
// cleanup
}
// Yes!
// Q: What happens with multiple catch blocks?
try {
// code
} catch (IOException e) {
// specific first
} catch (Exception e) {
// general last
} catch (Throwable t) {
// most general last
}
// Order matters! More specific must come first.
Q5: Custom exceptions?
// Checked exception
public class InsufficientFundsException extends Exception {
private double amount;
public InsufficientFundsException(double amount) {
super("Insufficient funds: " + amount);
this.amount = amount;
}
public double getAmount() { return amount; }
}
// Unchecked exception
public class InvalidArgumentException extends RuntimeException {
public InvalidArgumentException(String message) {
super(message);
}
}
// Usage
public void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) throw new InsufficientFundsException(amount);
balance -= amount;
}
Multithreading Questions
Multithreading Interview Questions
Q1: synchronized keyword?
// synchronized method - locks on 'this'
public synchronized void increment() {
count++;
}
// synchronized block - locks on specific object
public void transfer(Account target, int amount) {
synchronized (this) {
this.balance -= amount;
}
synchronized (target) {
target.balance += amount;
}
}
// Static synchronized - locks on Class object
public static synchronized void staticMethod() {
// Only one thread can execute this across all instances
}
Q2: volatile keyword?
// volatile ensures visibility, not atomicity
private volatile boolean running = true;
public void stop() {
running = false; // Visible to other threads immediately
}
public void run() {
while (running) { // Always reads from main memory
// do work
}
}
// volatile does NOT make count++ atomic!
private volatile int count = 0;
public void increment() {
count++; // Still has race condition!
// Need synchronized or AtomicInteger
}
Q3: Deadlocks?
// Deadlock: two threads waiting for each other's locks
Thread 1: lock(A), wait for B
Thread 2: lock(B), wait for A
→ Both waiting forever
// Prevention:
// 1. Lock ordering (same order everywhere)
// 2. tryLock with timeout
// 3. Avoid nested locks
// 4. Lock timeout
// Detection:
// - Thread dumps: jstack <pid>
// - ThreadMXBean.findDeadlockedThreads()
Q4: Thread pools?
// Types:
ExecutorService fixed = Executors.newFixedThreadPool(4);
ExecutorService cached = Executors.newCachedThreadPool();
ExecutorService single = Executors.newSingleThreadExecutor();
ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(4);
// Lifecycle:
executor.submit(task);
executor.shutdown(); // No new tasks
executor.awaitTermination(60, TimeUnit.SECONDS);
executor.shutdownNow(); // Interrupt all
// Callable + Future
Callable<Integer> task = () -> { return 42; };
Future<Integer> future = executor.submit(task);
int result = future.get(); // Blocks
Q5: Thread safety approaches?
// 1. Synchronized
public synchronized void method() { }
// 2. Volatile (visibility only)
private volatile int flag;
// 3. Atomic classes
AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet(); // Atomic
// 4. Lock
ReentrantLock lock = new ReentrantLock();
lock.lock();
try { // critical section } finally { lock.unlock(); }
// 5. Concurrent collections
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
// 6. Immutable objects
final class ImmutablePoint {
final int x, y;
ImmutablePoint(int x, int y) { this.x = x; this.y = y; }
}
Q6: wait() vs sleep() vs join()?
// sleep() - pause current thread
Thread.sleep(1000); // Static method, doesn't release lock
// wait() - release lock, wait for notify
synchronized (obj) {
obj.wait(); // Releases lock on obj
obj.notify(); // Wakes up one waiting thread
}
// join() - wait for thread to finish
Thread t = new Thread(() -> { });
t.start();
t.join(); // Main thread waits for t to complete
Java 8+ Questions
Java 8+ Interview Questions
Q1: Lambda expressions?
// Lambda syntax: (parameters) -> expression or { statements }
// Before Java 8
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello");
}
};
// With lambda
Runnable r = () -> System.out.println("Hello");
// With parameters
Comparator<String> comp = (a, b) -> a.length() - b.length();
// With multiple statements
Function<String, Integer> parser = s -> {
System.out.println("Parsing: " + s);
return Integer.parseInt(s);
};
// Method reference (shorthand for lambda)
List<String> names = Arrays.asList("Alice", "Bob");
names.forEach(System.out::println); // Equivalent to s -> System.out.println(s)
Q2: Stream API?
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// Filter
List<String> longNames = names.stream()
.filter(name -> name.length() > 3)
.collect(Collectors.toList());
// Map
List<Integer> lengths = names.stream()
.map(String::length)
.collect(Collectors.toList());
// Reduce
int totalLength = names.stream()
.map(String::length)
.reduce(0, Integer::sum);
// Collect
Map<Integer, List<String>> byLength = names.stream()
.collect(Collectors.groupingBy(String::length));
// Chaining
String result = names.stream()
.filter(n -> n.length() > 3)
.map(String::toUpperCase)
.sorted()
.collect(Collectors.joining(", "));
// Parallel streams
List<String> parallel = names.parallelStream()
.filter(n -> n.length() > 3)
.collect(Collectors.toList());
Q3: Optional?
// Optional prevents NullPointerException
Optional<String> optional = Optional.ofNullable(getName());
// Common methods
optional.isPresent(); // Check if present
optional.get(); // Get value (throws if empty)
optional.orElse("default"); // Default if empty
optional.orElseThrow(() -> new RuntimeException());
optional.ifPresent(System.out::println);
// Functional style
String name = Optional.ofNullable(user)
.map(User::getName)
.filter(n -> !n.isEmpty())
.orElse("Anonymous");
// In method return types
public Optional<String> findName(int id) {
if (id > 0) return Optional.of("Alice");
return Optional.empty();
}
// Usage
findName(1).ifPresent(System.out::println);
String name = findName(-1).orElse("Unknown");
Q4: Functional interfaces?
// Functional interface: single abstract method
@FunctionalInterface
interface Transformer<T, R> {
R transform(T input);
}
// Built-in functional interfaces:
// Predicate<T>: boolean test(T t)
// Function<T, R>: R apply(T t)
// Consumer<T>: void accept(T t)
// Supplier<T>: T get()
// BiFunction<T, U, R>: R apply(T t, U u)
// Usage
Predicate<Integer> isEven = n -> n % 2 == 0;
Function<String, Integer> length = String::length;
Consumer<String> printer = System.out::println;
Supplier<List<String>> listFactory = ArrayList::new;
Q5: Default methods in interfaces?
interface Vehicle {
void start(); // Abstract
default void honk() { // Default method
System.out.println("Honk!");
}
static void staticMethod() { // Static method
System.out.println("Static");
}
}
class Car implements Vehicle {
@Override
public void start() { System.out.println("Car starts"); }
// honk() inherited from interface
}
// Diamond problem resolution
class A { void method() { } }
interface B { default void method() { } }
interface C { default void method() { } }
class D implements B, C {
@Override
public void method() { // Must override
B.super.method(); // Choose which default
}
}
Design Questions
Design Interview Questions
Q1: Why did you choose HashMap?
// HashMap: O(1) get/put, no ordering
Map<String, User> users = new HashMap<>();
// When: Need fast lookup, don't care about order
// TreeMap: O(log n), sorted keys
Map<String, User> sortedUsers = new TreeMap<>();
// When: Need sorted iteration, range queries
// LinkedHashMap: O(1), insertion order
Map<String, User> orderedUsers = new LinkedHashMap<>();
// When: Need to maintain insertion order
// ConcurrentHashMap: thread-safe
ConcurrentHashMap<String, User> concurrentUsers = new ConcurrentHashMap<>();
// When: Multiple threads access map
Q2: Why ArrayList over LinkedList?
// Almost always ArrayList:
// 1. Cache locality - contiguous memory
// 2. O(1) random access
// 3. Less memory (no pointers)
// 4. Better for iteration
List<String> list = new ArrayList<>(); // Default choice
// LinkedList only when:
// 1. Frequent add/remove at head/tail
// 2. No random access needed
// 3. Implementing queue/deque
// In practice, ArrayList beats LinkedList in most scenarios
Q3: When to use PriorityQueue?
// PriorityQueue: min-heap by default
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
// Max-heap
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
// Use cases:
// 1. Top K elements
// 2. Median finding (two heaps)
// 3. Task scheduling by priority
// 4. Merge K sorted lists
// Top K pattern
PriorityQueue<Integer> topK = new PriorityQueue<>();
for (int num : numbers) {
topK.offer(num);
if (topK.size() > k) topK.poll();
}
// topK contains K largest elements
Q4: String vs StringBuilder?
// String: immutable, thread-safe, slow for concatenation
String s = "";
for (int i = 0; i < 1000; i++) s += i; // O(n²)
// StringBuilder: mutable, not thread-safe, fast
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) sb.append(i); // O(n)
// Rule:
// - Single concatenation: String is fine
// - Loop concatenation: Always StringBuilder
// - Multi-threaded: StringBuffer (synchronized)
Q5: Thread pool sizing?
// CPU-bound tasks
int cpuThreads = Runtime.getRuntime().availableProcessors();
ExecutorService cpuPool = Executors.newFixedThreadPool(cpuThreads);
// IO-bound tasks
int ioThreads = Runtime.getRuntime().availableProcessors() * 2;
ExecutorService ioPool = Executors.newFixedThreadPool(ioThreads);
// General formula:
// CPU-bound: N threads (N = CPU cores)
// IO-bound: N * (1 + wait_time / service_time)
// Never use newCachedThreadPool() in production
// It creates unlimited threads → can crash
Practice Problems
Implement Java Interview Mastery in Java. Include proper error handling and follow Java conventions.
Solution
// Java implementation:
// 1. Proper class structure
// 2. Error handling
// 3. JavaDoc comments
// 4. Unit testsAnalyze the time and space complexity of Java Interview Mastery operations. Optimize for common use cases.
Solution
// Complexity analysis:
// - Time: depends on implementation
// - Space: consider auxiliary space
// - Trade-offs between time and spaceApply Java best practices when using Java Interview Mastery. Consider immutability, thread safety, and clean code.
Solution
// Best practices:
// 1. Use immutable objects where possible
// 2. Thread-safe implementations
// 3. Proper exception handling
// 4. Resource management (try-with-resources)
// 5. JavaDoc documentationQuiz
1. What is the difference between == and equals()?
2. Why are Strings immutable in Java?
3. What is the difference between synchronized and volatile?
4. What does the Stream API provide?
5. When should you use a checked vs unchecked exception?
6. What is the purpose of Optional in Java 8?
7. What is the difference between HashMap and ConcurrentHashMap?
8. What is the diamond problem?
9. What is the difference between Iterator and ListIterator?
10. What is the time complexity of HashMap operations?
Flashcards
Question
What are the 4 principles of OOP?
Click to reveal answer
Answer
Encapsulation (hide internals), Abstraction (show essential), Inheritance (reuse code), Polymorphism (many forms).
Question
What is the difference between abstract class and interface?
Click to reveal answer
Answer
Abstract class: single inheritance, constructors, any access modifiers. Interface: multiple inheritance, no constructors, public by default.
Question
How does HashMap handle collisions?
Click to reveal answer
Answer
Uses linked list (or tree if > 8 entries). Hash to bucket, then linear search within bucket.
Question
Why is String immutable?
Click to reveal answer
Answer
Enables string pool, security (class loading), thread safety, and hash code caching.
Question
What is the difference between checked and unchecked exceptions?
Click to reveal answer
Answer
Checked: must handle (IOException). Unchecked: RuntimeException subclasses (NullPointerException).
Question
What is the volatile keyword?
Click to reveal answer
Answer
Ensures visibility of writes across threads. Does NOT provide atomicity. Use for status flags.
Question
What is a functional interface?
Click to reveal answer
Answer
An interface with exactly one abstract method. Used with lambdas. Examples: Predicate, Function, Consumer.
Question
When should you use ArrayList vs LinkedList?
Click to reveal answer
Answer
ArrayList: random access, iteration. LinkedList: frequent add/remove at head/tail. ArrayList is usually better.
Question
What is the difference between wait() and sleep()?
Click to reveal answer
Answer
wait(): releases lock, waits for notify(). sleep(): pauses thread, doesn't release lock.
Question
What is the equals/hashCode contract?
Click to reveal answer
Answer
Equal objects must have equal hash codes. hashCode must be consistent with equals. Override both together.
Revision Notes
Key Takeaways
- 1.Java is pass-by-value, not pass-by-reference
- 2.Override equals() and hashCode() together
- 3.Strings are immutable for pool, security, and thread safety
- 4.Use try-with-resources for resource management
- 5.volatile ensures visibility, not atomicity
- 6.Prefer ArrayList over LinkedList in most cases
- 7.Use Optional to avoid NullPointerException
- 8.Stream API for functional-style collection processing
Interview Tips
- •Always provide code examples with your answers
- •Explain the 'why' behind Java design decisions
- •Discuss trade-offs between different approaches
- •Mention performance implications
- •Know the time complexity of common operations
- •Be ready to write code on a whiteboard
- •Practice explaining concepts simply
- •Know both the theory and practical usage
Cheat Sheet
Java Interview Cheat Sheet
OOP
- Encapsulation, Abstraction, Inheritance, Polymorphism
- SOLID principles
- Abstract class vs interface
- Composition > Inheritance
Collections
- HashMap: O(1), no order
- TreeMap: O(log n), sorted
- ArrayList: O(1) get, compact
- LinkedList: O(1) add/remove, poor cache
Memory
- Stack: primitives, references
- Heap: objects, GC managed
- String pool: shared literals
- equals/hashCode: override together
Strings
- Immutable: pool, security, thread-safe
- StringBuilder: mutable, fast concat
- == vs equals(): reference vs content
Exceptions
- Checked: must handle
- Unchecked: RuntimeException
- try-with-resources: AutoCloseable
Multithreading
- synchronized: atomicity + visibility
- volatile: visibility only
- Deadlock: lock ordering
- Thread pools: Fixed, Cached, Scheduled
Java 8+
- Lambda: (params) -> expression
- Stream: filter, map, reduce, collect
- Optional: null-safe
- Default methods: interfaces