0% found this document useful (0 votes)
3 views158 pages

Interview

The document provides an in-depth overview of core Java concepts, focusing on Object-Oriented Programming principles such as encapsulation, abstraction, inheritance, and polymorphism. It explains the differences between method overloading and overriding, the roles of JVM, JDK, and JRE, and details about memory management including stack vs heap memory and garbage collection. Additionally, it discusses the immutability of Strings and the implications of memory leaks in Java.

Uploaded by

anjalivs.dev
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views158 pages

Interview

The document provides an in-depth overview of core Java concepts, focusing on Object-Oriented Programming principles such as encapsulation, abstraction, inheritance, and polymorphism. It explains the differences between method overloading and overriding, the roles of JVM, JDK, and JRE, and details about memory management including stack vs heap memory and garbage collection. Additionally, it discusses the immutability of Strings and the implications of memory leaks in Java.

Uploaded by

anjalivs.dev
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

CORE JAVA (VERY HIGH FREQUENCY)

OOPS

1. What are the four pillars of OOP?

The four pillars of Object-Oriented Programming are:

1. Encapsulation
2. Abstraction
3. Inheritance
4. Polymorphism

Encapsulation

Encapsulation means binding data and methods that operate on that data into a single unit, typically a
class, and restricting direct access to some of the object's components.

In Java, we achieve this using:

 private fields
 public getters and setters

Example:

class BankAccount {
private double balance; // data hidden

public void deposit(double amount) {


if (amount > 0) {
balance += amount;
}
}

public double getBalance() {


return balance;
}
}

Here:

 The balance field is not directly accessible.


 We control how it is modified through methods.

Why important?

 Protects data integrity.


 Prevents invalid states.
 Improves maintainability.
Abstraction

Abstraction means hiding implementation details and exposing only essential functionality.

In Java, we achieve this using:

 abstract classes
 interfaces

interface Payment {
void pay(double amount);
}

The user of Payment does not know:

 Whether it is UPI
 Credit card
 Net banking

They only know: pay().

Why important?

 Reduces complexity.
 Allows flexibility.
 Encourages loose coupling.

Inheritance

Inheritance means one class acquires properties and behavior of another class.

In Java, we use extends.

class Vehicle {
void start() {
[Link]("Vehicle starting");
}
}

class Car extends Vehicle {


void drive() {
[Link]("Car driving");
}
}

Why important?

 Code reuse.
 Logical hierarchy.
 Supports polymorphism.
Polymorphism

Polymorphism means one interface, multiple implementations.

There are two types:

[Link]-time (Method Overloading)

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


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

[Link] (Method Overriding)

class Animal {
void sound() {
[Link]("Animal sound");
}
}

class Dog extends Animal {


@Override
void sound() {
[Link]("Bark");
}
}

2. What is abstraction vs encapsulation?

Abstraction and Encapsulation are both OOP principles, but they solve different problems.

Abstraction is about hiding implementation details and exposing only the essential functionality to the
user.
It focuses on what an object does, not how it does it.

In Java, abstraction is achieved using interfaces and abstract classes.


For example, if I use a Payment interface with a pay() method, the user only knows they can call pay(), but
they don’t know whether the implementation is UPI, credit card, or net banking.

Encapsulation, on the other hand, is about binding data and methods together and restricting direct access
to the object’s internal state.
It focuses on data protection.

In Java, encapsulation is achieved using private fields and public getters/setters.


For example, if a class has a private balance variable, it cannot be modified directly from outside the class. It
can only be changed through controlled methods like deposit().
In short:
Abstraction hides implementation complexity,
Encapsulation hides data and internal state.

3. What is method overloading vs overriding?

Method Overloading and Method Overriding are two ways to achieve polymorphism in Java, but they
differ in when and how they are resolved.

Method Overloading

Method overloading means defining multiple methods with the same name in the same class, but with
different parameter lists.

The difference must be in:

 Number of parameters, or
 Type of parameters, or
 Order of parameters

Return type alone cannot differentiate overloaded methods.

Overloading is resolved at compile time, so it is also called compile-time polymorphism.

Method Overriding

Method overriding means a subclass provides a specific implementation of a method that is already defined
in its parent class.

Conditions:

 Same method name


 Same parameter list
 Same or covariant return type
 Cannot reduce visibility
 Parent method must not be final, static, or private

Overriding is resolved at runtime, so it is called runtime polymorphism.

4. What is runtime polymorphism?

5. What is composition vs inheritance?


Composition and Inheritance are two ways to establish a relationship between classes, but they represent
different types of relationships and design approaches.

Inheritance

Inheritance represents an “is-a” relationship.

A subclass inherits properties and behavior from a parent class using the extends keyword.

It promotes code reuse but creates tight coupling between parent and child

Composition

Composition represents a “has-a” relationship.

One class contains an object of another class as a member variable.

It promotes loose coupling and is generally preferred over inheritance in many real-world designs.

6. What is the difference between abstract class and interface?

Abstract class and Interface are both used to achieve abstraction in Java

Abstract Class

An abstract class is a class that cannot be instantiated and may contain:

 Abstract methods (without body)


 Concrete methods (with implementation)
 Instance variables
 Constructors

It is used when there is a common base class with shared state or behavior.

Interface

An interface defines a contract that classes must implement.

It contains:

 Abstract methods (by default public and abstract)


 Default and static methods (since Java 8)
 Only public static final variables (constants)

It does not support instance variables or constructors.


A class can implement multiple interfaces, which allows multiple inheritance of behavior.

Key Differences

 Abstract class can have state (instance variables); interface cannot.


 Abstract class can have constructors; interface cannot.
 A class can extend only one abstract class.
 A class can implement multiple interfaces.
 Abstract class is used for shared base functionality.
 Interface is used to define a contract

7. What is multiple inheritance in Java?

Multiple inheritance means a class inheriting from more than one parent class.

In Java, multiple inheritance of classes is not supported, meaning a class cannot extend more than one class.

Why Java does not support multiple inheritance of classes?

Because of the Diamond Problem.

If two parent classes have a method with the same name, and the child inherits both, the compiler will not know
which method to execute.

How Java achieves multiple inheritance?

Java supports multiple inheritance through interfaces.

In summary:
Java does not support multiple inheritance of classes to avoid the diamond problem, but it achieves multiple
inheritance using interfaces.

JVM & Memory

8. What is JVM?
JVM stands for Java Virtual Machine.

It is a part of the Java Runtime Environment (JRE) that is responsible for executing Java bytecode and
converting it into machine-level instructions for the underlying operating system.

In simple terms, JVM enables the principle of “Write Once, Run Anywhere

JVM is the runtime engine that executes Java bytecode, manages memory, and ensures platform independence.

9. Difference between JDK, JRE, JVM?

JDK, JRE, and JVM are three different components of the Java ecosystem, each serving a specific
purpose.

1. JVM (Java Virtual Machine)

JVM is the runtime engine that executes Java bytecode.

Its responsibilities include:

 Loading class files


 Verifying bytecode
 Managing memory (Heap, Stack, etc.)
 Garbage collection
 Executing bytecode using Interpreter or JIT

It ensures platform independence by converting bytecode into machine-specific instructions.

2. JRE (Java Runtime Environment)

JRE provides the environment required to run Java applications.

It contains:

 JVM
 Core libraries (like [Link], [Link], etc.)
 Supporting files required at runtime

JRE is used only for running Java applications, not for developing them.

3. JDK (Java Development Kit)


JDK is used for developing Java applications.

It contains:

 JRE
 Development tools such as:
o javac (compiler)
o javadoc
o jdb (debugger)
o Other development utilities

JDK allows you to write, compile, and run Java programs.

10. Explain JVM architecture.

JVM architecture defines how the Java Virtual Machine loads, stores, and executes Java programs.

It mainly consists of three major components:

1. Class Loader Subsystem


2. Runtime Data Areas (Memory Areas)
3. Execution Engine

1. Class Loader Subsystem

The Class Loader is responsible for loading .class files into memory.

It works in three phases:

a) Loading

 Loads bytecode into memory.

b) Linking

 Verification → Ensures bytecode is valid and secure.


 Preparation → Allocates memory for static variables.
 Resolution → Replaces symbolic references with actual memory references.

c) Initialization

 Assigns actual values to static variables.


 Executes static blocks.

There are three main class loaders:


 Bootstrap ClassLoader
 Extension (Platform) ClassLoader
 Application ClassLoader

2. Runtime Data Areas (Memory Structure)

JVM memory is divided into the following areas:

1) Method Area

 Stores class metadata


 Static variables
 Runtime constant pool

2) Heap Area

 Stores objects and instance variables


 Shared across all threads
 Managed by Garbage Collector

3) Stack Area (per thread)

 Stores method calls


 Local variables
 Partial results
 Each method call creates a stack frame

4) PC Register (per thread)

 Stores address of currently executing instruction

5) Native Method Stack

 Used for native methods written in C/C++

3. Execution Engine

The Execution Engine executes bytecode.

It has:

Interpreter

 Executes bytecode line by line


 Slower but starts quickly

JIT (Just-In-Time Compiler)


 Converts frequently executed bytecode into native machine code
 Improves performance

Garbage Collector

 Automatically removes unused objects from heap memory


 Prevents memory leaks

Flow of Execution

1. Source code is compiled into bytecode.


2. Class Loader loads the class.
3. Memory is allocated in runtime data areas.
4. Execution Engine executes bytecode.
5. Garbage Collector cleans unused objects.

In summary:
JVM architecture consists of the Class Loader, Memory Areas, and Execution Engine, which together load,
manage, and execute Java programs efficiently and securely.

11. What is Heap vs Stack memory?

Heap and Stack are two different memory areas in JVM used for different purposes

Stack Memory

Stack memory is used for:

 Method execution
 Local variables
 Method parameters
 Stack frames

Each thread has its own stack.

Whenever a method is called, a new stack frame is created.


When the method completes, that stack frame is automatically removed.

Stack memory follows LIFO (Last In, First Out) order.

Stack memory is:

 Faster access
 Automatically managed
 Limited in size
 Not shared between threads

Heap Memory

Heap memory is used for:

 Objects
 Instance variables
 Arrays

Heap is shared across all threads.

Objects created using new keyword are stored in heap memory.

Heap memory is:

 Larger than stack


 Shared among threads
 Managed by Garbage Collector
 Slower compared to stack

12. What is Metaspace?

Metaspace is a memory area in the JVM where class metadata is stored.

It was introduced in Java 8, replacing the older PermGen (Permanent Generation) space.

What is stored in Metaspace?

 Class metadata (class structure information)


 Method metadata
 Static variables
 Runtime constant pool

It stores information about the class definitions, not the objects themselves.
Objects are stored in the Heap.

13. What causes memory leak in Java?

A memory leak in Java occurs when objects that are no longer needed are still referenced, so the
Garbage Collector cannot remove them from the heap.
Even though Java has automatic garbage collection, memory leaks can still happen due to improper object
reference management.

Main Causes of Memory Leak in Java

1. Unused Objects Still Referenced

If an object is still reachable through a reference, the Garbage Collector will not remove it.

Example:

List<String> list = new ArrayList<>();


while (true) {
[Link]("data");
}

Here, objects keep getting added and never removed, so memory keeps increasing.

2. Static Fields Holding References

Static variables live for the entire lifetime of the application.

If a static collection holds references to objects that are no longer needed, they will never be garbage collected.

3. Unclosed Resources

If we don’t close:

 Database connections
 File streams
 Network sockets

They may lead to memory or resource leaks.

Example:

FileInputStream fis = new FileInputStream("[Link]");


// not closing fis

4. Improper Use of Caches

If cache size is not limited, it can grow indefinitely and cause memory issues.
5. Listener or Callback References

If listeners are registered but never removed, they keep objects alive in memory.

Important Point

Garbage Collector removes only objects that are unreachable.

If an object is still reachable, even if logically unused, it will not be removed.

In short:
Memory leaks in Java occur when unused objects remain referenced, preventing Garbage Collection and
gradually consuming heap memory.

Garbage Collection

14. What is Garbage Collection?

Garbage Collection in Java is the automatic process of identifying and removing objects from heap
memory that are no longer reachable, in order to free up memory.

It is handled by the JVM, so the developer does not need to manually deallocate memory like in C or C++.

Why Garbage Collection is Needed

When we create objects using new, memory is allocated in the heap.

If unused objects are not removed:

 Heap memory fills up


 Application performance degrades
 It may result in OutOfMemoryError

Garbage Collector ensures memory is reused efficiently.

How Garbage Collection Works

The Garbage Collector uses the concept of reachability.


An object is eligible for GC if:

 It is no longer referenced by any active part of the application.


 It is not reachable from GC roots (like stack references, static variables, etc.).

If an object is unreachable, it becomes eligible for garbage collection.

15. What are minor GC and major GC?

16. What are different GC algorithms?

17. Can we force GC?

Yes, we can request Garbage Collection in Java, but we cannot force it.

We can suggest the JVM to perform GC using:

[Link]();

or

[Link]().gc();

However, this is only a request, not a guarantee.


The JVM may ignore it based on its internal GC algorithm and memory conditions.

Why can’t we force GC?

Garbage Collection is fully controlled by the JVM.


It decides:

 When to run GC
 Which algorithm to use
 How much memory to reclaim

Forcing GC manually can negatively impact performance because it may trigger unnecessary stop-the-world
pauses.

Best Practice

We should:
 Avoid calling [Link]() in production code.
 Let JVM manage memory automatically.
 Fix memory leaks by removing unnecessary references instead of trying to force GC.

In short:
We can request Garbage Collection using [Link](), but we cannot force it, as the JVM has full control over
when GC actually runs.

Strings

19. Why is String immutable?

String is immutable in Java, meaning once a String object is created, its value cannot be changed.

There are several important reasons why Java designers made String immutable:

1. Security

Strings are heavily used in sensitive areas like:

 Database URLs
 File paths
 Network connections
 Class loading

If String were mutable, its value could be changed after validation, which could lead to security vulnerabilities.

Immutability ensures that once a String is created and validated, it cannot be altered.

2. String Pool Optimization

Java uses a String Constant Pool to optimize memory.

Example:

String s1 = "Hello";
String s2 = "Hello";

Both s1 and s2 refer to the same object in the pool.

If Strings were mutable, changing one reference would affect others, breaking the pooling concept.
Immutability allows safe sharing of String objects.

3. Thread Safety

Since Strings cannot be modified, they are inherently thread-safe.

Multiple threads can use the same String object without synchronization.

4. HashCode Caching

Strings are commonly used as keys in HashMap.

Because String is immutable:

 Its hashcode does not change.


 JVM can cache the hashcode for performance.
 It ensures consistent behavior in hash-based collections.

If String were mutable and its value changed, the hashcode would change, breaking hash-based collections.

In summary:

String is immutable in Java for:

 Security
 Memory optimization (String Pool)
 Thread safety
 Reliable hashing behavior

That’s why immutability is a critical design decision for the String class.

20. What is String pool?

String Pool is a special memory area inside the Heap where Java stores string literals to optimize
memory usage.

It is also called the String Constant Pool.

Why String Pool exists


Since String objects are immutable, multiple references can safely point to the same String object.

Instead of creating a new object every time, JVM reuses existing String literals from the pool to save memory.

Example
String s1 = "Hello";
String s2 = "Hello";

Here:

 Only one object is created in the String Pool.


 Both s1 and s2 refer to the same object.

So:

s1 == s2 // true

What if we use new keyword?


String s3 = new String("Hello");

In this case:

 A new object is created in heap memory.


 The literal "Hello" may still exist in the String Pool.
 s3 points to a different object.

So:

s1 == s3 // false

How to add to String Pool manually?

We can use:

[Link]();

intern() returns the reference from the String Pool if it exists.

Where is String Pool located?

It is stored in the Heap memory (since Java 7).


Before Java 7, it was stored in PermGen.
In summary:
String Pool is a special area in heap memory where Java stores string literals to enable memory optimization
and reuse of immutable String objects.

21. Difference between String, StringBuilder, StringBuffer?

String, StringBuilder, and StringBuffer are all used to represent and manipulate character sequences in
Java, but they differ in mutability and thread-safety.

1. String

 Immutable
 Once created, its value cannot be changed
 Stored in String Pool (for literals)
 Thread-safe because it is immutable

Example:

String s = "Hello";
s = s + " World";

Here, a new object is created.


The original object remains unchanged.

Use case:
When the value does not need to change frequently.

2. StringBuilder

 Mutable
 Not thread-safe
 Faster than StringBuffer
 Introduced in Java 5

Example:

StringBuilder sb = new StringBuilder("Hello");


[Link](" World");

Here, the same object is modified.


No new object is created.

Use case:
When frequent modifications are needed in a single-threaded environment.
3. StringBuffer

 Mutable
 Thread-safe
 Methods are synchronized
 Slower than StringBuilder due to synchronization

Example:

StringBuffer sb = new StringBuffer("Hello");


[Link](" World");

Use case:
When multiple threads modify the same string object.

Key Differences

 String → Immutable, thread-safe


 StringBuilder → Mutable, not thread-safe, faster
 StringBuffer → Mutable, thread-safe, slower

In summary:
Use String when data is constant.
Use StringBuilder for frequent modifications in single-threaded applications.
Use StringBuffer when thread safety is required.

22. What does intern() do?

The intern() method is used to move a String object to the String Pool or return the reference of the
existing pooled string if it already exists.

In simple terms, intern() ensures that the string reference points to the String Constant Pool.

Why is it needed?

When we create a String using new, it is stored in the heap, not in the String Pool.

Example:

String s1 = new String("Hello");


String s2 = "Hello";
Here:

 s1 is created in heap memory.


 "Hello" literal is stored in the String Pool.
 s1 == s2 → false (different references)

Using intern()
String s3 = [Link]();

Now:

 s3 will refer to the String Pool object.


 If "Hello" already exists in the pool, it returns that reference.
 If not, it adds it to the pool and returns it.

So:

s3 == s2 // true

Key Points

 intern() returns the pooled reference.


 It helps in memory optimization.
 Useful when handling a large number of duplicate strings.

In summary:
The intern() method ensures that a String object refers to the unique instance stored in the String Pool.

23. Why is String used as HashMap key?

String is commonly used as a key in HashMap because it is immutable, properly implements equals()
and hashCode(), and provides reliable hashing behavior.

There are three main reasons:

1. Immutability

String is immutable, meaning its value cannot change after creation.


In a HashMap, the key’s hashCode() is used to determine the bucket location.

If a key were mutable and its value changed after insertion:

 Its hashCode would change


 The object would go to a different bucket
 The map would not be able to retrieve it correctly

Since String is immutable, its hashCode remains constant, ensuring stable behavior.

2. Proper Implementation of equals() and hashCode()

String class overrides both:

 equals() → compares actual content


 hashCode() → computed based on characters

This ensures:

 Two Strings with the same content are treated as equal keys
 Hash-based collections work correctly

Example:

Map<String, String> map = new HashMap<>();


[Link]("name", "Anjali");

[Link]("name"); // Works correctly

3. HashCode Caching (Performance)

String caches its hashCode internally.

Once computed, it stores the hash value and reuses it, which improves performance in hash-based collections
like HashMap.

In summary:

String is ideal as a HashMap key because it is:

 Immutable
 Correctly implements equals() and hashCode()
 Efficient due to hashCode caching

That makes it safe and reliable for hash-based data structures.


Collections (EXTREMELY IMPORTANT)

24. Difference between List, Set, Map?

List, Set, and Map are core collection interfaces in Java, but they differ in how they store and manage data.

1. List

 Stores elements in an ordered sequence


 Allows duplicate elements
 Allows null values
 Elements are accessed by index

Common implementations:

 ArrayList
 LinkedList
 Vector

Example:

List<String> list = new ArrayList<>();


[Link]("A");
[Link]("A");

Duplicates are allowed, and insertion order is maintained.

2. Set

 Stores unique elements (no duplicates)


 Does not allow duplicate values
 May or may not maintain order depending on implementation

Common implementations:

 HashSet → No order
 LinkedHashSet → Maintains insertion order
 TreeSet → Sorted order

Example:

Set<String> set = new HashSet<>();


[Link]("A");
[Link]("A"); // Duplicate ignored

3. Map
 Stores key-value pairs
 Keys must be unique
 Values can be duplicated
 Does not extend Collection interface

Common implementations:

 HashMap
 LinkedHashMap
 TreeMap
 Hashtable

Example:

Map<String, Integer> map = new HashMap<>();


[Link]("A", 1);

Here:

 Keys are unique


 Values can repeat

Key Differences

 List → Ordered, allows duplicates


 Set → No duplicates
 Map → Key-value pairs, unique keys

In summary:
Use List when order and duplicates matter.
Use Set when uniqueness matters.
Use Map when you need key-value mapping.

25. How does HashMap work internally?

HashMap stores data in key-value pairs and works internally using a hash table. It uses hashing to
determine where to store each key-value entry in memory.

Step 1: Hashing

When we insert a key-value pair:


[Link]("name", "Anjali");

1. HashMap calls hashCode() on the key.


2. The hash is transformed into an index using:
3. index = hash & (n - 1)

where n is the current capacity of the internal array.

This determines the bucket location.

Step 2: Bucket Storage

Internally, HashMap maintains an array of buckets.

Each bucket can store:

 A single node, or
 A linked list of nodes (if collisions happen), or
 A balanced tree (Red-Black Tree) if too many collisions occur (Java 8+).

Step 3: Collision Handling

If two keys produce the same index, it is called a collision.

HashMap handles collisions using:

 Linked List (before Java 8)


 Linked List → converted to Red-Black Tree if bucket size exceeds 8 (Java 8+)

Tree conversion improves worst-case lookup from O(n) to O(log n).

Step 4: Retrieval

When we call:

[Link]("name");

1. HashMap computes hashCode of the key.


2. Finds the bucket index.
3. Compares keys using equals() to locate the exact entry.

Both hashCode() and equals() are used.


Step 5: Resizing

Default initial capacity = 16


Default load factor = 0.75

When size exceeds:

capacity × load factor

HashMap resizes (doubles capacity) and rehashes entries.

Time Complexity

 Average case → O(1)


 Worst case → O(n) (before Java 8)
 Worst case → O(log n) (Java 8+, due to tree conversion)

Important Points

 Not thread-safe
 Allows one null key
 Allows multiple null values

In summary:
HashMap works by computing the key’s hashCode, mapping it to a bucket index, handling collisions using
linked lists or trees, and using equals() for exact key matching.

26. What is load factor?

Load factor in HashMap defines how full the hash table is allowed to get before it is resized.

It is a measure of how much data can be stored relative to the current capacity.

Default Value

The default load factor in HashMap is 0.75.


How It Works

Resize happens when:

current size ≥ capacity × load factor

For example:

 Default capacity = 16
 Load factor = 0.75

Threshold =
16 × 0.75 = 12

So when the 13th element is inserted, the HashMap resizes (capacity becomes 32).

Why 0.75?

 Lower load factor → fewer collisions → more memory usage


 Higher load factor → more collisions → better memory usage but slower performance

0.75 provides a good balance between performance and memory efficiency.

After Resizing

 Capacity doubles
 All existing entries are rehashed
 New bucket indexes are recalculated

Resizing is an expensive operation, so choosing an appropriate initial capacity helps performance.

In summary:
Load factor determines when a HashMap should resize.
It controls the trade-off between memory usage and lookup performance.

27. What happens when hashCode() collisions occur?

A hashCode collision occurs when two different keys produce the same hash value, resulting in the same
bucket index in a HashMap.
HashMap handles collisions using a structured approach.

Step 1: Same Bucket

When a collision occurs:

1. HashMap calculates the bucket index using the key’s hashCode().


2. If that bucket is already occupied, it does not overwrite.
3. It stores the new entry in the same bucket.

Step 2: Collision Handling Mechanism

Before Java 8:

 Colliding entries are stored in a Linked List.


 New nodes are added to the list.
 Lookup becomes O(n) in worst case.

Java 8 and later:

 If the number of nodes in a bucket exceeds 8,


the linked list is converted into a Red-Black Tree.
 This improves worst-case lookup from O(n) to O(log n).

If the size later drops below 6, it may convert back to a linked list.

Step 3: Retrieval During Collision

When get(key) is called:

1. HashMap finds the bucket index.


2. It traverses the linked list or tree.
3. It uses equals() to compare keys.
4. When match is found, value is returned.

Important:
hashCode() decides the bucket.
equals() decides the exact key match.

Example

If two different objects return the same hashCode:


[Link]() == [Link]()

But:

[Link](key2) == false

They will be stored in the same bucket but as separate entries.

Performance Impact

 Few collisions → O(1) average


 Many collisions → O(n) (pre-Java 8)
 Many collisions → O(log n) (Java 8+ with tree conversion)

In summary:
When hashCode collisions occur, HashMap stores multiple entries in the same bucket using a linked list or a
tree and uses equals() to differentiate between keys

28. Difference between HashMap and ConcurrentHashMap?

HashMap and ConcurrentHashMap both store key-value pairs, but they differ mainly in thread safety
and internal concurrency handling.

1. Thread Safety

HashMap

 Not thread-safe
 If multiple threads modify it simultaneously, it may lead to data inconsistency
 Requires external synchronization

ConcurrentHashMap

 Thread-safe
 Designed for concurrent access without locking the entire map

2. Locking Mechanism

HashMap
 No internal synchronization

ConcurrentHashMap

 Java 7 → Used segment-level locking


 Java 8+ → Uses finer-grained locking (CAS + synchronized on buckets)
 Allows multiple threads to read and write simultaneously on different buckets

This improves performance in multi-threaded environments.

3. Null Keys and Values

HashMap

 Allows one null key


 Allows multiple null values

ConcurrentHashMap

 Does NOT allow null keys


 Does NOT allow null values

This prevents ambiguity in concurrent environments.

4. Performance

 In single-threaded environments → HashMap is slightly faster


 In multi-threaded environments → ConcurrentHashMap performs much better due to internal
concurrency control

5. Iteration Behavior

HashMap

 Fail-fast iterator (throws ConcurrentModificationException)

ConcurrentHashMap

 Fail-safe / weakly consistent iterator


 Does not throw ConcurrentModificationException
 Reflects changes made during iteration (not guaranteed to show all updates)

In summary:
 Use HashMap in single-threaded scenarios.
 Use ConcurrentHashMap in multi-threaded environments where high concurrency is required without
full synchronization.

29. Difference between ArrayList and LinkedList?

ArrayList and LinkedList are both implementations of the List interface, but they differ in their internal
data structure and performance characteristics.

1. Internal Data Structure

ArrayList

 Backed by a dynamic array


 Elements are stored in contiguous memory locations

LinkedList

 Implemented as a doubly linked list


 Each element (node) stores:
o Data
o Reference to next node
o Reference to previous node

2. Access Time

ArrayList

 Random access is fast → O(1)


 Because elements are index-based

LinkedList

 Access by index is slow → O(n)


 Because it must traverse from head or tail

3. Insertion and Deletion

ArrayList

 Insertion/deletion in middle → O(n)


 Because elements need to be shifted
LinkedList

 Insertion/deletion at beginning or middle → O(1) (if node reference is available)


 No shifting required

4. Memory Usage

ArrayList

 Less memory overhead


 Stores only data

LinkedList

 More memory overhead


 Stores extra references (next and previous pointers)

5. Use Case

 Use ArrayList when:


o Frequent reads
o Random access required
o Fewer insertions/deletions in middle
 Use LinkedList when:
o Frequent insertions/deletions
o Less need for random access

In summary:

ArrayList is better for fast random access and read-heavy operations.


LinkedList is better for frequent insertions and deletions

30. What is fail-fast vs fail-safe iterator?

Fail-fast and fail-safe iterators define how a collection behaves when it is modified while being iterated.

Fail-Fast Iterator

A fail-fast iterator throws a ConcurrentModificationException if the collection is structurally modified after


the iterator is created.
Structural modification means:

 Adding
 Removing
 Updating structure (not just changing value)

Example:

List<String> list = new ArrayList<>();


[Link]("A");

Iterator<String> it = [Link]();
[Link]("B"); // structural modification

[Link](); // throws ConcurrentModificationException

Internally, fail-fast iterators use a modCount variable.

 If modCount changes during iteration,


 The iterator detects it and throws exception.

Collections like:

 ArrayList
 HashMap
 HashSet

use fail-fast iterators.

Important:
Fail-fast does not guarantee exception, but it detects modification on best-effort basis.

Fail-Safe Iterator

Fail-safe iterator does not throw exception if the collection is modified during iteration.

Instead:

 It works on a copy of the collection.


 Changes made after iterator creation may or may not be reflected.

Example:

 ConcurrentHashMap
 CopyOnWriteArrayList

These are used in concurrent environments.


Key Differences

Fail-Fast:

 Throws ConcurrentModificationException
 Works on original collection
 Used in non-concurrent collections

Fail-Safe:

 No exception
 Works on cloned or snapshot copy
 Used in concurrent collections

In summary:
Fail-fast iterator fails immediately if collection is modified during iteration, whereas fail-safe iterator operates
on a copy and allows modification without throwing exception.

31. Why must we override equals() and hashCode() together?

We must override equals() and hashCode() together to maintain the general contract defined in the
Java Object class, especially when using hash-based collections like HashMap or HashSet.

The Contract Rule

The contract says:

1. If two objects are equal according to equals(),


then they must return the same hashCode().
2. If two objects have the same hashCode(),
they are not necessarily equal.

Why This Is Important

Hash-based collections work in two steps:

1. Use hashCode() to find the bucket.


2. Use equals() to compare keys inside the bucket.

If we override equals() but not hashCode():

 Two logically equal objects may return different hash codes.


 They will go to different buckets.
 The collection will treat them as different keys.
 Lookup and duplicate detection will fail.

Example
class Student {
int id;

@Override
public boolean equals(Object o) {
Student s = (Student) o;
return [Link] == [Link];
}
}

If hashCode() is not overridden:

 Two students with same id may not be treated as equal in a HashSet.


 It breaks collection behavior.

Correct Implementation

If equals() is overridden, hashCode() must also be overridden using the same fields used in equals
comparison.

In summary:

We override both equals() and hashCode() together to ensure consistent behavior in hash-based collections
and to satisfy the Java contract that equal objects must have equal hash codes.

Exception Handling

32. What is checked vs unchecked exception?

Checked and Unchecked exceptions are two categories of exceptions in Java based on when they are
checked by the compiler.

1. Checked Exceptions

Checked exceptions are checked at compile time.


This means the compiler forces us to either:

 Handle the exception using try-catch, or


 Declare it using throws

They are subclasses of Exception (but not RuntimeException).

Examples:

 IOException
 SQLException
 ClassNotFoundException

Example:

FileReader fr = new FileReader("[Link]"); // must handle IOException

If not handled, compilation fails.

Checked exceptions usually represent recoverable conditions like file not found or database issues.

2. Unchecked Exceptions

Unchecked exceptions are not checked at compile time.

They occur at runtime and are subclasses of RuntimeException.

Examples:

 NullPointerException
 ArithmeticException
 ArrayIndexOutOfBoundsException
 IllegalArgumentException

Example:

int x = 10 / 0; // ArithmeticException at runtime

The compiler does not force handling.

Unchecked exceptions usually represent programming errors.

Key Differences

Checked Exception:

 Checked at compile time


 Must be handled or declared
 Represent recoverable conditions

Unchecked Exception:

 Occur at runtime
 Not mandatory to handle
 Represent programming mistakes

In summary:
Checked exceptions are compile-time enforced and must be handled, while unchecked exceptions occur at
runtime and are not required to be explicitly handled.

33. What is try-with-resources?

Try-with-resources is a feature introduced in Java 7 that automatically closes resources after execution,
eliminating the need for explicit finally blocks.

It is used for resources that implement the AutoCloseable interface.

Examples of such resources:

 File streams
 Database connections
 BufferedReader
 Scanner

Syntax
try (FileReader fr = new FileReader("[Link]")) {
// use the resource
} catch (IOException e) {
[Link]();
}

After the try block completes, the FileReader is automatically closed.

No need for:

finally {
[Link]();
}

Why It Is Important
Before Java 7:

 We had to manually close resources in a finally block.


 If closing threw an exception, it could suppress the original exception.

Try-with-resources:

 Automatically closes resources


 Reduces boilerplate code
 Prevents resource leaks
 Handles suppressed exceptions properly

Multiple Resources

We can declare multiple resources separated by semicolons:

try (BufferedReader br = new BufferedReader(new FileReader("[Link]"));


FileWriter fw = new FileWriter("[Link]")) {
}

Resources are closed in reverse order.

In summary:

Try-with-resources automatically manages and closes resources that implement AutoCloseable, improving code
safety and preventing resource leaks.

34. What is finally block?

The finally block is a part of exception handling in Java that is always executed after the try and catch
blocks, regardless of whether an exception occurs or not.

It is mainly used for cleanup activities.

Purpose of finally

The finally block is typically used to:

 Close resources
 Release database connections
 Close file streams
 Perform cleanup operations
Example
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
[Link]("Exception occurred");
} finally {
[Link]("Finally block executed");
}

Even though an exception occurs, the finally block will still execute.

Important Points

 finally executes whether:


o Exception occurs
o Exception does not occur
o Exception is handled or not handled
 It will not execute only in rare cases like:
o JVM crash
o [Link]() call

Modern Alternative

For resource management, try-with-resources is preferred over finally because it handles resource closing
automatically.

In summary:
The finally block is used to execute cleanup code that must run regardless of whether an exception occurs or
not.

35. How to create custom exception?

A custom exception in Java is created by extending either Exception (for checked exceptions) or
RuntimeException (for unchecked exceptions).

Step 1: Create the Custom Exception Class

If I want a checked exception, I extend Exception:


public class InvalidAgeException extends Exception {

public InvalidAgeException(String message) {


super(message);
}
}

If I want an unchecked exception, I extend RuntimeException:

public class InvalidAgeException extends RuntimeException {

public InvalidAgeException(String message) {


super(message);
}
}

Step 2: Throw the Custom Exception


public class Test {

public static void validateAge(int age) throws InvalidAgeException {


if (age < 18) {
throw new InvalidAgeException("Age must be 18 or above");
}
}

public static void main(String[] args) {


try {
validateAge(15);
} catch (InvalidAgeException e) {
[Link]([Link]());
}
}
}

When to Use Custom Exceptions

 When built-in exceptions do not clearly represent the business error.


 When you want meaningful, domain-specific error handling.
 To improve readability and maintainability.

In summary:

To create a custom exception, extend Exception or RuntimeException, define constructors, and throw it using
the throw keyword when required.

36. What happens if exception occurs in finally?


If an exception occurs inside the finally block, it overrides any exception that was thrown in the try or
catch block.

That means the exception from the finally block will propagate, and the original exception will be lost (unless
handled properly).

Example
try {
int x = 10 / 0; // ArithmeticException
} finally {
throw new RuntimeException("Exception in finally");
}

In this case:

 ArithmeticException occurs in the try block.


 But the RuntimeException thrown in finally overrides it.
 The caller will only see:
RuntimeException: Exception in finally

The original ArithmeticException is suppressed.

Why This Is Dangerous

 It hides the real root cause.


 Makes debugging difficult.
 Can lead to unexpected behavior.

How Java 7+ Handles This (Try-with-resources)

In try-with-resources:

 If both try and close() throw exceptions,


 The original exception is preserved.
 The second exception is added as a suppressed exception.

We can access suppressed exceptions using:

[Link]();

Best Practice

 Avoid throwing exceptions from finally.


 Use finally only for cleanup logic.
 Prefer try-with-resources for resource handling.

In summary:
If an exception occurs in finally, it overrides any previous exception and becomes the one that propagates,
which can hide the original error.

Multithreading (HIGH IMPACT)

37. What is thread lifecycle?

The thread lifecycle in Java defines the different states a thread goes through from creation to
termination.

A thread in Java goes through the following states:

1. New

 When a thread object is created using new Thread().


 The thread is not yet started.
 It has not begun execution.

Example:

Thread t = new Thread();

At this point, the thread is in the New state.

2. Runnable

 When we call start(), the thread moves to the Runnable state.


 It is ready to run and waiting for CPU scheduling.
 The actual execution depends on the thread scheduler.

[Link]();

3. Running

 When the thread scheduler selects the thread, it enters the Running state.
 The run() method is being executed.
Note:
Java does not explicitly distinguish Runnable and Running in [Link], but conceptually they are
different.

4. Blocked / Waiting / Timed Waiting

A thread moves to these states when it is temporarily inactive.

Blocked

 Waiting to acquire a monitor lock.

Waiting

 Waiting indefinitely for another thread to perform a specific action.


 Example: wait(), join() (without timeout)

Timed Waiting

 Waiting for a specified time.


 Example: sleep(), wait(timeout), join(timeout)

5. Terminated (Dead)

 When the run() method completes.


 Or if the thread ends due to an uncaught exception.
 The thread cannot be restarted.

In summary:

The thread lifecycle states are:

New → Runnable → Running → Blocked/Waiting → Terminated

These states represent how a thread moves from creation to completion during execution.

38. Difference between Runnable and Callable?

Runnable and Callable are both interfaces used to create tasks that can be executed by a thread, but they
differ in return type and exception handling.
1. Runnable

 Introduced in Java 1.0


 Method: run()
 Does NOT return any result
 Cannot throw checked exceptions

Runnable task = () -> {


[Link]("Running task");
};

Used with:

 Thread class
 ExecutorService

2. Callable

 Introduced in Java 5 (with concurrency framework)


 Method: call()
 Returns a result
 Can throw checked exceptions

Callable<Integer> task = () -> {


return 10 + 20;
};

Callable is used with:

 ExecutorService
 Returns result via Future

Example:

ExecutorService service = [Link]();


Future<Integer> future = [Link](task);

Integer result = [Link](); // gets returned value

Key Differences

Runnable:

 run() method
 No return value
 Cannot throw checked exception

Callable:
 call() method
 Returns a value
 Can throw checked exception

In summary:

Use Runnable when no result is needed.


Use Callable when you need a result or want to handle checked exceptions in concurrent tasks.

39. What is synchronization?

Synchronization in Java is a mechanism used to control access to shared resources in a multi-threaded


environment, so that only one thread can access a critical section at a time.

It helps prevent issues like:

 Race conditions
 Data inconsistency
 Thread interference

Why Synchronization Is Needed

When multiple threads modify shared data simultaneously, the result may become inconsistent.

Example:

class Counter {
int count = 0;

void increment() {
count++;
}
}

If two threads call increment() at the same time, the final value may be incorrect due to race conditions.

How Synchronization Works

Java uses an intrinsic lock (monitor lock) associated with every object.

When a thread enters a synchronized block or method:


 It acquires the lock.
 Other threads must wait until the lock is released.

Synchronized Method
synchronized void increment() {
count++;
}

Here, the lock is on the current object (this).

Synchronized Block
void increment() {
synchronized (this) {
count++;
}
}

We can also synchronize on any specific object:

synchronized (lockObject) {
// critical section
}

Key Points

 Synchronization ensures mutual exclusion.


 It affects performance because only one thread can execute the synchronized block at a time.
 It works at object level, not at method level.

In summary:

Synchronization is a mechanism that ensures only one thread accesses shared resources at a time to maintain
data consistency in multi-threaded applications.

40. What is volatile?

volatile is a keyword in Java used to ensure visibility of a variable across multiple threads.

It guarantees that:
 Changes made by one thread to a variable
 Are immediately visible to other threads

Why volatile Is Needed

In a multi-threaded environment:

Each thread may cache variables locally.

If one thread updates a variable, other threads may still see the old cached value.

volatile prevents this by forcing:

 Reads and writes directly from main memory


 Not from thread-local cache

Example
class Shared {
volatile boolean flag = false;
}

If one thread sets:

flag = true;

Other threads will immediately see the updated value.

What volatile Guarantees

1. Visibility
2. Prevents instruction reordering (provides memory ordering guarantees)

What volatile Does NOT Guarantee

 It does NOT provide atomicity.

Example:

volatile int count = 0;


count++;

count++ is not atomic because it involves:


 Read
 Increment
 Write

For atomic operations, we use:

 synchronized
 AtomicInteger

When to Use volatile

 When a variable is shared between threads


 When only one thread writes and others read
 For simple state flags

In summary:

volatile ensures visibility of shared variables across threads but does not guarantee atomicity or mutual
exclusion.

41. What is deadlock?

Deadlock is a situation in multi-threading where two or more threads are permanently blocked because
each thread is waiting for a resource held by another thread.

As a result, none of the threads can proceed.

Classic Example
class A {}
class B {}

A a = new A();
B b = new B();

Thread t1 = new Thread(() -> {


synchronized (a) {
synchronized (b) {
[Link]("Thread 1 acquired both locks");
}
}
});

Thread t2 = new Thread(() -> {


synchronized (b) {
synchronized (a) {
[Link]("Thread 2 acquired both locks");
}
}
});

Here:

 Thread 1 locks object a and waits for b.


 Thread 2 locks object b and waits for a.
 Both threads wait forever → Deadlock.

Conditions Required for Deadlock

There are four necessary conditions:

1. Mutual Exclusion – Resource can be held by only one thread at a time


2. Hold and Wait – Thread holds one resource and waits for another
3. No Preemption – Resource cannot be forcibly taken away
4. Circular Wait – Circular chain of threads waiting for each other

If all four conditions exist, deadlock can occur.

How to Prevent Deadlock

 Maintain consistent lock ordering


 Avoid nested locks
 Use tryLock() with timeout
 Minimize synchronized blocks

In summary:

Deadlock is a situation where two or more threads are stuck waiting for each other’s resources indefinitely,
preventing program execution from progressing.

42. What is race condition?

A race condition occurs when two or more threads access and modify shared data concurrently, and the
final outcome depends on the timing or order of execution of those threads.

It leads to unpredictable and inconsistent results.


Example
class Counter {
int count = 0;

void increment() {
count++;
}
}

If two threads execute increment() at the same time:

count++ is not atomic. It involves:

1. Read count
2. Increment value
3. Write back

If both threads read the same value before writing, one update may overwrite the other.

Expected result: 2
Actual result may be: 1

This is a race condition.

Why It Happens

 Shared mutable data


 Lack of synchronization
 Multiple threads executing simultaneously

How to Prevent Race Condition

 Use synchronized blocks or methods


 Use volatile (for visibility, not atomicity)
 Use atomic classes like AtomicInteger
 Use proper concurrency utilities

In summary:

A race condition occurs when multiple threads modify shared data without proper synchronization, causing
unpredictable and incorrect results.
43. What is ExecutorService?

ExecutorService is a high-level concurrency framework in Java used to manage and control a pool of
threads for executing tasks asynchronously.

It is part of the [Link] package and provides a better alternative to manually creating and
managing threads.

Why We Need ExecutorService

Creating threads manually using:

new Thread().start();

is not scalable because:

 Thread creation is expensive


 Hard to manage lifecycle
 Difficult to control concurrency

ExecutorService solves this by:

 Managing a thread pool


 Reusing threads
 Controlling task execution

How It Works

Instead of creating threads directly, we submit tasks:

ExecutorService service = [Link](3);

[Link](() -> {
[Link]("Task executed");
});

The thread pool executes the task using one of its worker threads.

Common Methods

 submit() → Submits Runnable or Callable task


 execute() → Executes Runnable task
 shutdown() → Graceful shutdown
 shutdownNow() → Immediate shutdown
Types of Thread Pools

 FixedThreadPool
 CachedThreadPool
 SingleThreadExecutor
 ScheduledThreadPool

Benefits

 Better resource management


 Improved performance
 Scalability
 Simplifies concurrent programming

In summary:
ExecutorService is a thread pool framework that manages and executes asynchronous tasks efficiently without
manually handling threads.

44. What is ThreadPool?

A Thread Pool is a collection of pre-created worker threads that are reused to execute multiple tasks,
instead of creating a new thread for every task.

It improves performance and resource management in concurrent applications.

Why Thread Pool Is Needed

Creating a new thread for each request is expensive because:

 Thread creation takes time


 Memory overhead is high
 Too many threads can crash the application

Thread pool solves this by:

 Reusing existing threads


 Limiting number of concurrent threads
 Managing task queue
How It Works

1. Threads are created once and kept in a pool.


2. Tasks are submitted to a queue.
3. An available thread picks the task and executes it.
4. After completion, the thread returns to the pool instead of terminating.

Example (Using ExecutorService)


ExecutorService service = [Link](3);

[Link](() -> {
[Link]("Task executed by " + [Link]().getName());
});

Only 3 threads will execute tasks concurrently; others wait in queue.

Benefits

 Faster execution (no repeated thread creation)


 Better CPU utilization
 Prevents resource exhaustion
 Scalable for high-load systems

In summary:

A Thread Pool is a group of reusable threads used to execute multiple tasks efficiently, improving performance
and controlling concurrency.

45. Difference between synchronized and Lock?

Both synchronized and Lock are used to achieve thread synchronization in Java, but they differ in
flexibility and control.

1. Basic Nature

synchronized

 Keyword in Java
 Built-in language-level feature
 Automatically acquires and releases lock

Lock

 Interface from [Link]


 Provides more advanced locking mechanisms
 Requires manual lock and unlock

2. Lock Acquisition & Release

synchronized

synchronized (this) {
// critical section
}

 Lock is automatically released when block exits


 Even if exception occurs

Lock

Lock lock = new ReentrantLock();

[Link]();
try {
// critical section
} finally {
[Link]();
}

 Must manually release lock


 If unlock() is forgotten → deadlock risk

3. Flexibility

synchronized

 No timeout
 No non-blocking attempt
 No interruptible lock acquisition

Lock
Provides advanced features:

 tryLock() → non-blocking
 tryLock(timeout) → wait with timeout
 lockInterruptibly() → interruptible lock
 Fair locking option
4. Performance

 In modern Java versions, performance difference is minimal.


 Lock is generally preferred in complex concurrent scenarios.

5. Condition Variables

Lock supports multiple condition variables using Condition interface.


synchronized supports only one condition (wait() / notify()).

In summary:

 Use synchronized for simple synchronization needs.


 Use Lock when you need advanced features like timeout, fairness, interruptible locking, or multiple
conditions.

Java 8 (ALWAYS ASKED)

46. What is functional interface?

A functional interface is an interface that contains exactly one abstract method.

It can have:

 One abstract method


 Any number of default methods
 Any number of static methods

Functional interfaces are mainly used to enable lambda expressions in Java.

Example
@FunctionalInterface
interface MyInterface {
void display();
}

Here, display() is the only abstract method.

The @FunctionalInterface annotation is optional but recommended because:


 It ensures the interface has exactly one abstract method.
 If we add another abstract method, it gives a compile-time error.

Using with Lambda


MyInterface obj = () -> [Link]("Hello");
[Link]();

Lambda expressions work only with functional interfaces.

Common Built-in Functional Interfaces

From [Link] package:

 Predicate<T> → boolean test(T t)


 Function<T, R> → R apply(T t)
 Consumer<T> → void accept(T t)
 Supplier<T> → T get()

Also:

 Runnable
 Callable
 Comparator

are functional interfaces.

In summary:

A functional interface is an interface with exactly one abstract method, used primarily to support lambda
expressions and functional programming in Java.

47. What is lambda expression?

A lambda expression in Java is a concise way to represent an anonymous function that can be passed as
an argument to a method or stored as a variable.

It was introduced in Java 8 to support functional programming.

Lambda expressions are mainly used with functional interfaces.


Syntax
(parameters) -> { body }

Example Without Lambda


Runnable r = new Runnable() {
@Override
public void run() {
[Link]("Running task");
}
};

Same Example Using Lambda


Runnable r = () -> [Link]("Running task");

Lambda reduces boilerplate code and improves readability.

Example with Parameters


Comparator<Integer> comp = (a, b) -> a - b;

Key Points

 Works only with functional interfaces.


 Makes code more concise.
 Often used with Streams and collections.
 Improves readability for small behavior implementations.

In summary:

A lambda expression is a short, anonymous implementation of a functional interface method, used to write
cleaner and more expressive code in Java.

48. What is Stream API?

Stream API is a feature introduced in Java 8 that allows us to process collections of data in a functional
and declarative way.

It is part of the [Link] package.


Stream does not store data.
It processes data from a source like a collection, array, or I/O channel.

Why Stream API Is Used

Before Java 8, we used loops:

for (Integer num : list) {


if (num > 10) {
[Link](num);
}
}

With Stream API:

[Link]()
.filter(num -> num > 10)
.forEach([Link]::println);

It makes code:

 More readable
 More concise
 Easier to parallelize

Stream Pipeline

A stream operation has three parts:

1. Source
o Collection, array, etc.
2. Intermediate Operations
o filter()
o map()
o sorted()
o distinct()
o These are lazy (not executed immediately)
3. Terminal Operation
o forEach()
o collect()
o count()
o reduce()
o This triggers execution

Example
List<String> names = [Link]("Anjali", "Rahul", "Arun");

List<String> result = [Link]()


.filter(name -> [Link]("A"))
.collect([Link]());

Key Features

 Functional style programming


 Supports parallel processing using parallelStream()
 Lazy evaluation
 Does not modify the original collection

In summary:

Stream API provides a functional and efficient way to process collections using operations like filter, map, and
reduce, with support for parallel execution.

49. Difference between map() and flatMap()?

Both map() and flatMap() are intermediate operations in the Stream API used to transform elements,
but they differ in how they handle nested structures.

map()

 Transforms each element into another element.


 Returns a stream of the same size.
 One-to-one mapping.

Example:

List<String> names = [Link]("Anjali", "Rahul");

[Link]()
.map(String::toUpperCase)
.forEach([Link]::println);

If input is:

["Anjali", "Rahul"]

Output becomes:

["ANJALI", "RAHUL"]
Each element maps to exactly one element.

flatMap()

 Used when each element maps to multiple elements.


 Flattens nested structures.
 Converts Stream<Stream<T>> into Stream<T>.

Example:

List<List<String>> list = [Link](


[Link]("A", "B"),
[Link]("C", "D")
);

[Link]()
.flatMap(innerList -> [Link]())
.forEach([Link]::println);

Without flatMap, you'd get:

Stream<Stream<String>>

With flatMap, you get:

A, B, C, D

Key Difference

map() → One-to-one transformation


flatMap() → One-to-many transformation + flattening

Simple Way to Remember

 map() changes the shape of elements.


 flatMap() changes the shape and flattens nested streams.

In summary:
Use map() when each element transforms into a single value.
Use flatMap() when each element produces multiple values and you want a flattened result.

50. What is Optional?


Optional is a container object introduced in Java 8 that may or may not contain a non-null value.

It is used to avoid NullPointerException and to represent the absence of a value in a more expressive way.

It belongs to the [Link] package.

Why Optional Is Needed

Before Java 8:

String name = getName();


if (name != null) {
[Link]([Link]());
}

We had to manually check for null everywhere.

With Optional:

Optional<String> name = getName();


[Link](n -> [Link]([Link]()));

It reduces null-check boilerplate.

How to Create Optional


Optional<String> opt1 = [Link]("Hello"); // value must not be null
Optional<String> opt2 = [Link](null); // can handle null
Optional<String> opt3 = [Link](); // empty Optional

Common Methods

 isPresent() → checks if value exists


 get() → returns value (not recommended without check)
 ifPresent() → executes if value exists
 orElse() → returns default value
 orElseGet() → returns value from supplier
 orElseThrow() → throws exception if empty
 map() → transforms value if present

Example:

Optional<String> name = [Link]("Anjali");

String result = [Link](String::toUpperCase)


.orElse("Default");
Important Note

Optional is mainly intended for return types, not for:

 Fields
 Method parameters
 Serialization

In summary:

Optional is a wrapper class that represents the presence or absence of a value, helping to avoid
NullPointerException and making null handling more expressive.

51. What is method reference?

A method reference in Java is a shorthand syntax for referring to an existing method without executing
it.

It is used in place of a lambda expression when the lambda simply calls an existing method.

Method reference was introduced in Java 8 and works with functional interfaces.

Why Use Method Reference?

It makes code:

 More readable
 More concise
 Cleaner than lambda expressions

Syntax
ClassName::methodName

Types of Method References

1. Reference to a static method

Function<String, Integer> func = Integer::parseInt;


Equivalent lambda:

str -> [Link](str)

2. Reference to an instance method of a particular object

PrintStream out = [Link];


Consumer<String> consumer = out::println;

Equivalent lambda:

str -> [Link](str)

3. Reference to an instance method of an arbitrary object

List<String> names = [Link]("A", "B");


[Link]([Link]::println);

4. Reference to a constructor

Supplier<List<String>> supplier = ArrayList::new;

Equivalent lambda:

() -> new ArrayList<>();

In summary:

A method reference is a compact way to refer to an existing method or constructor, used with functional
interfaces to improve readability and reduce boilerplate code.

DATABASE & BACKEND RELATED (VERY COMMON)

52. What are ACID properties?

ACID properties define the four fundamental principles that ensure reliable and consistent database
transactions.

ACID stands for:

 Atomicity
 Consistency
 Isolation
 Durability
1. Atomicity

Atomicity means a transaction must either complete fully or not execute at all.

If any part of the transaction fails, the entire transaction is rolled back.

Example:
If money is transferred from Account A to Account B:

 Deduct from A
 Add to B

If adding to B fails, deduction from A must also be rolled back.

It ensures “all or nothing”.

2. Consistency

Consistency means a transaction must bring the database from one valid state to another valid state.

All constraints must be satisfied:

 Primary key constraints


 Foreign key constraints
 Unique constraints
 Business rules

After transaction commit, the database must remain consistent.

3. Isolation

Isolation means multiple transactions executing concurrently should not interfere with each other.

Each transaction should behave as if it is running alone.

Database provides isolation levels like:

 Read Uncommitted
 Read Committed
 Repeatable Read
 Serializable

Isolation prevents issues like:

 Dirty reads
 Non-repeatable reads
 Phantom reads

4. Durability

Durability means once a transaction is committed, the changes are permanently stored, even if the system
crashes.

Committed data is written to disk and preserved.

In summary:

ACID properties ensure that database transactions are reliable, consistent, isolated from each other, and
permanently stored after commit.

53. What is normalization?

Normalization is the process of organizing data in a database to reduce redundancy and improve data
integrity.

It involves dividing large tables into smaller related tables and defining relationships between them.

The main goal is to:

 Eliminate duplicate data


 Avoid data anomalies
 Ensure consistency

Why Normalization Is Needed

Without normalization, we may face:

 Insertion anomaly
 Update anomaly
 Deletion anomaly

Example:

If student and course details are stored in one table, repeating student data for every course leads to redundancy.
Normal Forms

Normalization is done in stages called Normal Forms.

1NF (First Normal Form)

 No repeating groups
 Each column contains atomic (indivisible) values
 Each row is unique

Example:
No multiple phone numbers in one column.

2NF (Second Normal Form)

 Must be in 1NF
 No partial dependency
 All non-key attributes must depend on the entire primary key

Applicable mainly when we have composite keys.

3NF (Third Normal Form)

 Must be in 2NF
 No transitive dependency
 Non-key attributes should depend only on the primary key

Example:
If Student table stores department name and department location, and location depends on department, not
student → transitive dependency → must separate.

Benefits

 Reduces redundancy
 Improves data integrity
 Makes database more maintainable

Trade-off

Highly normalized databases may require more joins, which can impact performance.
In summary:

Normalization is the process of structuring database tables to eliminate redundancy and ensure data integrity by
following normal forms like 1NF, 2NF, and 3NF.

1NF (First Normal Form)

A table is in 1NF if:

 Each column contains atomic (indivisible) values


 No repeating groups
 Each row is uniquely identifiable (primary key exists)

Example (Not in 1NF)

StudentID Name PhoneNumbers

1 Anjali 9876, 8765

Multiple phone numbers in one column ❌

Convert to 1NF

StudentID Name PhoneNumber

1 Anjali 9876

1 Anjali 8765

Now each cell has a single value ✔

2NF (Second Normal Form)

A table is in 2NF if:

 It is already in 1NF
 No partial dependency exists

Partial dependency means:


A non-key column depends on only part of a composite primary key.

Example

If primary key = (StudentID, CourseID)


StudentID CourseID StudentName

Here:

 StudentName depends only on StudentID


 Not on full composite key

This violates 2NF ❌

Fix

Split into:

Student Table
| StudentID | StudentName |

Enrollment Table
| StudentID | CourseID |

3NF (Third Normal Form)

A table is in 3NF if:

 It is in 2NF
 No transitive dependency exists

Transitive dependency means:


A non-key column depends on another non-key column.

Example

| StudentID | DepartmentID | DepartmentName |

If:
DepartmentName depends on DepartmentID
Not directly on StudentID

This violates 3NF ❌

Fix

Student Table
| StudentID | DepartmentID |

Department Table
| DepartmentID | DepartmentName |
Simple Way to Remember

1NF → No repeating columns


2NF → No partial dependency
3NF → No transitive dependency

In summary:
1NF ensures atomic values,
2NF removes partial dependency,
3NF removes transitive dependency to reduce redundancy and maintain data integrity.

54. What is indexing?

Indexing is a database optimization technique used to improve the speed of data retrieval operations on a
table.

It works similarly to an index in a book — instead of scanning the entire table, the database uses the index to
quickly locate the required rows.

Why Indexing Is Needed

Without an index:

 Database performs a full table scan


 Time complexity is O(n)
 Slower for large tables

With an index:

 Database can search efficiently (typically using a B-Tree structure)


 Time complexity becomes O(log n)

How Index Works Internally

Most relational databases use a B-Tree (or B+ Tree) structure.

The index stores:

 Indexed column values


 Pointer/reference to the actual row location

When a query runs:


SELECT * FROM users WHERE email = 'abc@[Link]';

If email is indexed:

 DB searches index tree


 Finds row pointer
 Retrieves row directly

Instead of scanning all rows.

Types of Indexes

 Primary Index (automatically created for primary key)


 Unique Index
 Composite Index (multiple columns)
 Clustered Index
 Non-Clustered Index

Trade-offs

Advantages:

 Faster SELECT queries


 Efficient filtering and joins

Disadvantages:

 Slower INSERT, UPDATE, DELETE


 Extra storage required

Because every data modification must update the index.

In summary:

Indexing improves query performance by creating a data structure (usually B-Tree) that allows fast lookup of
rows, but it increases storage and write operation cost.

55. What is database scaling?

Database scaling is the process of increasing a database system’s capacity and performance to handle
growing data volume and traffic.

There are two main types of database scaling:


 Vertical Scaling
 Horizontal Scaling

1. Vertical Scaling (Scaling Up)

Vertical scaling means increasing the resources of a single database server.

For example:

 Adding more RAM


 Increasing CPU
 Using faster SSD storage

Advantages:

 Simple to implement
 No changes in application logic

Disadvantages:

 Hardware limits
 Single point of failure
 Expensive at higher levels

2. Horizontal Scaling (Scaling Out)

Horizontal scaling means adding more database servers and distributing the load.

This includes:

a) Read Replicas

 One primary database handles writes


 Multiple replicas handle reads
 Improves read performance

b) Sharding

 Splitting data across multiple databases


 Each server stores a portion of the data

Example:

 Users 1–1M → DB1


 Users 1M–2M → DB2

Advantages:
 Highly scalable
 Handles very large traffic

Disadvantages:

 Complex implementation
 Requires data distribution logic

Other Scaling Techniques

 Caching (Redis, Memcached)


 Database partitioning
 Load balancing
 Using NoSQL for specific workloads

In summary:

Database scaling is the process of improving database capacity and performance either by increasing server
resources (vertical scaling) or by distributing data across multiple servers (horizontal scaling).

56. What is sharding?

Sharding is a database scaling technique where a large database is split into smaller, independent
databases called shards, each storing a portion of the data.

It is a type of horizontal scaling.

Why Sharding Is Needed

When a single database:

 Becomes too large


 Cannot handle high traffic
 Faces performance bottlenecks

Instead of upgrading one server, we split data across multiple servers.

How Sharding Works


Data is divided based on a shard key.

Example:

If we shard users based on user ID:

 Users with ID 1–1,000,000 → Shard 1


 Users with ID 1,000,001–2,000,000 → Shard 2

Each shard:

 Has its own database instance


 Stores only a subset of data

Application logic decides which shard to query.

Common Sharding Strategies

1. Range-based sharding
Data split by value ranges
2. Hash-based sharding
Use hash function on shard key
3. shard = hash(userId) % number_of_shards
4. Geographic sharding
Based on region (e.g., India, US, Europe)

Advantages

 Handles very large datasets


 Improves scalability
 Distributes load

Challenges

 Complex to implement
 Cross-shard joins are difficult
 Rebalancing data is hard
 Requires shard key design carefully

In summary:

Sharding is a horizontal scaling technique where data is partitioned across multiple databases to improve
scalability and handle large volumes of traffic.
57. What is transaction isolation level?

Transaction isolation level defines how much one transaction is isolated from other concurrent
transactions in a database system.

It controls how and when changes made by one transaction become visible to others.

Isolation is one of the ACID properties.

Why Isolation Levels Are Needed

When multiple transactions run at the same time, problems can occur such as:

 Dirty reads
 Non-repeatable reads
 Phantom reads

Isolation levels control these issues.

Four Standard Isolation Levels (Lowest to Highest)

1. Read Uncommitted

 A transaction can read uncommitted data from another transaction.


 Dirty reads are possible.

Example:
Transaction A updates data but does not commit.
Transaction B reads that uncommitted value.

This is the lowest isolation level.

2. Read Committed

 A transaction can read only committed data.


 Dirty reads are prevented.
 Non-repeatable reads are still possible.

This is the most commonly used level in many databases.


3. Repeatable Read

 Rows read cannot be changed by other transactions until current transaction completes.
 Prevents dirty reads and non-repeatable reads.
 Phantom reads may still occur (depending on DB).

4. Serializable

 Highest isolation level.


 Transactions execute as if they are running sequentially.
 Prevents dirty reads, non-repeatable reads, and phantom reads.
 Slower due to strict locking.

Quick Summary of Problems

 Dirty Read → Reading uncommitted data


 Non-repeatable Read → Same row gives different values in same transaction
 Phantom Read → New rows appear in repeated query

In summary:

Transaction isolation level determines how visible one transaction’s changes are to others and helps control
concurrency issues in database systems.

58. What is optimistic vs pessimistic locking?

Optimistic locking and pessimistic locking are two concurrency control mechanisms used to handle
concurrent updates in a database.

They differ in how they assume conflicts will occur.

1. Pessimistic Locking

Pessimistic locking assumes that conflicts are likely to happen, so it locks the data immediately when it is
read.

Other transactions cannot modify the locked data until the lock is released.

Example:
SELECT * FROM users WHERE id = 1 FOR UPDATE;

This locks the row.

Characteristics:

 Uses database locks


 Prevents other transactions from modifying the row
 Safer but reduces concurrency
 Can cause deadlocks

Use Case:

When:

 High contention on data


 Critical financial operations

2. Optimistic Locking

Optimistic locking assumes that conflicts are rare, so it does not lock the row when reading.

Instead, it checks for changes before updating.

Usually implemented using:

 Version column
 Timestamp

Example:

id name version

1 Anjali 1

Update query:

UPDATE users
SET name = 'A', version = version + 1
WHERE id = 1 AND version = 1;

If version changed → update fails → conflict detected.

Characteristics:

 No locking during read


 Better performance
 More scalable
 Requires retry logic
Use Case:

When:

 Low probability of conflicts


 High-read systems

Key Differences

Pessimistic Locking:

 Locks data immediately


 Lower concurrency
 Risk of deadlock

Optimistic Locking:

 No lock during read


 Checks for conflicts before update
 Better performance in low-conflict systems

In summary:

Pessimistic locking prevents conflicts by locking data early, while optimistic locking allows concurrent access
and detects conflicts at the time of update.

SPRING BOOT (CRITICAL FOR YOU)

59. What is Spring Boot?

Spring Boot is a framework built on top of the Spring Framework that simplifies the development of
production-ready, stand-alone Java applications with minimal configuration.

It reduces boilerplate configuration and helps developers quickly build REST APIs, microservices, and web
applications.

Why Spring Boot Was Introduced

Traditional Spring required:


 XML configuration
 Manual dependency setup
 Server deployment (like Tomcat separately)

Spring Boot simplifies this by providing:

 Auto-configuration
 Embedded server
 Starter dependencies

Key Features

1. Auto-Configuration
Automatically configures application based on dependencies present in classpath.
2. Starter Dependencies
Predefined dependency groups like:
o spring-boot-starter-web
o spring-boot-starter-data-jpa
o spring-boot-starter-security
3. Embedded Server
Comes with embedded Tomcat (default), Jetty, or Undertow.
No need to deploy WAR separately.
4. Production-Ready Features
o Actuator (health checks, metrics)
o Externalized configuration
o Logging support

Example
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
[Link]([Link], args);
}
}

Single annotation replaces:

 @Configuration
 @EnableAutoConfiguration
 @ComponentScan

In summary:

Spring Boot is a convention-over-configuration framework that simplifies building and deploying Spring-based
applications by providing auto-configuration, embedded servers, and starter dependencies.
60. What is Dependency Injection?

Dependency Injection (DI) is a design pattern where the dependencies of a class are provided from the
outside instead of the class creating them itself.

It promotes loose coupling and improves testability and maintainability.

Without Dependency Injection (Tight Coupling)


class Car {
Engine engine = new Engine(); // Car creates dependency
}

Here:

 Car is tightly coupled to Engine.


 Hard to replace Engine with another implementation.
 Difficult to unit test.

With Dependency Injection (Loose Coupling)


class Car {
private Engine engine;

Car(Engine engine) {
[Link] = engine;
}
}

Now:

 Engine is injected from outside.


 Car does not create it.
 Easy to replace implementation.

Types of Dependency Injection

1. Constructor Injection (recommended)


2. Setter Injection
3. Field Injection (not recommended for production code)
In Spring

Spring container creates objects (beans) and injects dependencies automatically.

Example:

@Service
class CarService {

private final Engine engine;

public CarService(Engine engine) {


[Link] = engine;
}
}

Spring resolves and injects the Engine bean.

Benefits

 Loose coupling
 Easier testing (mock dependencies)
 Better maintainability
 Follows SOLID principles

In summary:

Dependency Injection is a design pattern where objects receive their dependencies from an external source
rather than creating them internally, enabling loose coupling and better software design.

61. What is IoC container?

IoC container (Inversion of Control container) is a core part of the Spring framework that is responsible
for creating, managing, and injecting dependencies between objects (beans).

It implements the concept of Dependency Injection.

What Is Inversion of Control (IoC)?

Normally in traditional programming:

 Objects create their own dependencies.

In IoC:
 The control of object creation and dependency management is transferred to the container.
 The container manages the lifecycle of objects.

That inversion of control is handled by the IoC container.

What Does IoC Container Do?

1. Creates objects (beans)


2. Injects dependencies
3. Manages bean lifecycle
4. Handles configuration
5. Manages scopes (singleton, prototype, etc.)

Types of IoC Containers in Spring

1. BeanFactory
o Basic container
o Lazy initialization
2. ApplicationContext
o Advanced container
o Supports:
 AOP
 Internationalization
 Event propagation
 Eager initialization

ApplicationContext is commonly used in Spring Boot.

Example
@Service
class Engine {}

@Service
class Car {

private final Engine engine;

public Car(Engine engine) {


[Link] = engine;
}
}

Here:

 Spring IoC container creates Engine.


 It creates Car.
 It injects Engine into Car automatically.

In summary:

The IoC container is a Spring component that manages object creation, dependency injection, and lifecycle
management, implementing the principle of Inversion of Control.

62. What is @Autowired?

@Autowired is a Spring annotation used for automatic dependency injection.

It tells the Spring IoC container to automatically resolve and inject a required bean into a class.

How It Works

When Spring sees @Autowired:

 It looks for a matching bean in the application context.


 It injects that bean into the dependency.
 By default, it performs injection by type.

Example (Constructor Injection – Recommended)


@Service
class Engine {
}

@Service
class Car {

private final Engine engine;

@Autowired
public Car(Engine engine) {
[Link] = engine;
}
}

Spring automatically injects the Engine bean into Car.

Note:
In modern Spring versions, if a class has only one constructor, @Autowired is optional.
Other Injection Types

1. Field Injection

@Autowired
private Engine engine;

Not recommended because:

 Hard to test
 Breaks immutability

2. Setter Injection

@Autowired
public void setEngine(Engine engine) {
[Link] = engine;
}

What If Multiple Beans Exist?

If multiple beans of the same type exist, Spring throws an exception.

We can resolve this using:

 @Qualifier
 @Primary

In summary:

@Autowired is used in Spring to automatically inject required dependencies by type from the IoC container into
a class.

63. What is @Component vs @Service vs @Repository?

@Component, @Service, and @Repository are Spring stereotype annotations used to declare beans, but
they serve different semantic purposes in application architecture.

All three are detected during component scanning and registered as Spring beans.

1. @Component

 Generic stereotype annotation


 Used for any Spring-managed component
 Base annotation for others
Example:

@Component
class UtilityClass {
}

Use when:

 The class does not belong specifically to service or repository layer.

2. @Service

 Specialized version of @Component


 Used in the service layer
 Contains business logic

Example:

@Service
class UserService {
}

It improves readability by indicating that the class holds business logic.

Functionally same as @Component, but semantically clearer.

3. @Repository

 Specialized version of @Component


 Used in the data access layer (DAO layer)
 Handles database interactions

Example:

@Repository
class UserRepository {
}

Extra feature:

 Enables automatic exception translation


 Converts database-specific exceptions into Spring’s DataAccessException

Key Differences
@Component
 Generic bean

@Service

 Business logic layer

@Repository

 Data access layer + exception translation

In summary:

All three create Spring-managed beans, but @Service is used for business logic, @Repository for database
operations, and @Component for general-purpose components.

64. What is Spring Bean lifecycle?

The Spring Bean lifecycle describes the stages a bean goes through from creation to destruction inside the
Spring IoC container.

Spring manages the entire lifecycle of a bean.

Steps in Spring Bean Lifecycle

1. Bean Instantiation

Spring creates the bean instance using:

 Constructor
 Or factory method

At this stage, the object is created but dependencies are not injected yet.

2. Dependency Injection

Spring injects required dependencies into the bean.

This can be:

 Constructor injection
 Setter injection
 Field injection
3. Aware Interfaces (Optional)

If the bean implements special interfaces like:

 BeanNameAware
 BeanFactoryAware
 ApplicationContextAware

Spring calls their respective methods to provide additional information.

4. Pre-Initialization

Spring calls:

 @PostConstruct method
 afterPropertiesSet() (if implementing InitializingBean)
 Custom init-method (if defined)

This is used for initialization logic.

5. Bean Ready for Use

Now the bean is fully initialized and ready to serve requests.

6. Pre-Destruction

Before bean removal:

 @PreDestroy method is called


 destroy() method (if implementing DisposableBean)
 Custom destroy-method (if configured)

7. Bean Destruction

Bean is removed from container.

For singleton beans:

 Destroyed when application context closes.


For prototype beans:

 Spring does not manage destruction automatically.

Simple Flow

Instantiation → Dependency Injection → Initialization → Ready → Destruction

In summary:

The Spring Bean lifecycle includes creation, dependency injection, initialization, usage, and destruction, all
managed by the Spring IoC container.

65. What is REST API?

REST API (Representational State Transfer API) is an architectural style used to design scalable and
stateless web services that communicate over HTTP.

It allows clients and servers to communicate using standard HTTP methods.

Core Principles of REST

1. Stateless
o Each request from client contains all necessary information.
o Server does not store client session state.
2. Client-Server Architecture
o Clear separation between client and server.
3. Resource-Based
o Everything is treated as a resource.
o Each resource is identified by a URI.

Example:

/users
/users/1
/orders/100

HTTP Methods Used in REST

 GET → Retrieve data


 POST → Create new resource
 PUT → Update resource
 PATCH → Partial update
 DELETE → Remove resource

Example:

GET /users/1
POST /users
DELETE /users/1

Data Format

REST APIs usually exchange data in:

 JSON (most common)


 XML (less common)

Example JSON response:

{
"id": 1,
"name": "Anjali"
}

REST vs Traditional Web Services

 REST is lightweight
 Uses HTTP directly
 No strict protocol like SOAP
 Easier to scale

In summary:

REST API is a stateless, resource-based web service architecture that uses HTTP methods to perform CRUD
operations and typically communicates using JSON.

66. What is @RestController?

@RestController is a Spring annotation used to create RESTful web services. It combines @Controller
and @ResponseBody.

It tells Spring that:

 The class handles HTTP requests


 The return value of methods should be written directly to the HTTP response body (usually JSON)
What It Actually Means

@RestController is equivalent to:

@Controller
@ResponseBody

So every method inside the class automatically returns data, not a view.

Example
@RestController
@RequestMapping("/users")
class UserController {

@GetMapping("/{id}")
public String getUser(@PathVariable int id) {
return "User ID: " + id;
}
}

When we call:

GET /users/1

Response will be:

User ID: 1

No view resolution happens.

Difference Between @Controller and @RestController


@Controller

 Used for web MVC


 Returns view names (like JSP, HTML)

@RestController

 Used for REST APIs


 Returns JSON/XML directly

In summary:
@RestController is a Spring annotation used to build REST APIs, where methods return response data
directly instead of view pages.

67. What is @RequestMapping?

@RequestMapping is a Spring annotation used to map HTTP requests to specific handler methods or
classes.

It defines:

 URL path
 HTTP method
 Request parameters
 Headers
 Content type

Basic Usage

At class level:

@RestController
@RequestMapping("/users")
class UserController {
}

All endpoints inside this controller will start with /users.

At method level:

@RequestMapping(value = "/{id}", method = [Link])


public String getUser(@PathVariable int id) {
return "User ID: " + id;
}

This maps:

GET /users/{id}

to this method.

Shortcut Annotations (Preferred)

Instead of writing:

@RequestMapping(method = [Link])
We can use:

 @GetMapping
 @PostMapping
 @PutMapping
 @DeleteMapping
 @PatchMapping

Example:

@GetMapping("/{id}")

Cleaner and more readable.

What It Can Define

 value → URL path


 method → HTTP method
 params → Required query parameters
 headers → Required headers
 produces → Response content type
 consumes → Request content type

In summary:

@RequestMapping is used in Spring to map HTTP requests to controller methods by specifying URL patterns
and HTTP methods.

68. What is JWT?

JWT (JSON Web Token) is a compact, URL-safe token format used for securely transmitting
information between parties, commonly used for authentication and authorization in REST APIs.

It is stateless and self-contained.

Structure of JWT

A JWT consists of three parts separated by dots:

[Link]

Example:

[Link]
1. Header

Contains:

 Token type (JWT)


 Signing algorithm (e.g., HS256, RS256)

Example:

{
"alg": "HS256",
"typ": "JWT"
}

2. Payload

Contains claims (data):

 User information (e.g., userId, role)


 Expiration time
 Issued time

Example:

{
"sub": "12345",
"role": "ADMIN",
"exp": 1700000000
}

3. Signature

Created by:

HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secretKey
)

Used to verify that the token was not tampered with.

How JWT Works (Authentication Flow)

1. User logs in with credentials.


2. Server validates credentials.
3. Server generates JWT and sends it to client.
4. Client stores token (usually in local storage).
5. Client sends token in Authorization header:

Authorization: Bearer <token>

6. Server verifies signature and allows access.

Why JWT Is Popular

 Stateless (no session stored on server)


 Scalable
 Compact
 Works well in microservices

Important Points

 JWT is signed, not encrypted (by default).


 Payload can be decoded easily.
 Sensitive data should not be stored inside JWT.

In summary:

JWT is a stateless authentication token consisting of header, payload, and signature, used to securely transmit
user identity and authorization information in web applications.

69. What is Spring Security?

Spring Security is a powerful and customizable authentication and authorization framework for securing
Java and Spring-based applications.

It provides security at multiple levels including:

 Authentication
 Authorization
 Protection against common attacks

1. Authentication vs Authorization

Authentication
→ Verifying who the user is
Example: Username and password validation
Authorization
→ Verifying what the user is allowed to access
Example: Only ADMIN can access /admin endpoint

Spring Security handles both.

2. Key Features of Spring Security

 Authentication (Form login, JWT, OAuth2, etc.)


 Role-based access control
 Method-level security (@PreAuthorize)
 CSRF protection
 Session management
 Password encryption (BCrypt)

3. How It Works Internally (High Level)

Spring Security works using a filter chain.

When a request comes:

1. It passes through security filters.


2. Authentication is validated.
3. Authorization rules are checked.
4. If valid → request proceeds.
5. If invalid → access denied.

4. Example (Basic Configuration)


@Configuration
@EnableWebSecurity
class SecurityConfig {

@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin();

return [Link]();
}
}
5. Why It Is Important

 Protects APIs from unauthorized access


 Ensures data security
 Essential for production applications

In summary:

Spring Security is a comprehensive security framework that provides authentication, authorization, and
protection against common web security threats in Spring applications.

70. What is CORS?

CORS (Cross-Origin Resource Sharing) is a security mechanism implemented by browsers that controls
how resources on a web server can be requested from a different origin.

An origin consists of:

 Protocol (http/https)
 Domain
 Port

If any of these differ, it is considered a different origin.

Example:

Frontend: [Link]
Backend: [Link]

Different ports → different origins → CORS applies.

Why CORS Exists

Browsers enforce the Same-Origin Policy for security.

This means:

 A web page can only make requests to the same origin by default.

CORS allows servers to relax this restriction in a controlled way.


How CORS Works

When a browser sends a cross-origin request:

It includes headers like:

Origin: [Link]

Server must respond with:

Access-Control-Allow-Origin: [Link]

If allowed → request succeeds.


If not → browser blocks it.

Preflight Request

For certain requests (PUT, DELETE, custom headers):

Browser first sends an OPTIONS request (preflight).

Server must respond with:

 Allowed methods
 Allowed headers

Then actual request is sent.

Enabling CORS in Spring Boot

Example:

@CrossOrigin(origins = "[Link]
@RestController
class UserController {
}

Or globally using configuration.

Important Point

CORS is enforced by the browser, not by the server.

Tools like Postman ignore CORS restrictions.


In summary:

CORS is a browser security mechanism that controls cross-origin HTTP requests and allows servers to specify
which external origins can access their resources.

71. What is Hibernate?

Hibernate is an Object-Relational Mapping (ORM) framework for Java that simplifies database
interaction by mapping Java objects to database tables.

It eliminates the need to write most of the JDBC boilerplate code.

Why Hibernate Is Needed

Without Hibernate (using JDBC):

 We manually write SQL


 Handle ResultSet mapping
 Manage connections
 Handle exceptions

Hibernate automates:

 Object-table mapping
 SQL generation
 Connection handling
 Caching

Core Concept: ORM

Hibernate maps:

 Java Class → Database Table


 Java Object → Table Row
 Class fields → Table columns

Example:

@Entity
class User {

@Id
private Long id;

private String name;


}
This class maps to a user table.

How Hibernate Works Internally

1. We define entity classes using annotations.


2. Hibernate reads metadata.
3. Generates SQL automatically.
4. Converts ResultSet into objects.
5. Manages persistence context (Session).

Key Features

 Automatic CRUD operations


 HQL (Hibernate Query Language)
 Caching (First-level, Second-level)
 Lazy and eager loading
 Transaction management

Hibernate vs JPA

 JPA is a specification (interface).


 Hibernate is one implementation of JPA.

In Spring Boot, we usually use Hibernate as the JPA provider.

In summary:

Hibernate is a Java ORM framework that maps Java objects to database tables and simplifies database
operations by automatically handling SQL generation and object mapping.

72. What is JPA?

JPA (Java Persistence API) is a specification in Java that defines how Java objects should be mapped to
relational database tables.

It is not a framework itself, but a standard set of interfaces and annotations for ORM (Object-Relational
Mapping).
Why JPA Is Needed

Before JPA:

 Each ORM framework had its own API.


 No standard approach.

JPA provides:

 A common standard
 Portable persistence logic
 Vendor independence

Important Point

JPA is only a specification.

It needs an implementation like:

 Hibernate
 EclipseLink
 OpenJPA

In most Spring Boot applications, Hibernate is used as the JPA implementation.

Core Concepts in JPA

1. Entity

A Java class mapped to a database table.

@Entity
class User {

@Id
private Long id;

private String name;


}

2. EntityManager

Main interface used to perform:

 Persist
 Remove
 Find
 Merge

3. Persistence Context

A set of managed entities inside a transaction.

Key Features

 Standardized ORM mapping


 JPQL (Java Persistence Query Language)
 Annotation-based configuration
 Transaction management

JPA vs Hibernate

JPA → Specification
Hibernate → Implementation of JPA

In summary:

JPA is a Java specification that defines standard ORM mapping and persistence operations, while frameworks
like Hibernate implement it to provide actual functionality.

73. What is Lazy vs Eager loading?

Lazy loading and Eager loading define when related entities are fetched from the database in ORM
frameworks like JPA or Hibernate.

1. Eager Loading

Eager loading means:

 Related entities are fetched immediately along with the main entity.
 Data is loaded in the same query or immediately after.

Example:

@OneToMany(fetch = [Link])
private List<Order> orders;
If we fetch a User:

User user = [Link](1L);

Hibernate will also fetch all related orders immediately.

Pros:

 No LazyInitializationException
 Data is ready to use

Cons:

 Can cause unnecessary data loading


 Performance issues if relationships are large
 May lead to N+1 query problem

2. Lazy Loading

Lazy loading means:

 Related entities are fetched only when accessed.


 Data is not loaded until explicitly needed.

Example:

@OneToMany(fetch = [Link])
private List<Order> orders;

When we fetch User:

User user = [Link](1L);

Orders are not loaded immediately.

Only when:

[Link]();

Hibernate then queries the database.

Pros:

 Better performance
 Loads only required data

Cons:

 Can cause LazyInitializationException if accessed outside transaction


Default Behavior

 @OneToMany → LAZY (default)


 @ManyToOne → EAGER (default)

In summary:

Eager loading fetches related data immediately, while lazy loading fetches related data only when accessed,
providing better performance but requiring careful transaction management.

74. What is N+1 problem?

The N+1 problem is a performance issue in ORM frameworks like Hibernate where one query is
executed to fetch parent entities, and then an additional query is executed for each parent to fetch related
entities.

This results in N+1 queries instead of just 1 or 2, which severely impacts performance.

Example

Suppose we have:

 User
 Order (OneToMany relationship)

Step 1: Fetch all users


List<User> users = [Link]();

This executes:

SELECT * FROM users;

Assume we fetched 10 users.

Step 2: Access orders


for (User user : users) {
[Link]().size();
}

Now, Hibernate executes:


SELECT * FROM orders WHERE user_id = 1;
SELECT * FROM orders WHERE user_id = 2;
SELECT * FROM orders WHERE user_id = 3;
...

So total queries =

1 (users) + 10 (orders) = 11 queries

This is called the N+1 problem.

Why It Happens

It usually happens with:

 Lazy loading
 Iterating over collections
 Improper fetching strategy

Why It Is Bad

 Multiple unnecessary database calls


 Increased latency
 Poor performance in large datasets

How To Fix It

1. Use JOIN FETCH in JPQL

@Query("SELECT u FROM User u JOIN FETCH [Link]")


List<User> findAllWithOrders();

2. Use @EntityGraph
3. Use batch fetching

In summary:

The N+1 problem occurs when fetching one set of entities results in additional queries for each related entity,
causing performance degradation due to excessive database calls.

75. What is @Transactional?


@Transactional is a Spring annotation used to manage database transactions declaratively.

It ensures that a method runs within a transaction boundary.

If the method completes successfully → transaction commits.


If a runtime exception occurs → transaction rolls back.

Why It Is Needed

Database operations must follow ACID properties.

If multiple operations are part of one logical unit:

 All should succeed


 Or all should fail

@Transactional ensures atomicity.

Example
@Service
class UserService {

@Transactional
public void transferMoney(Account from, Account to, double amount) {
[Link](amount);
[Link](amount);
}
}

If credit() fails:

 Entire transaction is rolled back.


 No partial update happens.

How It Works Internally

Spring uses:

 AOP (proxy-based mechanism)


 Wraps method execution inside a transaction
 Starts transaction before method
 Commits or rolls back after execution
Default Behavior

 Rolls back only for unchecked exceptions (RuntimeException).


 Does NOT roll back for checked exceptions unless specified.

We can customize:

@Transactional(rollbackFor = [Link])

Important Points

 Should be placed on service layer methods.


 Internal method calls in same class may not trigger transaction (proxy limitation).

In summary:

@Transactional is a Spring annotation that defines transaction boundaries, ensuring that a group of database
operations either fully commit or fully roll back based on execution outcome.

76. What happens internally when a REST request hits Spring Boot?

When a REST request hits a Spring Boot application, it goes through a structured processing flow inside
the Spring MVC framework before generating a response.

Here is the step-by-step internal flow:

1. Request Reaches Embedded Server

 Spring Boot runs with an embedded server (like Tomcat).


 The HTTP request first reaches the server.
 The server forwards it to Spring’s DispatcherServlet.

2. DispatcherServlet (Front Controller)

DispatcherServlet is the central component of Spring MVC.

It:

 Receives all incoming requests


 Decides which controller should handle them
 Coordinates request processing

3. Handler Mapping

DispatcherServlet consults HandlerMapping to:

 Match the URL


 Match the HTTP method
 Identify the correct controller method

Example:

GET /users/1

Mapped to:

@GetMapping("/users/{id}")

4. Handler Adapter

HandlerAdapter invokes the selected controller method.

Before calling the method:

 Resolves path variables


 Resolves request parameters
 Converts JSON request body to Java object using HttpMessageConverters

5. Controller Execution

The controller method executes.

It may:

 Call service layer


 Call repository layer
 Perform business logic
 Fetch data from database

6. Return Value Processing

If using @RestController:

 The return object is converted into JSON.


 Spring uses Jackson (by default) for serialization.

This is handled by HttpMessageConverters.

7. Response Sent Back

 JSON response is written to HTTP response body.


 Sent back through server to client.

Important Internal Components

 Embedded Server (Tomcat)


 DispatcherServlet
 HandlerMapping
 HandlerAdapter
 Controller
 HttpMessageConverter
 ExceptionHandler (if error occurs)

In summary:

When a REST request hits Spring Boot, it flows through the embedded server, DispatcherServlet, handler
mapping, controller execution, message conversion, and finally returns a JSON response to the client.

DESIGN & ARCHITECTURE

77. What are SOLID principles?

SOLID is a set of five object-oriented design principles that help in writing clean, maintainable, scalable,
and loosely coupled code.

SOLID stands for:

 S → Single Responsibility Principle


 O → Open/Closed Principle
 L → Liskov Substitution Principle
 I → Interface Segregation Principle
 D → Dependency Inversion Principle
1. Single Responsibility Principle (SRP)

A class should have only one reason to change.

It should have only one responsibility.

❌ Bad:

class UserService {
void saveUser() {}
void sendEmail() {}
void generateReport() {}
}

Too many responsibilities.

✔ Good:

 UserService handles user logic


 EmailService handles email
 ReportService handles reports

2. Open/Closed Principle (OCP)

Software entities should be open for extension but closed for modification.

Instead of modifying existing code, we should extend it.

Example:
Use interfaces and polymorphism instead of if-else blocks.

3. Liskov Substitution Principle (LSP)

Subclasses should be replaceable with their parent class without breaking functionality.

If class B extends class A,


we should be able to use B wherever A is expected.

4. Interface Segregation Principle (ISP)

Clients should not be forced to depend on methods they do not use.

Instead of one large interface:

interface Worker {
void work();
void eat();
}

Split into smaller, specific interfaces.

5. Dependency Inversion Principle (DIP)

High-level modules should not depend on low-level modules.


Both should depend on abstractions.

Instead of:

class Car {
Engine engine = new DieselEngine();
}

Use:

class Car {
Engine engine;
}

Inject dependency via constructor.

In summary:

SOLID principles promote good object-oriented design by encouraging single responsibility, extensibility,
substitutability, interface clarity, and dependency on abstractions rather than concrete implementations.

78. What is Microservices?

Microservices is an architectural style where an application is built as a collection of small, independent,


and loosely coupled services, each responsible for a specific business capability.

Each microservice:

 Has its own logic


 Has its own database (ideally)
 Can be deployed independently
 Communicates over HTTP or messaging

Why Microservices?

In traditional Monolithic architecture:


 Entire application is one large codebase
 Hard to scale specific modules
 Hard to deploy small changes
 One failure can affect the entire system

Microservices solve this by breaking the system into smaller services.

Example (E-commerce Application)

Instead of one big application, we split into:

 User Service
 Order Service
 Payment Service
 Inventory Service

Each service:

 Runs independently
 Can scale independently
 Can be developed by different teams

Key Characteristics

 Independent deployment
 Decentralized database
 Lightweight communication (REST, Kafka, etc.)
 Fault isolation
 Technology independence

Communication Between Services

 REST APIs
 Message queues (Kafka, RabbitMQ)
 Service discovery

Advantages

 Better scalability
 Faster development
 Fault isolation
 Easier maintenance
Challenges

 Distributed system complexity


 Network latency
 Data consistency issues
 Monitoring and logging complexity

In summary:

Microservices is an architectural approach where an application is divided into small, independent services that
communicate over a network and can be developed, deployed, and scaled independently.

79. What is Monolithic architecture?

Monolithic architecture is a traditional software design approach where the entire application is built as
a single, unified codebase and deployed as one unit.

All components are tightly integrated into one application.

Structure of Monolithic Application

Typically includes:

 UI layer
 Business logic layer
 Data access layer
 Database

All packaged and deployed together as a single application (e.g., one WAR/JAR file).

Example

In an e-commerce application:

 User module
 Order module
 Payment module
 Inventory module

All exist inside one single application.


If you want to update payment logic:

 You must redeploy the entire application.

Advantages

 Simple to develop initially


 Easy to deploy (single unit)
 Easier debugging in early stages
 No network communication overhead

Disadvantages

 Difficult to scale specific modules


 Tight coupling between components
 Slower deployments
 One failure can affect entire system
 Hard to maintain as application grows

Monolith vs Microservices (Quick Comparison)

Monolithic:

 Single deployable unit


 Shared database
 Simpler initially

Microservices:

 Multiple independent services


 Independent databases
 Better scalability

In summary:

Monolithic architecture is a design where all components of an application are tightly integrated and deployed
as a single unit, making it simpler initially but harder to scale and maintain as the system grows.

80. What is API Gateway?

An API Gateway is a single entry point for all client requests in a microservices architecture.
Instead of clients calling multiple microservices directly, they send requests to the API Gateway, which routes
them to the appropriate service.

Why API Gateway Is Needed

In microservices:

If we have:

 User Service
 Order Service
 Payment Service

Without API Gateway:

 Client must know all service URLs


 Client handles multiple calls
 Security must be implemented in each service

With API Gateway:

 Client calls only one endpoint


 Gateway handles routing
 Centralized authentication and authorization

What API Gateway Does

1. Request routing
2. Authentication & authorization
3. Rate limiting
4. Logging & monitoring
5. Load balancing
6. Aggregating responses from multiple services

Example

Client request:

GET /api/orders/1

API Gateway:

 Validates JWT token


 Routes request to Order Service
 Returns response to client
Common API Gateway Tools

 Spring Cloud Gateway


 Netflix Zuul
 Kong
 NGINX

Benefits

 Simplifies client communication


 Centralized security
 Improves scalability
 Reduces client complexity

In summary:

An API Gateway is a centralized entry point in microservices architecture that routes client requests to
appropriate services and handles cross-cutting concerns like security, logging, and load balancing.

81. What is Circuit Breaker?

A Circuit Breaker is a design pattern used in microservices to prevent a system from repeatedly trying to
call a failing service.

It improves fault tolerance and prevents cascading failures in distributed systems.

Why It Is Needed

In microservices:

If Service A calls Service B, and Service B is down:

 A keeps trying to call B


 Threads get blocked
 System resources get exhausted
 Entire system may crash

Circuit Breaker prevents this.


How It Works

Circuit Breaker has three states:

1. Closed (Normal State)

 Requests pass through normally.


 Failures are monitored.
 If failures exceed a threshold → move to Open state.

2. Open (Failure State)

 Requests are immediately rejected.


 No call is made to the failing service.
 System returns fallback response.
 After a timeout → move to Half-Open state.

3. Half-Open

 Allows a limited number of test requests.


 If successful → move to Closed.
 If failure → move back to Open.

Example

If Payment Service is down:

Instead of:

Order Service → keeps calling Payment Service

Circuit Breaker:

 Stops calling it
 Returns fallback response like:
“Payment service temporarily unavailable”

Benefits

 Prevents cascading failures


 Improves system resilience
 Faster failure response
 Protects system resources

Common Tools

 Resilience4j
 Netflix Hystrix (deprecated)

In summary:

A Circuit Breaker is a fault-tolerance pattern that stops repeated calls to a failing service, protecting the system
from cascading failures and improving resilience in microservices architecture.

82. What is caching?

Caching is a technique used to store frequently accessed data in a temporary storage layer so that future
requests can be served faster without repeatedly querying the database or external service.

It improves performance and reduces system load.

Why Caching Is Needed

Without caching:

 Every request hits the database


 Increased latency
 Higher database load

With caching:

 Frequently used data is stored in memory


 Faster response time
 Reduced database calls

Example

Suppose we have:

GET /products/1

If product data is cached:


 First request → fetched from database and stored in cache
 Next requests → served directly from cache

No DB call needed.

Types of Caching

1. In-Memory Cache

 Stored in application memory


 Example: ConcurrentHashMap
 Fast but not shared across instances

2. Distributed Cache

 Shared across multiple application instances


 Example:
o Redis
o Memcached

Used in scalable systems.

Caching Strategies

 Cache Aside (Lazy Loading)


 Write Through
 Write Back
 Write Around

Most common: Cache Aside.

Benefits

 Improved performance
 Reduced database load
 Better scalability
 Lower latency

Challenges

 Cache invalidation (hard problem)


 Data consistency
 Memory usage

In summary:

Caching is a performance optimization technique where frequently accessed data is stored temporarily to reduce
database load and improve response time.

83. What is load balancing?

Load balancing is the process of distributing incoming network traffic across multiple servers to ensure
no single server becomes overloaded.

It improves:

 Performance
 Scalability
 High availability

Why Load Balancing Is Needed

If all client requests go to one server:

 That server may crash


 Response time increases
 System becomes unreliable

With load balancing:

 Traffic is distributed across multiple instances


 System remains stable under heavy load

Example

Suppose we have 3 application servers:

 Server A
 Server B
 Server C

Load balancer distributes requests like:

Client → Load Balancer → Any available server


Common Load Balancing Algorithms

1. Round Robin
Requests are distributed sequentially.
2. Least Connections
Sends request to server with least active connections.
3. IP Hash
Same client IP goes to same server.
4. Weighted Round Robin
Servers with higher capacity receive more traffic.

Types of Load Balancers

1. Hardware Load Balancer

Physical device.

2. Software Load Balancer

Examples:

 NGINX
 HAProxy
 AWS ELB

Benefits

 Improved performance
 High availability
 Fault tolerance
 Horizontal scalability

In summary:

Load balancing distributes incoming requests across multiple servers to improve performance, availability, and
scalability of an application.

84. What is vertical vs horizontal scaling?

Vertical scaling and horizontal scaling are two approaches to increase the capacity and performance of a
system.
1. Vertical Scaling (Scaling Up)

Vertical scaling means increasing the resources of a single server.

For example:

 Add more RAM


 Increase CPU cores
 Upgrade to faster SSD

You are making one machine more powerful.

Advantages:

 Simple to implement
 No major architecture change
 Easy to manage

Disadvantages:

 Hardware limits exist


 Expensive
 Single point of failure

Example:
If your database is slow → upgrade the server hardware.

2. Horizontal Scaling (Scaling Out)

Horizontal scaling means adding more servers and distributing the load.

Instead of upgrading one machine, you add multiple machines.

Example:

 Server A
 Server B
 Server C

Load balancer distributes traffic among them.

Advantages:

 Highly scalable
 Fault tolerant
 Better for large systems
Disadvantages:

 More complex
 Requires load balancing
 Data consistency challenges

Quick Comparison

Vertical:

 Increase power of one machine


 Limited scalability
 Easier

Horizontal:

 Add more machines


 Highly scalable
 More complex

In summary:

Vertical scaling increases the capacity of a single server, while horizontal scaling increases system capacity by
adding multiple servers and distributing the load.

85. What is Docker?

Docker is a containerization platform that allows applications to be packaged along with their
dependencies into lightweight, portable containers.

It ensures that an application runs consistently across different environments.

Why Docker Is Needed

Without Docker:

 Works on my machine problem


 Dependency conflicts
 Environment differences (dev, test, prod)

With Docker:

 Application + dependencies are packaged together


 Same behavior everywhere

What Is a Container?

A container:

 Is a lightweight isolated environment


 Shares the host OS kernel
 Contains application + runtime + libraries

Unlike virtual machines, containers are lightweight and start quickly.

Key Components of Docker

1. Docker Image
o Blueprint of application
o Read-only template
2. Docker Container
o Running instance of image
3. Dockerfile
o Script to create image

Example:

FROM openjdk:17
COPY [Link] [Link]
ENTRYPOINT ["java", "-jar", "[Link]"]

How It Works

1. Build image
2. Run container
3. Application runs in isolated environment

Benefits

 Portability
 Lightweight
 Faster deployment
 Environment consistency
 Easy scaling
Docker vs Virtual Machine

Docker:

 Shares OS kernel
 Lightweight
 Fast startup

VM:

 Full OS
 Heavy
 Slower startup

In summary:

Docker is a containerization platform that packages applications and their dependencies into lightweight,
portable containers to ensure consistent execution across environments.

86. What is CI/CD?

CI/CD stands for Continuous Integration and Continuous Deployment (or Continuous Delivery). It is a
DevOps practice that automates the process of building, testing, and deploying applications.

It helps teams release software faster and more reliably.

1. Continuous Integration (CI)

Continuous Integration means:

 Developers frequently merge code into a shared repository.


 Every commit triggers:
o Automatic build
o Automated tests

If something breaks → team is notified immediately.

Benefits:

 Early bug detection


 Prevents integration issues
 Improves code quality

Example flow:
Developer pushes code →
Jenkins/GitHub Actions runs build →
Tests execute automatically.

2. Continuous Delivery (CD)

Continuous Delivery means:

 After CI passes, the application is ready to be deployed automatically.


 Deployment may require manual approval.

Code is always in a deployable state.

3. Continuous Deployment

Continuous Deployment means:

 Every successful build is automatically deployed to production.


 No manual approval required.

CI/CD Pipeline Flow

1. Code commit
2. Build
3. Run tests
4. Package (e.g., Docker image)
5. Deploy to staging/production

Common CI/CD Tools

 Jenkins
 GitHub Actions
 GitLab CI
 CircleCI
 Azure DevOps

Why CI/CD Is Important

 Faster release cycles


 Reduced human errors
 Automated testing
 Improved reliability

In summary:

CI/CD is an automated software development practice where code changes are continuously integrated, tested,
and deployed to ensure faster, reliable, and consistent application delivery.

Encryption

Domain: Security

Definition (interview-ready):

Encryption is the process of converting readable data (plaintext) into unreadable data (ciphertext) using an
algorithm and a key, so that only authorized parties can decrypt it.

Key Points:

 Uses a key
 Reversible (with correct key)
 Used for secure communication
 Example: HTTPS, SSL/TLS

Flow:
Plaintext → Encrypt (with key) → Ciphertext
Ciphertext → Decrypt (with key) → Plaintext

Purpose:
Confidentiality

Encoding

Domain: Data representation

Definition:

Encoding is the process of converting data into another format so it can be safely transmitted or stored, but it is
NOT meant for security.

Key Points:
 No secret key involved
 Easily reversible
 Used for data formatting
 Example: Base64, UTF-8

Example:
Binary data → Base64 string
Used when sending images in JSON.

Purpose:
Data compatibility, not security.

If someone can decode it easily, it’s not secure.

Hashing

Domain: Security + Data Structures

Definition:

Hashing is a one-way process that converts data into a fixed-length hash value using a hash function.

Key Points:

 No key needed (usually)


 Not reversible
 Used for password storage
 Used in HashMap

Example:
Password → SHA-256 → Random-looking string

You cannot get original password back from hash.

Purpose:
Integrity + Secure storage

HTTP status codes are standardized 3-digit response codes returned by a server to indicate the result of a
client’s HTTP request.

They are grouped into 5 categories:

 1xx → Informational
 2xx → Success
 3xx → Redirection
 4xx → Client Error
 5xx → Server Error

Now I’ll explain the most commonly used ones.

✅ 2xx – Success Codes

200 OK

Request was successful.


Most common response for GET requests.

201 Created

Resource successfully created.


Used after POST when a new resource is created.

202 Accepted

Request accepted but processing is not completed yet.

204 No Content

Request successful but no response body is returned.


Common in DELETE operations.

🔁 3xx – Redirection Codes

301 Moved Permanently

Resource permanently moved to a new URL.

302 Found

Temporarily redirected to another URL.


304 Not Modified

Resource not changed since last request.


Used for caching.

❌ 4xx – Client Error Codes

400 Bad Request

Invalid request syntax or bad input.

401 Unauthorized

Authentication required or invalid credentials.

403 Forbidden

Authenticated but not allowed to access resource.

404 Not Found

Requested resource does not exist.

405 Method Not Allowed

HTTP method not supported for that endpoint.

406 Not Acceptable

Server cannot return response in requested format.

408 Request Timeout


Client took too long to send request.

409 Conflict

Request conflicts with current state of resource.


Example: Duplicate entry.

415 Unsupported Media Type

Request body format not supported (e.g., sending XML when JSON expected).

422 Unprocessable Entity

Validation error — request syntax correct but semantic error.

💥 5xx – Server Error Codes

500 Internal Server Error

Generic server error.

501 Not Implemented

Server does not support requested functionality.

502 Bad Gateway

Invalid response from upstream server.

503 Service Unavailable

Server temporarily overloaded or under maintenance.


504 Gateway Timeout

Upstream server did not respond in time.

Quick Summary

2xx → Success
3xx → Redirection
4xx → Client mistakes
5xx → Server issues

In summary:
HTTP status codes communicate the result of an HTTP request, indicating whether it was successful, redirected,
failed due to client error, or failed due to server error.

React – Most Asked Interview Questions (Mid-Level / Full-Stack)


Fundamentals

1. What is React?

eact is a JavaScript library used for building user interfaces, especially single-page applications (SPAs).

It was developed by Facebook and is primarily used to build dynamic, fast, and interactive web applications.

Core Idea of React

React is based on:

 Component-based architecture
 Virtual DOM
 Unidirectional data flow

1. Component-Based Architecture

In React, the UI is divided into small reusable components.


Example:

 Navigation bar
 Sidebar
 ProductCard
 Footer

Each component:

 Has its own logic


 Can be reused
 Can be maintained independently

2. Virtual DOM

React uses a Virtual DOM to improve performance.

Instead of updating the entire real DOM:

 React updates the Virtual DOM first


 Compares it with previous version (diffing)
 Updates only changed parts in the real DOM

This makes React fast.

3. Unidirectional Data Flow

Data flows from:

Parent → Child

This makes application behavior predictable and easier to debug.

Example
function Welcome() {
return <h1>Hello, Anjali</h1>;
}

This is a simple React component.

Why React Is Popular

 Reusable components
 Fast rendering (Virtual DOM)
 Large ecosystem
 Strong community support
 Works well with REST APIs

In summary:

React is a component-based JavaScript library for building fast and interactive user interfaces using Virtual
DOM and unidirectional data flow.

2. What is Virtual DOM?

Virtual DOM is a lightweight in-memory representation of the real DOM used by React to optimize UI
updates.

Instead of directly updating the browser’s real DOM, React first updates the Virtual DOM.

Why Virtual DOM Is Needed

Updating the real DOM is:

 Slow
 Expensive
 Performance-heavy

Every DOM manipulation triggers:

 Reflow
 Repaint

Which impacts performance.

How Virtual DOM Works

Step-by-step process:

1. Initial render → React creates a Virtual DOM tree.


2. When state changes → React creates a new Virtual DOM.
3. React compares the old and new Virtual DOM (Diffing algorithm).
4. React identifies only the changed elements.
5. Only those changes are updated in the real DOM.

This process is called Reconciliation.


Example

If we update only a button text:

Instead of re-rendering the entire page,


React updates only that specific button in the real DOM.

Why It Improves Performance

 Minimizes direct DOM manipulation


 Updates only necessary parts
 Efficient diffing algorithm

Important Note

Virtual DOM is not faster than real DOM by itself.


It is faster because it reduces unnecessary DOM operations.

In summary:

Virtual DOM is a lightweight copy of the real DOM that React uses to efficiently detect changes and update
only the modified parts of the UI, improving performance.

3. What are components?

Components in React are independent, reusable pieces of UI that define how a part of the user interface
looks and behaves.

They are the building blocks of a React application.

4. Functional vs Class components?

Functional and Class components are two ways to create components in React, but functional
components are now preferred due to simplicity and Hooks support.

1. Functional Components

Functional components are simple JavaScript functions that return JSX.

function Welcome() {
return <h1>Hello</h1>;
}

With React Hooks (like useState, useEffect), functional components can:

 Manage state
 Handle lifecycle logic
 Perform side effects

Advantages:

 Simpler syntax
 Less boilerplate
 Easier to read and test
 Better performance (in modern React)

2. Class Components

Class components use ES6 classes and extend [Link].

class Welcome extends [Link] {


render() {
return <h1>Hello</h1>;
}
}

To manage state:

[Link] = { count: 0 };

To handle lifecycle:

 componentDidMount()
 componentDidUpdate()
 componentWillUnmount()

Disadvantages:

 More boilerplate
 this binding issues
 Harder to maintain

Key Differences

Functional:

 Uses Hooks
 No this keyword
 Simpler and modern
Class:

 Uses lifecycle methods


 Uses this
 Older approach

In summary:

Functional components are simpler and use Hooks for state and lifecycle management, while class components
use ES6 classes and traditional lifecycle methods. Modern React applications prefer functional components.

5. What are props?

Props (short for properties) are read-only inputs passed from a parent component to a child component
in React.

They are used to transfer data between components.

Why Props Are Needed

React follows unidirectional data flow, meaning:

Data flows from:

Parent → Child

Props allow parents to pass dynamic data to child components.

Example

Parent Component:

function App() {
return <Welcome name="Anjali" />;
}

Child Component:

function Welcome(props) {
return <h1>Hello, {[Link]}</h1>;
}

Here:
 name is a prop.
 It is passed from App to Welcome.

Important Characteristics

 Props are read-only.


 A child component cannot modify props.
 If data needs to change, parent must update it.

Props with Destructuring


function Welcome({ name }) {
return <h1>Hello, {name}</h1>;
}

Cleaner and commonly used.

In summary:

Props are read-only data passed from parent to child components in React, enabling dynamic and reusable UI
components.

6. What is state?

State in React is a built-in object that stores data that can change over time and affects how a component
renders.

Unlike props, state is managed inside the component.

When state changes, React automatically re-renders the component.

Why State Is Needed

State is used when:

 Data changes based on user interaction


 UI needs to update dynamically
 We need to track things like counters, form input, toggles, etc.

Example (Functional Component with useState)


import { useState } from "react";

function Counter() {
const [count, setCount] = useState(0);

return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}

Here:

 count is the state value.


 setCount updates the state.
 When setCount is called → component re-renders.

Key Differences Between Props and State

Props:

 Passed from parent


 Read-only

State:

 Managed inside component


 Can change
 Triggers re-render when updated

In summary:

State is a React component’s internal, mutable data that controls dynamic behavior and causes the component to
re-render when updated.

Hooks (VERY IMPORTANT)

8. What is useState?

useState is a React Hook that allows functional components to manage state.

It returns:
 Current state value
 A function to update the state

Example:

const [count, setCount] = useState(0);

 count → current state


 setCount() → updates state
 Updating state triggers re-render

9. What is useEffect?

useEffect is a Hook used to handle side effects in functional components.

Side effects include:

 API calls
 Subscriptions
 Timers
 DOM manipulation

Example:

useEffect(() => {
[Link]("Component mounted");
});

10. What is dependency array in useEffect?

The dependency array controls when the effect runs.

useEffect(() => {
// effect logic
}, [dependency]);

 React runs the effect when dependency changes.

11. When does useEffect run?

Depends on dependency array:

1. No dependency array
→ Runs after every render
2. Empty array []
→ Runs only once (on mount)
3. With dependencies [value]
→ Runs when that value changes

12. What is cleanup function in useEffect?

Cleanup function is returned from useEffect.

It runs:

 Before component unmounts


 Before next effect runs

Example:

useEffect(() => {
const timer = setInterval(() => {
[Link]("Running");
}, 1000);

return () => {
clearInterval(timer);
};
}, []);

Used to:

 Clear timers
 Remove event listeners
 Cancel subscriptions

13. What is useRef?

useRef is a Hook that stores a mutable value that does not cause re-render when updated.

Used for:

 Accessing DOM elements


 Storing previous values
 Persisting values between renders

Example:

const inputRef = useRef(null);

14. What is useMemo?

useMemo is a Hook that memoizes a computed value to avoid unnecessary recalculations.


It improves performance.

const result = useMemo(() => {


return expensiveCalculation(value);
}, [value]);

It recalculates only when dependency changes.

15. What is useCallback?

useCallback memoizes a function so that it is not recreated on every render.

Used mainly for performance optimization.

const handleClick = useCallback(() => {


[Link]("Clicked");
}, []);

Prevents unnecessary re-renders in child components.

16. What are custom hooks?

Custom hooks are reusable functions that use React Hooks internally to share logic between components.

They must start with use.

Example:

function useCounter() {
const [count, setCount] = useState(0);
return { count, setCount };
}

Custom hooks:

 Promote code reuse


 Keep components clean
 Encapsulate logic

Quick Summary

useState → Manage state


useEffect → Handle side effects
Dependency array → Controls when effect runs
Cleanup → Prevent memory leaks
useRef → Persist mutable value without re-render
useMemo → Memoize values
useCallback → Memoize functions
Custom hooks → Reusable hook logic

8. What is useRef?
9. What is useMemo?
10. What is useCallback?
11. What are custom hooks?

Rendering & Performance

17. What is Reconciliation?

Reconciliation is the process React uses to compare the previous Virtual DOM with the new Virtual
DOM and determine the minimal set of changes needed to update the real DOM.

When state or props change:

1. React creates a new Virtual DOM.


2. Compares it with the previous one (diffing algorithm).
3. Updates only the changed parts in the real DOM.

This makes React efficient and fast.

18. What is key in React and why is it important?

A key is a unique identifier used by React to identify elements in a list.

Example:

{[Link](item => (
<li key={[Link]}>{[Link]}</li>
))}

Why key is important:

 Helps React identify which items changed, added, or removed.


 Improves performance.
 Prevents UI bugs during reordering.

Important:
Keys must be unique and stable.
Avoid using array index as key if list can change.

19. What causes re-render in React?

A component re-renders when:


1. State changes
2. Props change
3. Parent component re-renders
4. Context value changes

React re-renders the component function, but updates real DOM only if needed.

20. How to optimize React performance?

Common techniques:

 Use [Link] to prevent unnecessary re-renders


 Use useMemo for expensive calculations
 Use useCallback for function memoization
 Use proper keys in lists
 Avoid inline functions in large lists
 Lazy loading components
 Code splitting

21. What is memoization in React?

Memoization is a performance optimization technique where React stores the result of a function and
reuses it if inputs haven’t changed.

It avoids unnecessary recalculations or re-renders.

Used with:

 useMemo (for values)


 useCallback (for functions)
 [Link] (for components)

22. What is [Link]?

[Link] is a higher-order component that prevents re-rendering of a component if its props have not
changed.

Example:

const MyComponent = [Link](function MyComponent({ name }) {


return <h1>{name}</h1>;
});

React will re-render only if name changes.

It performs shallow comparison of props.


Quick Summary

Reconciliation → Virtual DOM diffing process


Key → Unique identifier for list elements
Re-render → Caused by state/props/parent updates
Performance optimization → Memoization + avoiding unnecessary renders
[Link] → Prevents unnecessary component re-renders

Forms & Events

23. Controlled vs Uncontrolled Components

Controlled Components

In controlled components, form data is controlled by React state.

 Input value is stored in state.


 React controls the input.
 Every change updates state.

Example:

function Form() {
const [name, setName] = useState("");

return (
<input
value={name}
onChange={(e) => setName([Link])}
/>
);
}

Here:

 value comes from state


 onChange updates state

Advantages:

 Full control over input


 Easy validation
 Predictable behavior

Uncontrolled Components
In uncontrolled components, form data is handled by the DOM itself.

We use useRef to access values.

Example:

function Form() {
const inputRef = useRef();

return <input ref={inputRef} />;


}

Value is accessed using:

[Link]

When to use:

 Simple forms
 Less control required

Key Difference

Controlled → React manages state


Uncontrolled → DOM manages state

Controlled components are preferred in most real-world applications.

24. How do you handle form submission?

We handle form submission using the onSubmit event.

Example:

function Form() {
const [name, setName] = useState("");

const handleSubmit = (e) => {


[Link]();
[Link](name);
};

return (
<form onSubmit={handleSubmit}>
<input
value={name}
onChange={(e) => setName([Link])}
/>
<button type="submit">Submit</button>
</form>
);
}

Steps:

1. Attach onSubmit
2. Prevent default behavior
3. Process form data

25. How do you prevent page refresh?

By default, submitting a form refreshes the page.

To prevent it, use:

[Link]();

Inside the submit handler:

const handleSubmit = (e) => {


[Link]();
};

This stops the browser’s default form submission behavior.

Quick Summary

Controlled → State-driven inputs


Uncontrolled → DOM-driven inputs
Form submission → Use onSubmit handler
Prevent refresh → Use [Link]()

State Management

26. What is Lifting State Up?

Lifting state up is the process of moving state from a child component to a common parent component so
that multiple child components can share the same data.

When two components need the same data:

Instead of:

 Each maintaining its own state

We:
 Move the state to their closest common parent
 Pass data down via props

Example:

function Parent() {
const [value, setValue] = useState("");

return (
<>
<Input value={value} setValue={setValue} />
<Display value={value} />
</>
);
}

This ensures a single source of truth.

27. What is Prop Drilling?

Prop drilling is the process of passing props through multiple intermediate components just to reach a
deeply nested child.

Example:

Parent → Child → Grandchild → Target

Even if Child and Grandchild don’t use the prop, they must pass it.

Problem:

 Makes code messy


 Hard to maintain
 Reduces readability

Solution:

 Context API
 Redux

28. What is Context API?

Context API is a React feature that allows data to be shared globally without passing props manually at
every level.

Used for:

 Theme
 Authentication
 Language
 Global settings

Example:

const ThemeContext = [Link]();

function App() {
return (
<[Link] value="dark">
<Child />
</[Link]>
);
}

Child can access it using:

const theme = useContext(ThemeContext);

Prevents prop drilling.

29. What is Redux?

Redux is a predictable state management library used to manage global application state.

It stores all application state in a single centralized store.

Used when:

 Large applications
 Complex state sharing
 Multiple components need same data

30. How Does Redux Flow Work?

Redux follows a unidirectional data flow.

Flow steps:

1. Component dispatches an action


2. Action is sent to reducer
3. Reducer updates the store
4. Store notifies subscribed components
5. UI re-renders

Redux Core Concepts


 Store → Holds state
 Action → Describes what happened
 Reducer → Pure function that updates state
 Dispatch → Sends action

Example:

dispatch({ type: "INCREMENT" });

Reducer:

function counter(state = 0, action) {


switch ([Link]) {
case "INCREMENT":
return state + 1;
default:
return state;
}
}

Quick Summary

Lifting state up → Move state to common parent


Prop drilling → Passing props through many layers
Context API → Share global data without prop drilling
Redux → Centralized global state management
Redux flow → Action → Reducer → Store → UI update

Backend Integration

31. How do you call APIs in React?

We typically call APIs using:

 fetch()
 axios

Usually inside useEffect for data fetching.

Example using fetch:

useEffect(() => {
fetch("/api/users")
.then(res => [Link]())
.then(data => setUsers(data))
.catch(err => [Link](err));
}, []);

Modern approach with async/await:

useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch("/api/users");
const data = await [Link]();
setUsers(data);
} catch (error) {
[Link](error);
}
};

fetchData();
}, []);

32. Where do you call APIs — component or service?

Best practice: Call APIs from a separate service layer, not directly inside components.

Why?

 Keeps components clean


 Reusable API logic
 Easier testing
 Separation of concerns

Example:

[Link]

export const getUsers = async () => {


const response = await fetch("/api/users");
return [Link]();
};

Then inside component:

useEffect(() => {
getUsers().then(setUsers);
}, []);

Clean architecture > messy components.

33. How do you handle loading state?

We use state to track loading.

Example:

const [loading, setLoading] = useState(false);

useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const data = await getUsers();
setUsers(data);
} finally {
setLoading(false);
}
};

fetchData();
}, []);

Render conditionally:

if (loading) return <p>Loading...</p>;

34. How do you handle error state?

Use another state variable for errors.

const [error, setError] = useState(null);

Inside API call:

try {
const data = await getUsers();
setUsers(data);
} catch (err) {
setError("Failed to fetch data");
}

Render conditionally:

if (error) return <p>{error}</p>;

Production apps may use:

 Toast notifications
 Error boundaries
 Retry mechanisms

35. How do you store JWT on frontend?

Common options:

 localStorage
 sessionStorage
 HTTP-only cookies

Typical example:

[Link]("token", jwtToken);

And attach to API requests:


headers: {
Authorization: `Bearer ${token}`
}

36. Where should tokens be stored — localStorage vs cookies?

This is an important security question.

localStorage

Pros:

 Easy to use
 Accessible via JS

Cons:

 Vulnerable to XSS attacks

HTTP-only Cookies (Recommended for security)

Pros:

 Not accessible via JavaScript


 Protected from XSS

Cons:

 Need CSRF protection


 Slightly more setup

Best Practice

For production-level secure apps:

 Store JWT in HTTP-only cookies


 Use secure flag
 Use same-site settings

If simple app:

 localStorage is common but less secure


Quick Summary

API call → useEffect + async


Use service layer → clean architecture
Loading → loading state
Error → error state
JWT storage → Prefer HTTP-only cookies for security
localStorage → vulnerable to XSS

🔥 That last one is common in full-stack interviews.

Routing

37. What is React Router?

React Router is a library used for handling navigation and routing in React applications.

It enables Single Page Applications (SPAs) to navigate between different views without reloading the entire
page.

Instead of traditional page refresh:

 React Router dynamically renders components based on the URL.

Example:

<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>

When URL changes:

 React Router renders the matching component


 No full page reload

38. What is BrowserRouter?

BrowserRouter is a router implementation that uses the browser’s HTML5 History API to keep the UI in
sync with the URL.

It enables clean URLs like:

/dashboard
/profile
/orders
Instead of hash-based URLs like:

/#/dashboard

Example usage:

import { BrowserRouter } from "react-router-dom";

<BrowserRouter>
<App />
</BrowserRouter>

BrowserRouter:

 Wraps the entire application


 Enables routing
 Uses pushState internally

39. What are Protected Routes?

Protected routes are routes that are accessible only to authenticated users.

If a user is not logged in:

 They are redirected to login page.

Common use case:

 Dashboard
 Profile
 Admin pages

Example:

function ProtectedRoute({ children }) {


const token = [Link]("token");

return token ? children : <Navigate to="/login" />;


}

Usage:

<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>

If token exists → show dashboard


If not → redirect to login
Quick Summary

React Router → Handles navigation in SPA


BrowserRouter → Uses HTML5 history API for clean URLs
Protected Routes → Restrict access to authenticated users

Advanced

40. What is Lazy Loading in React?

Lazy loading in React means loading components only when they are needed instead of loading
everything at once.

This improves:

 Initial load time


 Performance
 Bundle size

React provides [Link]() for this.

Example:

import React, { Suspense } from "react";

const Dashboard = [Link](() => import("./Dashboard"));

function App() {
return (
<Suspense fallback={<p>Loading...</p>}>
<Dashboard />
</Suspense>
);
}

Here:

 Dashboard is loaded only when rendered.


 Suspense shows fallback while loading.

41. What is Code Splitting?

Code splitting is the process of splitting a large JavaScript bundle into smaller chunks that can be loaded
on demand.

By default:
 React apps bundle everything into one large JS file.
 That slows initial load.

Code splitting:

 Breaks code into smaller files.


 Loads only required chunks.

React supports it via:

 [Link]
 Dynamic import()
 Route-based splitting

Common in large apps for performance optimization.

42. What are Higher-Order Components (HOC)?

A Higher-Order Component (HOC) is a function that takes a component and returns a new enhanced
component.

It is used to reuse component logic.

Pattern:

function withLogger(WrappedComponent) {
return function EnhancedComponent(props) {
[Link]("Component rendered");
return <WrappedComponent {...props} />;
};
}

Usage:

const Enhanced = withLogger(MyComponent);

HOCs are used for:

 Authentication logic
 Logging
 Authorization
 Data fetching

Note:
With Hooks, HOCs are less common now but still important to know.

43. What is Strict Mode?

StrictMode is a development-only tool in React that helps identify potential problems in an application.
It does NOT affect production build.

Usage:

<[Link]>
<App />
</[Link]>

What it does:

 Detects unsafe lifecycle methods


 Warns about deprecated APIs
 Detects side effects
 Intentionally double-invokes certain functions in development

Important:
It runs components twice in development to detect side-effect bugs.

Quick Summary

Lazy loading → Load components when needed


Code splitting → Split large bundles into smaller chunks
HOC → Function that enhances a component
StrictMode → Dev tool to detect issues

Hi, I’m Anjali. I’m a Computer Science graduate with a strong interest in software development, especially
backend development using Java, SQL, and REST APIs.

I started my career as a Full Stack Developer, and later worked as a System Engineer on a contract role at IISc.
While that role did not involve heavy day-to-day coding, it gave me exposure to production systems, structured
environments, and how large institutions operate.

Throughout that period, I never stepped away from coding. I consistently worked on strengthening my core
computer science fundamentals and built projects on my own, including a full-stack e-commerce application
using Spring Boot, React, and SQL.

Through these projects, I focused on backend concepts like REST API design, database design, transactions,
and clean service-layer logic. I’ve always been genuinely interested in software engineering, and coding has
been a constant part of my journey even when my job role didn’t fully reflect it.

Right now, I’m actively looking for a backend or full-stack role where I can work more closely with code, apply
the skills I’ve built, and continue growing as a software engineer.
E-COMMERCE PROJECT — ONE GO EXPLANATION

One of my main projects is an E-commerce application built using Spring Boot for the backend, React for the
frontend, and a SQL database for data storage.

The goal of this project was to design and build a complete end-to-end system that handles core e-commerce
functionalities such as user management, product management, order processing, and data consistency.

From a functional perspective, the application allows users to register and log in, browse products, add items to
a cart, and place orders.

From a backend perspective, the system is implemented as a REST-based application using Spring Boot,
following a layered architecture with controller, service, and repository layers. Each layer has a clear
responsibility, which helps keep the code modular and maintainable.

I paid particular attention to database design. I designed relational tables for users, products, orders, and order
items, and defined proper relationships using foreign keys to maintain data integrity.

For API design, I followed REST principles and used appropriate HTTP methods and status codes. I
implemented validation at the service layer to handle invalid requests and ensure business rules are enforced
before interacting with the database.

One important aspect I worked on was order processing. When a user places an order, the system creates an
order record along with associated order items, and this is handled within a transactional boundary to ensure
consistency—so either the entire order is saved successfully or nothing is persisted.

On the frontend, I used React to consume the backend APIs and manage application state. The frontend
communicates with the backend through REST APIs.

Through this project, I gained hands-on experience in designing REST APIs, structuring backend applications,
relational database design, and integrating a React frontend with a Spring Boot backend.

SOLID is a set of five design principles that help us write clean, maintainable, scalable, and loosely coupled
object-oriented code.

They help improve readability, flexibility, and make systems easier to extend without breaking existing
functionality.

SOLID stands for:

Single Responsibility Principle,


Open Closed Principle,
Liskov Substitution Principle,
Interface Segregation Principle,
Dependency Inversion Principle.
1. Single Responsibility Principle (SRP)

This principle states that a class should have only one reason to change, meaning it should have only one
responsibility.

For example, in an e-commerce system, instead of having one class handling order processing, payment, and
email notifications, we should separate them into different classes like OrderService, PaymentService, and
NotificationService.

This makes code easier to maintain and test because changes in one functionality don’t affect others.

[Link] Closed Principle (OCP)

This principle says that software entities should be open for extension but closed for modification.

That means we should be able to add new functionality without changing existing code.

For example, if we have a Payment interface, we can add new payment methods like UPI or CreditCard by
creating new classes implementing the interface instead of modifying existing logic.

This reduces risk of breaking existing functionality.

[Link] Substitution Principle (LSP)

This principle states that a subclass should be able to replace its parent class without affecting program
correctness.

In other words, child classes should behave in a way that doesn’t break expectations of the base class.

A classic example is if a Bird class has a fly() method, then a Penguin subclass should not extend Bird because
penguins cannot fly.

Violating this principle leads to runtime issues and unexpected behavior.

4. Interface Segregation Principle (ISP)

This principle says that clients should not be forced to depend on interfaces they do not use.

Instead of having a large interface with many methods, we should split it into smaller, more specific interfaces.

For example, instead of a single Worker interface with work() and eat(), we can create separate interfaces like
Workable and Eatable so classes only implement what they need.
This improves flexibility and reduces unnecessary dependencies.

5. Dependency Inversion Principle (DIP)

This principle states that high-level modules should not depend on low-level modules. Both should depend on
abstractions.

Also, abstractions should not depend on details — details should depend on abstractions.

In Spring Boot, this is achieved using dependency injection. For example, a service depends on a Payment
interface instead of a specific implementation like CreditCardPayment.

This allows us to switch implementations easily and improves testability.

In summary, SOLID principles help us build scalable and maintainable software by promoting loose coupling,
high cohesion, and better separation of concerns.

They are widely used in enterprise applications and frameworks like Spring Boot.

“In my project I applied SRP by separating controller, service, and repository layers, and used DIP through
dependency injection.”

You might also like