Skip to main content

Top Java Interview Questions & Answers

A comprehensive collection of the most frequently asked Java interview questions with practical code examples and detailed explanations.


๐Ÿ“‹ Table of Contentsโ€‹

  1. Java Basics
  2. OOP Concepts
  3. String & Memory
  4. Collections Framework
  5. Exception Handling
  6. Multithreading
  7. Java 8+ Features
  8. JVM & Memory Management
  9. Design Patterns
  10. Coding Problems

Java Basicsโ€‹

1. What is Java and its key features?โ€‹

Answer: Java is a platform-independent, object-oriented programming language.

Key Features:

  • Platform Independent: Write once, run anywhere (WORA)
  • Object-Oriented: Encapsulation, Inheritance, Polymorphism, Abstraction
  • Secure: No pointers, bytecode verification, security manager
  • Multithreaded: Built-in support for concurrent programming
  • Automatic Memory Management: Garbage collection
// Platform independence through JVM
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
// Same bytecode runs on Windows, Linux, Mac

2. JDK vs JRE vs JVMโ€‹

Answer:

ComponentDescriptionContains
JVMJava Virtual MachineExecutes bytecode
JREJava Runtime EnvironmentJVM + Libraries
JDKJava Development KitJRE + Development Tools
// JDK provides compiler (javac)
javac HelloWorld.java // Compiles to bytecode

// JRE provides runtime
java HelloWorld // Executes bytecode

// JVM executes the bytecode

3. Primitive vs Reference typesโ€‹

Answer:

// Stored in stack, passed by value
int a = 10;
double b = 3.14;
boolean c = true;
char d = 'A';

// 8 primitive types:
byte, short, int, long, float, double, char, boolean

4. == vs equals() vs hashCode()โ€‹

Answer:

public class EqualsExample {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");

// == compares references
System.out.println(s1 == s2); // true (string pool)
System.out.println(s1 == s3); // false (different objects)

// equals() compares content
System.out.println(s1.equals(s3)); // true

// hashCode() for hash-based collections
System.out.println(s1.hashCode() == s3.hashCode()); // true
}
}

// Custom class example
class Person {
private String name;
private int age;

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && Objects.equals(name, person.name);
}

@Override
public int hashCode() {
return Objects.hash(name, age);
}
}

5. Access modifiers in Javaโ€‹

Answer:

ModifierSame ClassSame PackageSubclassDifferent Package
privateโœ“โœ—โœ—โœ—
defaultโœ“โœ“โœ—โœ—
protectedโœ“โœ“โœ“โœ—
publicโœ“โœ“โœ“โœ“
public class AccessExample {
private int privateVar = 1; // Only within class
int defaultVar = 2; // Within package
protected int protectedVar = 3; // Within package + subclasses
public int publicVar = 4; // Everywhere

private void privateMethod() { }
protected void protectedMethod() { }
public void publicMethod() { }
}

OOP Conceptsโ€‹

6. Encapsulation with exampleโ€‹

Answer: Bundling data and methods together, hiding internal implementation.

public class BankAccount {
private double balance; // Private data
private String accountNumber;

// Public methods to access private data
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}

public boolean withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return true;
}
return false;
}

public double getBalance() {
return balance; // Controlled access
}

// No direct access to balance field
}

7. Inheritance types and exampleโ€‹

Answer:

// Base class
class Animal {
protected String name;

public void eat() {
System.out.println(name + " is eating");
}
}

// Derived class
class Dog extends Animal {
public void bark() {
System.out.println(name + " is barking");
}
}

8. Polymorphism - Method Overloading vs Overridingโ€‹

Answer:

public class Calculator {
// Same method name, different parameters
public int add(int a, int b) {
return a + b;
}

public double add(double a, double b) {
return a + b;
}

public int add(int a, int b, int c) {
return a + b + c;
}

// Compiler decides which method to call
}

9. Abstract class vs Interfaceโ€‹

Answer:

abstract class Vehicle {
protected String brand; // Can have instance variables

public Vehicle(String brand) { // Can have constructor
this.brand = brand;
}

public void start() { // Can have concrete methods
System.out.println("Vehicle starting...");
}

abstract void move(); // Must be implemented by subclass
}

class Car extends Vehicle {
public Car(String brand) {
super(brand);
}

@Override
void move() {
System.out.println("Car is moving on road");
}
}

10. final, finally, finalizeโ€‹

Answer:

// final keyword
final class FinalClass { } // Cannot be extended
class Parent {
final void finalMethod() { } // Cannot be overridden
}
class Child extends Parent {
final int CONSTANT = 10; // Cannot be reassigned
}

// finally block
public void exampleMethod() {
try {
// risky code
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("Exception caught");
} finally {
// Always executes (except System.exit())
System.out.println("Cleanup code");
}
}

// finalize method (deprecated in Java 9+)
class Resource {
@Override
protected void finalize() throws Throwable {
// Called by GC before object destruction
// Not guaranteed to be called
super.finalize();
}
}

String & Memoryโ€‹

11. String, StringBuilder, StringBufferโ€‹

Answer:

String str = "Hello";
str = str + " World"; // Creates new String object
// Original "Hello" becomes eligible for GC

// String pool optimization
String s1 = "Java"; // In string pool
String s2 = "Java"; // Points to same object
String s3 = new String("Java"); // New object in heap

12. Memory areas in JVMโ€‹

Answer:

public class MemoryExample {
static int staticVar = 100; // Method Area

public void method() {
int localVar = 10; // Stack
String str = "Hello"; // String Pool (Heap)
Object obj = new Object(); // Heap

// Stack: method parameters, local variables, method calls
// Heap: objects, instance variables, arrays
// Method Area: class metadata, static variables, constants
}
}

// Memory allocation example
class Person {
String name; // Instance variable (Heap)
static int count; // Static variable (Method Area)

public Person(String name) {
this.name = name; // 'this' reference (Stack)
count++; // Accessing static variable
}
}

13. Garbage Collection basicsโ€‹

Answer:

public class GCExample {
public static void main(String[] args) {
// Object creation
Person p1 = new Person("John");
Person p2 = new Person("Jane");

// Making objects eligible for GC
p1 = null; // No reference to "John" object
p2 = p1; // "Jane" object becomes unreachable

// Suggest GC (not guaranteed)
System.gc();

// Objects with circular references
Node node1 = new Node();
Node node2 = new Node();
node1.next = node2;
node2.next = node1; // Circular reference

node1 = null;
node2 = null; // Both become eligible for GC
}
}

class Node {
Node next;
// GC can handle circular references
}

Collections Frameworkโ€‹

14. List vs Set vs Mapโ€‹

Answer:

List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Apple"); // Duplicates allowed
list.add(1, "Orange"); // Insert at index

// ArrayList vs LinkedList
List<String> arrayList = new ArrayList<>(); // Fast random access
List<String> linkedList = new LinkedList<>(); // Fast insertion/deletion

// Vector (synchronized ArrayList)
List<String> vector = new Vector<>(); // Thread-safe but slower

15. HashMap internal workingโ€‹

Answer:

public class HashMapExample {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();

// Internal process:
// 1. Calculate hash of key: "Apple".hashCode()
// 2. Find bucket: hash % buckets.length
// 3. Handle collision: chaining or open addressing
// 4. Store key-value pair

map.put("Apple", 5); // hash("Apple") -> bucket index
map.put("Banana", 3); // hash("Banana") -> different bucket

// Collision handling
map.put("FB", 1); // Same hash as "Ea"
map.put("Ea", 2); // Collision -> chaining
}
}

// Custom key class
class CustomKey {
private String key;

@Override
public int hashCode() {
return key.hashCode(); // Important for HashMap
}

@Override
public boolean equals(Object obj) {
// Must be consistent with hashCode()
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
CustomKey that = (CustomKey) obj;
return Objects.equals(key, that.key);
}
}

16. Comparable vs Comparatorโ€‹

Answer:

class Student implements Comparable<Student> {
private String name;
private int age;

@Override
public int compareTo(Student other) {
return this.age - other.age; // Sort by age
// return this.name.compareTo(other.name); // Sort by name
}
}

// Usage
List<Student> students = Arrays.asList(
new Student("Alice", 22),
new Student("Bob", 20),
new Student("Charlie", 25)
);
Collections.sort(students); // Uses compareTo()

17. Fail-fast vs Fail-safe iteratorsโ€‹

Answer:

// Fail-fast (ArrayList, HashMap)
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
Iterator<String> iterator = list.iterator();

while (iterator.hasNext()) {
String item = iterator.next();
if ("B".equals(item)) {
list.remove(item); // ConcurrentModificationException
}
}

// Correct way with fail-fast
Iterator<String> safeIterator = list.iterator();
while (safeIterator.hasNext()) {
String item = safeIterator.next();
if ("B".equals(item)) {
safeIterator.remove(); // Safe removal
}
}

// Fail-safe (ConcurrentHashMap, CopyOnWriteArrayList)
List<String> safeList = new CopyOnWriteArrayList<>(Arrays.asList("A", "B", "C"));
for (String item : safeList) {
if ("B".equals(item)) {
safeList.remove(item); // No exception, works on copy
}
}

Exception Handlingโ€‹

18. Checked vs Unchecked exceptionsโ€‹

Answer:

// Must be handled or declared
public void readFile(String filename) throws IOException {
FileReader file = new FileReader(filename); // IOException
// Compiler forces handling
}

// Handle with try-catch
public void handleCheckedException() {
try {
readFile("test.txt");
} catch (IOException e) {
System.out.println("File not found: " + e.getMessage());
}
}

// Common checked exceptions:
// IOException, SQLException, ClassNotFoundException

19. Custom exceptions and best practicesโ€‹

Answer:

// Custom checked exception
class InsufficientFundsException extends Exception {
private double amount;

public InsufficientFundsException(String message, double amount) {
super(message);
this.amount = amount;
}

public double getAmount() { return amount; }
}

// Custom unchecked exception
class InvalidAccountException extends RuntimeException {
public InvalidAccountException(String message) {
super(message);
}
}

// Usage
class BankAccount {
private double balance;

public void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) {
throw new InsufficientFundsException(
"Insufficient funds. Available: " + balance, amount);
}
balance -= amount;
}
}

// Exception handling best practices
public class ExceptionBestPractices {
public void goodPractices() {
try {
// Specific exceptions first
riskyOperation();
} catch (InsufficientFundsException e) {
// Handle specific exception
System.out.println("Insufficient funds: " + e.getAmount());
} catch (InvalidAccountException e) {
// Handle another specific exception
System.out.println("Invalid account: " + e.getMessage());
} catch (Exception e) {
// Generic handler last
System.out.println("Unexpected error: " + e.getMessage());
} finally {
// Cleanup code
closeResources();
}
}

// Try-with-resources (Java 7+)
public void tryWithResources() {
try (FileReader file = new FileReader("test.txt");
BufferedReader reader = new BufferedReader(file)) {
// Resources automatically closed
String line = reader.readLine();
} catch (IOException e) {
System.out.println("File error: " + e.getMessage());
}
}
}

Multithreadingโ€‹

20. Creating threads in Javaโ€‹

Answer:

class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}

// Usage
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
thread1.start();
thread2.start();

21. Synchronization mechanismsโ€‹

Answer:

class Counter {
private int count = 0;

// Synchronized method
public synchronized void increment() {
count++;
}

// Synchronized block
public void decrement() {
synchronized(this) {
count--;
}
}

// Static synchronization
private static int staticCount = 0;
public static synchronized void incrementStatic() {
staticCount++;
}

public int getCount() {
return count;
}
}

// Using locks (more flexible)
class CounterWithLock {
private int count = 0;
private final ReentrantLock lock = new ReentrantLock();

public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock(); // Always unlock in finally
}
}

// Try lock with timeout
public boolean tryIncrement() {
try {
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
count++;
return true;
} finally {
lock.unlock();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return false;
}
}

22. Producer-Consumer problemโ€‹

Answer:

class ProducerConsumer {
private final Queue<Integer> queue = new LinkedList<>();
private final int capacity = 5;
private final Object lock = new Object();

// Producer
public void produce() throws InterruptedException {
int value = 0;
while (true) {
synchronized (lock) {
while (queue.size() == capacity) {
lock.wait(); // Wait if queue is full
}

queue.offer(++value);
System.out.println("Produced: " + value);
lock.notifyAll(); // Notify consumers
}
Thread.sleep(1000);
}
}

// Consumer
public void consume() throws InterruptedException {
while (true) {
synchronized (lock) {
while (queue.isEmpty()) {
lock.wait(); // Wait if queue is empty
}

int value = queue.poll();
System.out.println("Consumed: " + value);
lock.notifyAll(); // Notify producers
}
Thread.sleep(1500);
}
}
}

// Using BlockingQueue (easier)
class ProducerConsumerWithBlockingQueue {
private final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5);

public void produce() throws InterruptedException {
int value = 0;
while (true) {
queue.put(++value); // Blocks if queue is full
System.out.println("Produced: " + value);
Thread.sleep(1000);
}
}

public void consume() throws InterruptedException {
while (true) {
int value = queue.take(); // Blocks if queue is empty
System.out.println("Consumed: " + value);
Thread.sleep(1500);
}
}
}

23. Thread pool and ExecutorServiceโ€‹

Answer:

public class ThreadPoolExample {
public static void main(String[] args) {
// Different types of thread pools
ExecutorService fixedPool = Executors.newFixedThreadPool(3);
ExecutorService cachedPool = Executors.newCachedThreadPool();
ExecutorService singlePool = Executors.newSingleThreadExecutor();
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2);

// Submit tasks
for (int i = 0; i < 10; i++) {
final int taskId = i;
fixedPool.submit(() -> {
System.out.println("Task " + taskId + " executed by " +
Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}

// Scheduled tasks
scheduledPool.schedule(() -> {
System.out.println("Delayed task executed");
}, 5, TimeUnit.SECONDS);

scheduledPool.scheduleAtFixedRate(() -> {
System.out.println("Periodic task executed");
}, 0, 3, TimeUnit.SECONDS);

// Shutdown
fixedPool.shutdown();
try {
if (!fixedPool.awaitTermination(60, TimeUnit.SECONDS)) {
fixedPool.shutdownNow();
}
} catch (InterruptedException e) {
fixedPool.shutdownNow();
}
}
}

// Custom ThreadPoolExecutor
ThreadPoolExecutor customPool = new ThreadPoolExecutor(
2, // Core pool size
4, // Maximum pool size
60L, // Keep alive time
TimeUnit.SECONDS, // Time unit
new LinkedBlockingQueue<>(100), // Work queue
new ThreadPoolExecutor.CallerRunsPolicy() // Rejection policy
);

Java 8+ Featuresโ€‹

24. Lambda expressions and functional interfacesโ€‹

Answer:

// Functional interfaces
@FunctionalInterface
interface Calculator {
int calculate(int a, int b);
}

public class LambdaExample {
public static void main(String[] args) {
// Lambda expressions
Calculator add = (a, b) -> a + b;
Calculator multiply = (a, b) -> a * b;
Calculator subtract = (a, b) -> {
System.out.println("Subtracting " + b + " from " + a);
return a - b;
};

System.out.println(add.calculate(5, 3)); // 8
System.out.println(multiply.calculate(5, 3)); // 15
System.out.println(subtract.calculate(5, 3)); // 2

// Built-in functional interfaces
Predicate<String> isEmpty = String::isEmpty;
Function<String, Integer> length = String::length;
Consumer<String> print = System.out::println;
Supplier<String> supplier = () -> "Hello World";

// Usage
List<String> words = Arrays.asList("hello", "", "world", "");
words.stream()
.filter(isEmpty.negate()) // Remove empty strings
.map(length) // Get lengths
.forEach(System.out::println); // Print lengths
}
}

25. Stream API operationsโ€‹

Answer:

public class StreamExample {
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("Alice", 25, "Engineer"),
new Person("Bob", 30, "Manager"),
new Person("Charlie", 35, "Engineer"),
new Person("Diana", 28, "Designer")
);

// Intermediate operations (lazy)
List<String> engineerNames = people.stream()
.filter(p -> "Engineer".equals(p.getJob())) // Filter
.map(Person::getName) // Transform
.sorted() // Sort
.collect(Collectors.toList()); // Terminal operation

// More stream operations
OptionalDouble avgAge = people.stream()
.mapToInt(Person::getAge)
.average();

Map<String, List<Person>> byJob = people.stream()
.collect(Collectors.groupingBy(Person::getJob));

Map<String, Long> jobCounts = people.stream()
.collect(Collectors.groupingBy(
Person::getJob,
Collectors.counting()
));

// Parallel streams
List<Integer> numbers = IntStream.rangeClosed(1, 1000000)
.boxed()
.collect(Collectors.toList());

long sum = numbers.parallelStream()
.mapToLong(Integer::longValue)
.sum();

// Custom collectors
String names = people.stream()
.map(Person::getName)
.collect(Collectors.joining(", ", "[", "]"));
}
}

26. Optional classโ€‹

Answer:

public class OptionalExample {
public static void main(String[] args) {
// Creating Optional
Optional<String> optional1 = Optional.of("Hello");
Optional<String> optional2 = Optional.ofNullable(null);
Optional<String> optional3 = Optional.empty();

// Checking presence
if (optional1.isPresent()) {
System.out.println(optional1.get());
}

// Better approach
optional1.ifPresent(System.out::println);

// Default values
String value1 = optional2.orElse("Default");
String value2 = optional2.orElseGet(() -> "Computed Default");

// Transformations
Optional<Integer> length = optional1.map(String::length);
Optional<String> upper = optional1
.filter(s -> s.length() > 3)
.map(String::toUpperCase);

// Chaining
String result = findUser("john")
.flatMap(this::findAddress)
.map(Address::getCity)
.orElse("Unknown City");
}

// Methods returning Optional
public Optional<User> findUser(String name) {
// Database lookup
if ("john".equals(name)) {
return Optional.of(new User(name));
}
return Optional.empty();
}

public Optional<Address> findAddress(User user) {
// Address lookup
return Optional.ofNullable(user.getAddress());
}
}

JVM & Memory Managementโ€‹

27. JVM memory structureโ€‹

Answer:

public class MemoryStructure {
static int staticVar = 100; // Method Area (Metaspace in Java 8+)

public void demonstrateMemory() {
int localVar = 10; // Stack
String str = "Hello"; // String Pool (part of Heap)
Object obj = new Object(); // Heap

// Method call creates new stack frame
recursiveMethod(5);
}

public void recursiveMethod(int n) {
if (n <= 0) return;
int local = n; // Each call creates new stack frame
recursiveMethod(n - 1); // Stack grows with each recursive call
}

// Memory areas:
// 1. Heap: Objects, instance variables, arrays
// - Young Generation (Eden, S0, S1)
// - Old Generation (Tenured)
// 2. Stack: Method calls, local variables, partial results
// 3. Method Area/Metaspace: Class metadata, static variables
// 4. PC Register: Current executing instruction
// 5. Native Method Stack: Native method calls
}

28. Garbage collection typesโ€‹

Answer:

public class GCExample {
public static void main(String[] args) {
// Different GC algorithms:

// 1. Serial GC (-XX:+UseSerialGC)
// Single-threaded, suitable for small applications

// 2. Parallel GC (-XX:+UseParallelGC) - Default in Java 8
// Multi-threaded, good for throughput

// 3. G1 GC (-XX:+UseG1GC) - Default in Java 9+
// Low-latency, suitable for large heaps

// 4. ZGC (-XX:+UseZGC) - Java 11+
// Ultra-low latency collector

// Memory leak example
List<String> memoryLeak = new ArrayList<>();
while (true) {
memoryLeak.add("Memory leak " + System.currentTimeMillis());
// This will eventually cause OutOfMemoryError
}
}

// Monitoring GC
public void monitorGC() {
// JVM flags for GC monitoring:
// -XX:+PrintGC
// -XX:+PrintGCDetails
// -XX:+PrintGCTimeStamps
// -Xloggc:gc.log

// Programmatic monitoring
List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean gcBean : gcBeans) {
System.out.println("GC Name: " + gcBean.getName());
System.out.println("Collection Count: " + gcBean.getCollectionCount());
System.out.println("Collection Time: " + gcBean.getCollectionTime() + "ms");
}
}
}

29. Memory leaks and preventionโ€‹

Answer:

public class MemoryLeakExamples {

// 1. Static collections holding references
private static List<Object> staticList = new ArrayList<>();

public void addToStaticList(Object obj) {
staticList.add(obj); // Objects never removed, potential leak
}

// 2. Listeners not removed
public class EventSource {
private List<EventListener> listeners = new ArrayList<>();

public void addListener(EventListener listener) {
listeners.add(listener);
}

// Missing remove method can cause leaks
public void removeListener(EventListener listener) {
listeners.remove(listener);
}
}

// 3. Inner class holding outer class reference
public class OuterClass {
private String data = "Large data";

public class InnerClass {
// Implicitly holds reference to OuterClass
public void doSomething() {
System.out.println("Inner class method");
}
}

// Better: use static inner class
public static class StaticInnerClass {
// No implicit reference to outer class
}
}

// 4. ThreadLocal not cleaned up
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();

public void useThreadLocal() {
threadLocal.set("Some value");
// Should call threadLocal.remove() when done
}

// Prevention strategies
public void preventMemoryLeaks() {
// 1. Use weak references for caches
Map<String, Object> cache = new WeakHashMap<>();

// 2. Properly close resources
try (FileInputStream fis = new FileInputStream("file.txt")) {
// Resource automatically closed
} catch (IOException e) {
e.printStackTrace();
}

// 3. Remove listeners
EventSource source = new EventSource();
EventListener listener = new EventListener() {};
source.addListener(listener);
// Later...
source.removeListener(listener);

// 4. Clean up ThreadLocal
threadLocal.set("value");
try {
// Use threadLocal
} finally {
threadLocal.remove(); // Important!
}
}
}

Design Patternsโ€‹

30. Singleton pattern implementationsโ€‹

Answer:

public class EagerSingleton {
private static final EagerSingleton INSTANCE = new EagerSingleton();

private EagerSingleton() {
// Private constructor
}

public static EagerSingleton getInstance() {
return INSTANCE;
}
}

31. Factory patternโ€‹

Answer:

// Product interface
interface Shape {
void draw();
}

// Concrete products
class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing Circle");
}
}

class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Drawing Rectangle");
}
}

class Triangle implements Shape {
@Override
public void draw() {
System.out.println("Drawing Triangle");
}
}

// Factory class
class ShapeFactory {
public static Shape createShape(String shapeType) {
if (shapeType == null) return null;

switch (shapeType.toUpperCase()) {
case "CIRCLE":
return new Circle();
case "RECTANGLE":
return new Rectangle();
case "TRIANGLE":
return new Triangle();
default:
throw new IllegalArgumentException("Unknown shape: " + shapeType);
}
}
}

// Usage
public class FactoryPatternDemo {
public static void main(String[] args) {
Shape circle = ShapeFactory.createShape("CIRCLE");
Shape rectangle = ShapeFactory.createShape("RECTANGLE");

circle.draw(); // Drawing Circle
rectangle.draw(); // Drawing Rectangle
}
}

32. Observer patternโ€‹

Answer:

import java.util.*;

// Subject interface
interface Subject {
void attach(Observer observer);
void detach(Observer observer);
void notifyObservers();
}

// Observer interface
interface Observer {
void update(String message);
}

// Concrete subject
class NewsAgency implements Subject {
private List<Observer> observers = new ArrayList<>();
private String news;

@Override
public void attach(Observer observer) {
observers.add(observer);
}

@Override
public void detach(Observer observer) {
observers.remove(observer);
}

@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(news);
}
}

public void setNews(String news) {
this.news = news;
notifyObservers();
}
}

// Concrete observers
class NewsChannel implements Observer {
private String name;

public NewsChannel(String name) {
this.name = name;
}

@Override
public void update(String news) {
System.out.println(name + " received news: " + news);
}
}

// Usage
public class ObserverPatternDemo {
public static void main(String[] args) {
NewsAgency agency = new NewsAgency();

NewsChannel cnn = new NewsChannel("CNN");
NewsChannel bbc = new NewsChannel("BBC");

agency.attach(cnn);
agency.attach(bbc);

agency.setNews("Breaking: Java 21 Released!");
// Output:
// CNN received news: Breaking: Java 21 Released!
// BBC received news: Breaking: Java 21 Released!
}
}

Coding Problemsโ€‹

33. Reverse a string without using built-in methodsโ€‹

Answer:

public class StringReverse {

// Method 1: Using char array
public static String reverse1(String str) {
if (str == null || str.length() <= 1) return str;

char[] chars = str.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);
}

// Method 2: Using StringBuilder
public static String reverse2(String str) {
if (str == null || str.length() <= 1) return str;

StringBuilder sb = new StringBuilder();
for (int i = str.length() - 1; i >= 0; i--) {
sb.append(str.charAt(i));
}
return sb.toString();
}

// Method 3: Recursive
public static String reverse3(String str) {
if (str == null || str.length() <= 1) return str;

return reverse3(str.substring(1)) + str.charAt(0);
}

public static void main(String[] args) {
String input = "Hello World";
System.out.println(reverse1(input)); // dlroW olleH
System.out.println(reverse2(input)); // dlroW olleH
System.out.println(reverse3(input)); // dlroW olleH
}
}

34. Find duplicate elements in arrayโ€‹

Answer:

import java.util.*;

public class FindDuplicates {

// Method 1: Using HashSet
public static List<Integer> findDuplicates1(int[] arr) {
Set<Integer> seen = new HashSet<>();
Set<Integer> duplicates = new HashSet<>();

for (int num : arr) {
if (!seen.add(num)) {
duplicates.add(num);
}
}

return new ArrayList<>(duplicates);
}

// Method 2: Using HashMap for count
public static List<Integer> findDuplicates2(int[] arr) {
Map<Integer, Integer> countMap = new HashMap<>();

for (int num : arr) {
countMap.put(num, countMap.getOrDefault(num, 0) + 1);
}

List<Integer> duplicates = new ArrayList<>();
for (Map.Entry<Integer, Integer> entry : countMap.entrySet()) {
if (entry.getValue() > 1) {
duplicates.add(entry.getKey());
}
}

return duplicates;
}

// Method 3: For array with elements 1 to n
public static List<Integer> findDuplicates3(int[] arr) {
List<Integer> duplicates = new ArrayList<>();

for (int i = 0; i < arr.length; i++) {
int index = Math.abs(arr[i]) - 1;
if (arr[index] < 0) {
duplicates.add(Math.abs(arr[i]));
} else {
arr[index] = -arr[index];
}
}

return duplicates;
}

public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 2, 5, 6, 3};
System.out.println(findDuplicates1(arr)); // [2, 3]
System.out.println(findDuplicates2(arr)); // [2, 3]

int[] arr2 = {4, 3, 2, 7, 8, 2, 3, 1};
System.out.println(findDuplicates3(arr2)); // [2, 3]
}
}

35. Implement LRU Cacheโ€‹

Answer:

import java.util.*;

public class LRUCache<K, V> {
private final int capacity;
private final Map<K, Node<K, V>> cache;
private final Node<K, V> head;
private final Node<K, V> tail;

static class Node<K, V> {
K key;
V value;
Node<K, V> prev;
Node<K, V> next;

Node(K key, V value) {
this.key = key;
this.value = value;
}
}

public LRUCache(int capacity) {
this.capacity = capacity;
this.cache = new HashMap<>();

// Create dummy head and tail nodes
this.head = new Node<>(null, null);
this.tail = new Node<>(null, null);
head.next = tail;
tail.prev = head;
}

public V get(K key) {
Node<K, V> node = cache.get(key);
if (node == null) {
return null;
}

// Move to head (most recently used)
moveToHead(node);
return node.value;
}

public void put(K key, V value) {
Node<K, V> node = cache.get(key);

if (node != null) {
// Update existing node
node.value = value;
moveToHead(node);
} else {
// Add new node
Node<K, V> newNode = new Node<>(key, value);

if (cache.size() >= capacity) {
// Remove least recently used (tail)
Node<K, V> tail = removeTail();
cache.remove(tail.key);
}

cache.put(key, newNode);
addToHead(newNode);
}
}

private void addToHead(Node<K, V> node) {
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}

private void removeNode(Node<K, V> node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}

private void moveToHead(Node<K, V> node) {
removeNode(node);
addToHead(node);
}

private Node<K, V> removeTail() {
Node<K, V> lastNode = tail.prev;
removeNode(lastNode);
return lastNode;
}

// Test
public static void main(String[] args) {
LRUCache<Integer, String> cache = new LRUCache<>(2);

cache.put(1, "One");
cache.put(2, "Two");
System.out.println(cache.get(1)); // "One"

cache.put(3, "Three"); // Evicts key 2
System.out.println(cache.get(2)); // null
System.out.println(cache.get(3)); // "Three"
System.out.println(cache.get(1)); // "One"
}
}

36. Binary search implementationโ€‹

Answer:

public class BinarySearch {

// Iterative binary search
public static int binarySearchIterative(int[] arr, int target) {
int left = 0;
int right = arr.length - 1;

while (left <= right) {
int mid = left + (right - left) / 2; // Avoid overflow

if (arr[mid] == target) {
return mid;
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}

return -1; // Not found
}

// Recursive binary search
public static int binarySearchRecursive(int[] arr, int target) {
return binarySearchRecursive(arr, target, 0, arr.length - 1);
}

private static int binarySearchRecursive(int[] arr, int target, int left, int right) {
if (left > right) {
return -1; // Not found
}

int mid = left + (right - left) / 2;

if (arr[mid] == target) {
return mid;
} else if (arr[mid] < target) {
return binarySearchRecursive(arr, target, mid + 1, right);
} else {
return binarySearchRecursive(arr, target, left, mid - 1);
}
}

// Find first occurrence
public static int findFirst(int[] arr, int target) {
int left = 0, right = arr.length - 1;
int result = -1;

while (left <= right) {
int mid = left + (right - left) / 2;

if (arr[mid] == target) {
result = mid;
right = mid - 1; // Continue searching left
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}

return result;
}

// Find last occurrence
public static int findLast(int[] arr, int target) {
int left = 0, right = arr.length - 1;
int result = -1;

while (left <= right) {
int mid = left + (right - left) / 2;

if (arr[mid] == target) {
result = mid;
left = mid + 1; // Continue searching right
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}

return result;
}

public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 4, 4, 5, 6, 7};

System.out.println(binarySearchIterative(arr, 4)); // 3 (any occurrence)
System.out.println(binarySearchRecursive(arr, 4)); // 3 (any occurrence)
System.out.println(findFirst(arr, 4)); // 3 (first occurrence)
System.out.println(findLast(arr, 4)); // 5 (last occurrence)
System.out.println(binarySearchIterative(arr, 8)); // -1 (not found)
}
}

Quick Fire Questionsโ€‹

37. What is the difference between JIT and JVM?โ€‹

Answer: JVM executes bytecode, JIT (Just-In-Time) compiler optimizes frequently used bytecode to native machine code for better performance.

38. Can we override static methods?โ€‹

Answer: No, static methods belong to class, not instance. They can be hidden but not overridden.

39. What is method hiding?โ€‹

Answer: When a subclass defines a static method with same signature as parent class static method.

40. Difference between Heap and Stack memory?โ€‹

Answer:

  • Stack: Method calls, local variables, faster access, limited size
  • Heap: Objects, instance variables, slower access, larger size, garbage collected

41. What is autoboxing and unboxing?โ€‹

Answer:

Integer i = 10;        // Autoboxing (int to Integer)
int j = i; // Unboxing (Integer to int)

42. What is the diamond problem?โ€‹

Answer: Multiple inheritance ambiguity. Java solves it by not allowing multiple class inheritance, only multiple interface inheritance with default methods.

43. What is covariant return type?โ€‹

Answer: Overriding method can return subtype of original return type.

class Parent {
Object getValue() { return new Object(); }
}
class Child extends Parent {
String getValue() { return "Hello"; } // String is subtype of Object
}

44. What is the difference between ArrayList and Vector?โ€‹

Answer:

  • ArrayList: Not synchronized, faster, allows null, introduced in Java 1.2
  • Vector: Synchronized, slower, thread-safe, legacy class from Java 1.0

45. What is the difference between throw and throws?โ€‹

Answer:

  • throw: Used to explicitly throw an exception
  • throws: Used in method signature to declare exceptions that method might throw

Performance Tips & Best Practicesโ€‹

Best Practices:โ€‹

  1. Use StringBuilder for string concatenation in loops
  2. Prefer ArrayList over Vector (unless thread safety needed)
  3. Use enhanced for loops when index not needed
  4. Initialize collections with capacity when size is known
  5. Use primitive collections (TIntList) for better performance
  6. Avoid creating unnecessary objects in loops
  7. Use static imports for frequently used static methods
  8. Implement equals() and hashCode() together
  9. Use try-with-resources for automatic resource management
  10. Prefer composition over inheritance

Common Mistakes to Avoid:โ€‹

  • Using == for string comparison
  • Not overriding equals() and hashCode() together
  • Creating objects in loops unnecessarily
  • Not handling exceptions properly
  • Using raw types instead of generics
  • Memory leaks with listeners and static collections
  • Not making utility classes final with private constructor

Summaryโ€‹

This comprehensive Java interview guide covers:

Core Concepts:โ€‹

  • Java basics, OOP principles
  • Memory management, JVM internals
  • Exception handling strategies

Collections & Data Structures:โ€‹

  • List, Set, Map implementations
  • Custom data structures (LRU Cache)
  • Performance characteristics

Concurrency:โ€‹

  • Thread creation and synchronization
  • Producer-consumer patterns
  • Thread pools and ExecutorService

Modern Java:โ€‹

  • Lambda expressions and streams
  • Optional class usage
  • Functional programming concepts

Problem Solving:โ€‹

  • Algorithm implementations
  • String manipulation
  • Array processing techniques

Practice coding these examples and understand the reasoning behind each solution. Focus on explaining trade-offs and alternative approaches during interviews! ๐Ÿš€