Java Interview Questions and Answers

Prepare for your Java job interview with confidence! Explore a comprehensive collection of Java interview questions and answers covering essential topics such as object-oriented programming, data structures, concurrency, exception handling, and more.

Detailed Java Interview Questions and Answers

  1. What are the main features of Java?
    • Answer: Java features include simplicity, object-oriented nature, portability, robustness, security, multithreading capability, and high performance through Just-In-Time compilation.
  2. Explain the concept of OOP and its principles in Java.
    • Answer: OOP principles in Java include:
      • Encapsulation: Bundling data and methods that operate on the data within a single unit (class).
public class Person {
    private String name;  // Encapsulated field
    
    public String getName() {  // Public method to access the field
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
}

Abstraction: Hiding complex implementation details and showing only necessary features.

abstract class Animal {
    abstract void makeSound();  // Abstract method
}

class Dog extends Animal {
    void makeSound() {
        System.out.println("Bark");
    }
}

Inheritance: A new class inherits properties and behavior from an existing class.

class Animal {
    void eat() {
        System.out.println("This animal eats food");
    }
}

class Dog extends Animal {
    void bark() {
        System.out.println("Bark");
    }
}

Polymorphism: Methods do different things based on the object it is acting upon.

Animal myDog = new Dog();
myDog.makeSound();  // Outputs: Bark

3. What is the difference between JDK, JRE, and JVM?

  • JDK (Java Development Kit): Contains tools for developing Java applications (JRE, compiler, debugger).
  • JRE (Java Runtime Environment): Runs Java applications, includes JVM and standard libraries.
  • JVM (Java Virtual Machine): Executes Java bytecode and provides a runtime environment.

4. Describe the memory management in Java.

Java uses automatic memory management with garbage collection. Memory is divided into heap (for objects) and stack (for method calls and local variables).

5. What is the Java Memory Model?

The Java Memory Model defines how threads interact through memory, ensuring visibility, ordering, and atomicity of shared variables.

6. How does garbage collection work in Java?

Garbage collection automatically frees memory by removing objects that are no longer referenced. Algorithms include mark-and-sweep and generational collection.

7. What are the different types of references in Java?

  • Strong: Default type, prevents garbage collection.
  • Soft: Used for caches, collected before OutOfMemoryError.
  • Weak: Used for canonicalizing mappings, collected eagerly.
  • Phantom: Used for cleanup actions, collected after finalization.

8. Explain the finalize() method.

The finalize() method is called by the garbage collector before an object is collected. It’s used to clean up resources but is deprecated due to unpredictability.

9. What is the difference between == and equals() in Java?

  • == compares reference identity.
  • equals() compares object content.
String a = new String("hello");
String b = new String("hello");
System.out.println(a == b);  // false
System.out.println(a.equals(b));  // true

10. What is the hashCode() method? How is it related to equals()?

The hashCode() method returns an integer hash code for the object. If two objects are equal (equals() returns true), they must have the same hash code to ensure correct functioning in hash-based collections.

public class Person {
    private String name;

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

    @Override
    public int hashCode() {
        return name.hashCode();
    }
}

11. Explain the use of the volatile keyword.

The volatile keyword ensures that the value of a variable is always read from main memory, not from a thread’s local cache. It guarantees visibility of changes to variables across threads.

private volatile boolean flag = true;

12. What are the differences between wait() and sleep()?

  • wait(): Causes the current thread to release the monitor lock and wait until another thread invokes notify() or notifyAll() on the same object.
  • sleep(): Causes the current thread to pause execution for a specified time without releasing the monitor lock.
synchronized (obj) {
    obj.wait();  // releases the lock on obj
}

Thread.sleep(1000);  // pauses the current thread for 1 second

13. What is the difference between notify() and notifyAll()?

  • notify(): Wakes up a single thread that is waiting on the object’s monitor.
  • notifyAll(): Wakes up all threads that are waiting on the object’s monitor.
synchronized (obj) {
    obj.notify();  // wakes up one waiting thread
}

synchronized (obj) {
    obj.notifyAll();  // wakes up all waiting threads
}

14. What is a deadlock? How can it be avoided?

A deadlock occurs when two or more threads are blocked forever, each waiting for the other to release a resource. It can be avoided by acquiring locks in a consistent order and using timeout for lock acquisition.

// Avoiding deadlock by acquiring locks in the same order
synchronized (lock1) {
    synchronized (lock2) {
        // critical section
    }
}

15. What are the different types of thread pools in Java?

  • FixedThreadPool: A fixed number of threads.
  • CachedThreadPool: Creates new threads as needed and reuses existing ones.
  • SingleThreadExecutor: A single worker thread.
  • ScheduledThreadPool: A pool that can schedule commands to run after a delay or periodically.
ExecutorService fixedPool = Executors.newFixedThreadPool(10);
ExecutorService cachedPool = Executors.newCachedThreadPool();
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(5);

16. Explain the use of the Callable and Future interfaces.

Callable is similar to Runnable but can return a result and throw a checked exception. Future represents the result of an asynchronous computation, allowing us to retrieve the result once the computation is complete.

Callable<Integer> task = () -> {
    return 123;
};

ExecutorService executor = Executors.newFixedThreadPool(1);
Future<Integer> future = executor.submit(task);

Integer result = future.get();  // returns 123

Collections Framework

17. What is the Java Collections Framework?

The Java Collections Framework provides a set of interfaces (List, Set, Map) and implementations (ArrayList, HashSet, HashMap) for managing groups of objects.

18. Explain the difference between ArrayList and LinkedList.

  • ArrayList: Uses a dynamic array, fast random access, slow insertions/deletions.
  • LinkedList: Uses a doubly-linked list, slower access, fast insertions/deletions.
List<String> arrayList = new ArrayList<>();
List<String> linkedList = new LinkedList<>();

19. How does HashMap work internally?

HashMap uses an array of buckets, each bucket containing a linked list or a tree. The key’s hash code determines the bucket index. Collisions are resolved by chaining (linked list) or tree (if many elements).

Map<String, Integer> map = new HashMap<>();
map.put("key", 1);

20. What is the difference between HashSet and TreeSet?

  • HashSet: Uses HashMap, no order, constant-time performance.
  • TreeSet: Uses TreeMap, maintains sorted order, log-time performance.
Set<String> hashSet = new HashSet<>();
Set<String> treeSet = new TreeSet<>();

21. What is the difference between Comparable and Comparator?

  • Comparable: Defines natural ordering within the class by implementing compareTo().
  • Comparator: Defines custom ordering outside the class by implementing compare().
class Person implements Comparable<Person> {
    private String name;

    @Override
    public int compareTo(Person other) {
        return this.name.compareTo(other.name);
    }
}

class PersonNameComparator implements Comparator<Person> {
    @Override
    public int compare(Person p1, Person p2) {
        return p1.name.compareTo(p2.name);
    }
}

22. What is the use of the Collections utility class?

The Collections class provides static methods for manipulating collections, such as sorting, searching, and shuffling.

List<String> list = new ArrayList<>(Arrays.asList("b", "c", "a"));
Collections.sort(list);  // sorts the list

23. Explain the Iterator interface.

The Iterator interface provides methods to iterate over a collection (hasNext(), next(), remove()).

List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

24. What is the difference between Iterator and ListIterator?

  • Iterator allows traversing elements in one direction.
  • ListIterator extends Iterator and allows bi-directional traversal and modification of elements.
List<String> list = new ArrayList<>();
ListIterator<String> listIterator = list.listIterator();

25. What is the LinkedHashMap class?

LinkedHashMap maintains a doubly-linked list of its entries, preserving insertion order or access order. It extends HashMap.

LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("one", 1);

26. What is the PriorityQueue class?

PriorityQueue is a queue that orders its elements according to their natural ordering or by a specified comparator. The head of the queue is the least element.

PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
priorityQueue.add(3);
priorityQueue.add(1);
priorityQueue.add(2);
System.out.println(priorityQueue.poll());  // Outputs: 1

27. How does the ConcurrentHashMap class work?

ConcurrentHashMap allows concurrent read and write operations by dividing the map into segments and locking only the affected segment during updates.

ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("key", 1);

28. What is the TreeMap class?

TreeMap is a NavigableMap implementation that uses a Red-Black tree. It orders its elements based on their natural ordering or by a specified comparator.

TreeMap<String, Integer> treeMap = new TreeMap<>();
treeMap.put("b", 2);
treeMap.put("a", 1);

29. What is the difference between HashMap and TreeMap?

HashMap provides constant-time performance for basic operations but does not maintain any order. TreeMap provides log-time performance and maintains its elements in sorted order.

HashMap<String, Integer> hashMap = new HashMap<>();
TreeMap<String, Integer> treeMap = new TreeMap<>();

30. How does the WeakHashMap class work?

WeakHashMap uses weak references for its keys, allowing them to be garbage-collected if there are no strong references. It is useful for implementing canonicalizing mappings.

WeakHashMap<String, Integer> weakHashMap = new WeakHashMap<>();

31. Explain the CopyOnWriteArrayList class.

CopyOnWriteArrayList is a thread-safe variant of ArrayList where all mutative operations (add, set, etc.) are implemented by making a fresh copy of the underlying array.

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

32. What is the Deque interface?

Deque (Double Ended Queue) is an interface that extends Queue and allows elements to be added or removed from both ends.

Deque<String> deque = new ArrayDeque<>();
deque.addFirst("first");
deque.addLast("last");

33. Explain the BlockingQueue interface.

BlockingQueue is a queue that supports operations that wait for the queue to become non-empty when retrieving and waiting for space to become available when storing. It’s useful in producer-consumer scenarios.

BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(10);

34. What is the difference between Iterator and ListIterator?

  • Iterator allows traversing elements in one direction.
  • ListIterator extends Iterator and allows bi-directional traversal and modification of elements.
List<String> list = new ArrayList<>();
ListIterator<String> listIterator = list.listIterator();

Concurrency and Multithreading

35. What is a Thread in Java?

A Thread is a lightweight process that can execute code concurrently with other threads within the same application.

Thread thread = new Thread(() -> System.out.println("Hello from a thread"));
thread.start();

36. What is the Runnable interface?

Runnable represents a task that can be executed by a thread. It has a single method run().

Runnable task = () -> System.out.println("Task is running");
Thread thread = new Thread(task);
thread.start();

37. What is the Callable interface?

Callable is similar to Runnable but can return a result and throw a checked exception.

Callable<Integer> task = () -> 123;

38. Explain synchronized methods and blocks.

Synchronization ensures that only one thread can execute a block of code at a time, preventing data inconsistency.

public synchronized void synchronizedMethod() {
    // synchronized code
}

public void method() {
    synchronized(this) {
        // synchronized block
    }
}

39. What are thread states in Java?

A thread can be in one of several states: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, and TERMINATED

40. What is the ExecutorService?

ExecutorService is a high-level replacement for working with threads directly. It manages a pool of worker threads, allowing you to submit tasks for execution.

ExecutorService executor = Executors.newFixedThreadPool(10);
executor.submit(() -> System.out.println("Task executed"));
executor.shutdown();

41. What is the difference between submit() and execute() methods in ExecutorService?

  • execute(): Executes a Runnable task but does not return a result.
  • submit(): Submits a Runnable or Callable task and returns a Future representing the task’s result.
ExecutorService executor = Executors.newFixedThreadPool(1);
executor.execute(() -> System.out.println("Runnable executed"));
Future<Integer> future = executor.submit(() -> 123);

42. What is a CountDownLatch?

CountDownLatch is a synchronization aid that allows one or more threads to wait until a set of operations in other threads completes.

CountDownLatch latch = new CountDownLatch(3);

Runnable task = () -> {
    System.out.println("Task completed");
    latch.countDown();
};

new Thread(task).start();
latch.await();  // Main thread waits until the count reaches zero

43. What is a CyclicBarrier?

CyclicBarrier is a synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.

CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("All tasks completed"));

Runnable task = () -> {
    System.out.println("Task executed");
    barrier.await();
};

new Thread(task).start();

44. Explain ReentrantLock and its usage.

ReentrantLock is a mutual exclusion lock with the same basic behavior as the implicit monitors accessed using synchronized blocks but with extended capabilities. It allows for more flexible locking operations and is useful in advanced concurrency scenarios.

ReentrantLock lock = new ReentrantLock();
lock.lock();  // Acquires the lock
try {
    // Critical section
} finally {
    lock.unlock();  // Releases the lock
}

45. What is a Semaphore?

Semaphore is a synchronization primitive that restricts the number of threads that can access a resource concurrently. It maintains a set of permits to control access.

Java Interview Questions and Answers

46. What is a BlockingQueue?

BlockingQueue is a queue that supports operations that wait for the queue to become non-empty when retrieving and wait for space to become available when storing. It’s useful in producer-consumer scenario

BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(10);

47. Explain the ThreadLocal class.

ThreadLocal provides thread-local variables, allowing each thread to have its own independently initialized instance of the variable. It’s typically used to store per-thread context or avoid synchronization.

private static final ThreadLocal<Integer> threadId = ThreadLocal.withInitial(() -> Thread.currentThread().getId());

public static int getThreadId() {
    return threadId.get();
}

48. What is the difference between start() and run() methods of the Thread class?

  • start(): Creates a new thread and starts its execution. It calls the run() method internally.
  • run(): Entry point for the thread’s execution. It should be overridden to define the task to be performed by the thread.
Thread thread = new Thread(() -> System.out.println("Hello from a thread"));
thread.start();  // Calls run() internally

49. What is a Future in Java concurrency?

Future represents the result of an asynchronous computation. It provides methods to check if the computation is complete, retrieve the result, or cancel the task.

ExecutorService executor = Executors.newFixedThreadPool(1);
Future<Integer> future = executor.submit(() -> 123);
Integer result = future.get();  // Waits for the computation to complete and retrieves the result

50. What is the CompletableFuture class?

CompletableFuture is a Future that may be explicitly completed (setting its value and status), enabling further control over the asynchronous computation.

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
future.thenApply(s -> s + " World").thenAccept(System.out::println);

Leave a Comment