Medium Java Interview Questions
Medium Java Interview Questions
`==`compares object references (memory addresses), while `.equals()` compares the actual
contents of objects.
`ArrayList` is backed by a dynamic array, providing fast random access but slow insertions and
deletions. `LinkedList` is backed by a doubly linked list, offering faster insertions and deletions but
slower access.
`HashMap`is a collection class that stores key-value pairs. It uses a hash table to store elements,
ensuring quick lookups, insertions, and deletions.
`final` can be used for variables (to prevent modification), methods (to prevent overriding), and
classes (to prevent inheritance).
A singleton pattern ensures that a class has only one instance, providing a global point of access. It
is typically implemented by making the constructor private and providing a static method to get
the instance.
An `abstract class` can have both abstract and concrete methods, whereas an `interface` can
only have abstract methods (until Java 8 introduced default and static methods).
Printed using Save ChatGPT as PDF, powered by PDFCrowd HTML to PDF API. 1/13
9. Explain the concept of Java’s garbage collection.
Garbage collection is the automatic process of reclaiming memory by deleting objects that are no
longer in use, helping manage memory efficiently.
`HashMap` stores keys in an unordered way using a hash table, while `TreeMap` stores keys in a
sorted order using a red-black tree.
The `transient` keyword prevents a variable from being serialized when an object is serialized.
12. What is the difference between `synchronized` method and `synchronized` block?
A `synchronized` method locks the entire method, while a `synchronized` block allows locking only
a specific part of the code.
13. Explain the concept of method overloading and method overriding in Java.
Method overloading is defining multiple methods with the same name but different parameters.
Method overriding is providing a specific implementation of a method in a subclass that is already
defined in the superclass.
The `volatile` keyword ensures that a variable’s value is always read from and written to the main
memory, rather than from the thread's cache.
`==` checks reference equality, while `instanceof` checks if an object is an instance of a specific
class or implements an interface.
A `List` allows duplicate elements and maintains insertion order, while a `Set` does not allow
duplicates and may not maintain order (e.g., `HashSet`).
The `Stream API` allows functional-style operations on collections, such as filtering, mapping, and
reducing, making code more concise and readable.
Constructor overloading occurs when a class has more than one constructor with different
parameter lists.
Printed using Save ChatGPT as PDF, powered by PDFCrowd HTML to PDF API. 2/13
`this` refers to the current object, and `super` refers to the superclass of the current object. `this`
is used to access instance variables and methods, while `super` is used to access superclass
methods and constructors.
`default methods` are methods in interfaces with a body. They allow adding new methods to
interfaces without breaking existing implementations.
Reflection allows inspecting and modifying classes, methods, and fields at runtime, providing
flexibility but at the cost of performance and security.
The `equals()` method is used to compare the contents of two objects for equality, typically
overridden in custom classes.
Lambda expressions provide a concise way to represent functional interfaces. They allow writing
cleaner and more readable code, especially when working with functional-style programming (e.g.,
using `Stream`).
`this()`is used to call another constructor in the same class, while `super()` is used to call the
constructor of the superclass.
25. What are `Java annotations` and how are they used?
Annotations are metadata that provide additional information about the code. Examples include
`@Override`, `@Deprecated`, and `@SuppressWarnings`.
Both are used to create mutable strings, but `StringBuffer` is thread-safe while `StringBuilder` is
not, making `StringBuilder` more efficient for single-threaded environments.
Java Collections are frameworks that provide classes and interfaces for storing and manipulating
groups of objects. Examples include `List`, `Set`, `Map`, and their implementations like
`ArrayList`, `HashSet`, `HashMap`.
Printed using Save ChatGPT as PDF, powered by PDFCrowd HTML to PDF API. 3/13
`private`, `default` (package-private), `protected`, and `public`. These modifiers define the
visibility and access control for classes, methods, and fields.
`try` block contains code that might throw exceptions. `catch` block handles exceptions, and
`finally` block executes code after the `try-catch` block, regardless of whether an exception
occurred.
These questions cover a wide range of core Java concepts and would typically be asked in medium-level
interviews.
Threads are an essential concept in Java, enabling multitasking and parallelism. Threads allow you to
perform multiple tasks concurrently within a program. Here’s a comprehensive explanation of threads in
Java, covering various aspects.
1. What is a Thread?
A thread is the smallest unit of a CPU's execution. In Java, a thread is an independent path of execution
within a program, allowing multiple tasks to run simultaneously. Java supports multithreading, which
means multiple threads can run in parallel, improving performance for CPU-bound tasks.
Each thread runs in its own context and has its own memory space (with some shared resources).
Multithreading helps optimize CPU utilization and leads to better performance in multi-core processors.
To create a thread, you can extend the `Thread` class and override its `run()` method. The `run()`
method defines the code that will be executed by the thread.
java
Printed using Save ChatGPT as PDF, powered by PDFCrowd HTML to PDF API. 4/13
The `run()` method contains the code that is executed when the thread runs.
Alternatively, you can create a thread by implementing the `Runnable` interface. This approach allows
the thread class to extend other classes while still being able to define a `run()` method.
java
In this case, we create an instance of `Thread` and pass the `Runnable` object to it.
A thread goes through various stages during its life. The lifecycle of a thread is controlled by the JVM and
the OS. The main thread states are:
4. Thread Methods
Java’s `Thread` class provides several important methods for controlling and managing threads:
Printed using Save ChatGPT as PDF, powered by PDFCrowd HTML to PDF API. 5/13
`yield()`: Suggests that the current thread is willing to yield its remaining time slice to other
threads of equal priority.
5. Thread Synchronization
In multithreading, threads may share resources. If two or more threads try to access and modify a
shared resource at the same time, it can lead to inconsistent or corrupted data. This is known as a race
condition. To avoid race conditions, synchronization is used.
A. Synchronized Methods
A synchronized method ensures that only one thread can execute the method at any given time.
java
class Counter {
private int count = 0;
B. Synchronized Blocks
Synchronized blocks allow you to lock only a specific section of code instead of the entire method.
java
class Counter {
private int count = 0;
C. Locks
Java provides explicit lock mechanisms via the `java.util.concurrent.locks.Lock` interface, including
classes like `ReentrantLock`. These provide more fine-grained control over synchronization.
6. Thread Communication
In multithreading, sometimes threads need to communicate with each other. Java provides several
methods for thread communication:
Printed using Save ChatGPT as PDF, powered by PDFCrowd HTML to PDF API. 6/13
`wait()`: A thread calls `wait()` to release the lock and wait until another thread signals it to
continue.
`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.
These methods are used within synchronized blocks to manage thread coordination.
java
class SharedResource {
synchronized void produce() throws InterruptedException {
wait();
System.out.println("Produced");
}
7. Thread Pools
Creating and managing threads can be resource-intensive. Java provides the `ExecutorService`
framework to manage thread pools efficiently. A thread pool is a collection of threads that can be reused
for executing multiple tasks.
java
import java.util.concurrent.*;
Thread pools allow you to manage a pool of threads for efficient execution, instead of constantly
creating and destroying threads.
8. Daemon Threads
Daemon threads are low-priority threads that run in the background. They do not prevent the JVM from
exiting if all user threads (non-daemon threads) have finished.
Printed using Save ChatGPT as PDF, powered by PDFCrowd HTML to PDF API. 7/13
java
Daemon threads are typically used for tasks such as garbage collection or background services.
Race Conditions: Occur when two or more threads try to access shared resources simultaneously.
Deadlocks: When two or more threads are waiting for each other to release resources, causing
them to get stuck.
Starvation: When a thread is perpetually denied access to resources due to other threads
constantly consuming them.
Livelocks: Similar to deadlocks, but instead of being stuck, threads continuously change state
without making progress.
To avoid these issues, careful thread synchronization and management are required.
Java allows setting priorities for threads. The thread scheduler uses thread priorities to determine the
order in which threads should run. However, thread scheduling depends on the underlying operating
system and may not strictly respect thread priorities.
java
Conclusion
Multithreading in Java allows for efficient utilization of CPU resources by executing tasks concurrently. It
involves managing threads, synchronizing them to avoid conflicts, and using thread pools to optimize
performance. Proper synchronization and thread communication techniques help in building scalable,
concurrent applications. Understanding threads and their lifecycle is key to designing efficient,
responsive applications in Java.
Synchronization in Java
Synchronization is a fundamental concept in Java that helps control access to shared resources by
multiple threads. It ensures that only one thread can access a critical section of code at a time,
preventing race conditions and ensuring the consistency of shared data.
Printed using Save ChatGPT as PDF, powered by PDFCrowd HTML to PDF API. 8/13
In multithreaded programs, threads often operate on shared resources (e.g., variables, objects), and
when multiple threads access the same resource concurrently, it can lead to data inconsistency or
corruption. Synchronization in Java helps to avoid these issues by restricting access to a particular
block of code or method, ensuring thread safety.
When multiple threads access and modify shared data without proper synchronization, it can lead to
problems such as:
Race Conditions: A situation where the outcome of a program depends on the non-deterministic
ordering of threads (e.g., two threads modifying the same data simultaneously).
Data Corruption: Without synchronization, multiple threads might read/write to the same variable
concurrently, resulting in unpredictable or corrupted results.
Synchronization ensures that only one thread can access the critical section (a block of code or method)
at a time, thus avoiding these issues.
2. Types of Synchronization
A. Synchronized Methods
A method can be synchronized using the `synchronized` keyword, which ensures that only one thread
can execute that method at a time on a particular object instance.
java
class Counter {
private int count = 0;
// Synchronized method
public synchronized void increment() {
count++;
}
How It Works: When a thread calls a synchronized method, it acquires a lock (or monitor) on the
object. This means that no other thread can enter any synchronized method on the same object
until the first thread releases the lock.
B. Synchronized Blocks
Instead of synchronizing an entire method, you can synchronize only specific blocks of code. This is
useful when you need to synchronize a small part of the code to reduce the performance overhead of
locking larger methods.
Printed using Save ChatGPT as PDF, powered by PDFCrowd HTML to PDF API. 9/13
java
class Counter {
private int count = 0;
How It Works: The `synchronized` block locks the specified object (in this case, `this` refers to the
current instance). Only one thread can execute the synchronized block at a time for the given
object.
C. Static Synchronization
When you synchronize a static method, it locks on the class object (not an instance of the class). This
ensures that only one thread can access any synchronized static method at a time, even if multiple
instances of the class exist.
java
class MyClass {
private static int count = 0;
How It Works: The lock in this case is on the `Class` object, meaning no other thread can access
any synchronized static method in the `MyClass` class at the same time.
In Java, every object has an implicit lock associated with it, called a monitor. When a thread enters a
synchronized method or block, it acquires the lock on the object. Other threads trying to enter
synchronized methods/blocks of the same object will be blocked until the lock is released.
If a method or block is synchronized, the thread that enters it automatically acquires the monitor
of the object.
When the thread finishes executing the synchronized code, it releases the lock.
Printed using Save ChatGPT as PDF, powered by PDFCrowd HTML to PDF API. 10/13
If another thread is waiting for the lock, it will acquire it once the current thread releases it.
The `synchronized` keyword helps achieve thread safety by ensuring that only one thread can access
the critical section of the code at a time. It guarantees mutual exclusion, which means that only one
thread can access the shared resource at a given point in time, thus preventing race conditions.
However, synchronization comes with a performance overhead due to the locking and unlocking
process, so it should be used judiciously.
5. Deadlock in Synchronization
A deadlock is a situation where two or more threads are blocked forever because they are each waiting
for the other to release a resource. This typically happens when two threads acquire locks on two
different objects and then each thread tries to acquire the lock held by the other.
Example of a Deadlock:
java
class A {
synchronized void methodA(B b) {
b.last();
}
class B {
synchronized void methodB(A a) {
a.last();
}
In this example, Thread 1 holds the lock on object `A` and waits to acquire the lock on object `B`, while
Thread 2 holds the lock on object `B` and waits to acquire the lock on object `A`. As a result, both
threads are in a state of deadlock.
Solution: To avoid deadlocks:
Printed using Save ChatGPT as PDF, powered by PDFCrowd HTML to PDF API. 11/13
6. Reentrant Locks and the `Lock` Interface
The `Lock` interface in Java, introduced in the `java.util.concurrent.locks` package, offers more
sophisticated locking mechanisms than the `synchronized` keyword. A major advantage of using `Lock`
is that it provides better flexibility, including features like try-lock, timed-lock, and interruptible lock
acquisition.
java
import java.util.concurrent.locks.ReentrantLock;
class Counter {
private int count = 0;
private final ReentrantLock lock = new ReentrantLock();
Reentrant Lock: The `ReentrantLock` allows a thread that already holds the lock to re-enter the
lock without blocking itself. This is useful in situations where a thread might need to acquire the
same lock multiple times.
While the `synchronized` keyword is used for mutual exclusion (locking), the `volatile` keyword is used
to ensure that changes to a variable are immediately visible to all threads. It does not provide mutual
exclusion but is useful when you want to guarantee that the latest value of a variable is always read by
other threads.
java
class SharedResource {
private volatile boolean flag = false;
Printed using Save ChatGPT as PDF, powered by PDFCrowd HTML to PDF API. 12/13
Volatile ensures that changes made to the `flag` variable are visible across all threads
immediately.
Thread Safety: Synchronization helps achieve thread safety by ensuring that only one thread can
access a critical section of code at a time.
Performance Overhead: Synchronization introduces some performance overhead due to locking
and unlocking. It's essential to synchronize only critical sections of code.
Deadlocks: If not used carefully, synchronization can lead to deadlocks. Always ensure a proper
order when acquiring locks.
Alternatives: Using `ReentrantLock` and other mechanisms like `ReadWriteLock` can provide more
flexibility than `synchronized` methods/blocks.
Conclusion
Synchronization is crucial for ensuring thread safety in Java, especially when dealing with shared
resources. While the `synchronized` keyword provides a straightforward way to manage concurrent
access to code, it can introduce performance overhead. In cases where more advanced locking
techniques are needed, Java offers explicit locks such as `ReentrantLock` that give greater control over
the synchronization process.
Printed using Save ChatGPT as PDF, powered by PDFCrowd HTML to PDF API. 13/13