ArrayList:
An ArrayList is a resizable array implementation of the List interface in Java. It stores elements in a dynamic array and allows fast random access using an index. However, insertion and deletion (except at the end) are slower as they may require shifting elements.
✅ What is a LinkedList?
A LinkedList is a doubly-linked list implementation of the List and Deque interfaces in Java. Each element is stored in a node, which contains data and pointers to the previous and next nodes, making insertions and deletions faster, especially in the middle.
-
data
-
a reference (link) to the next node in the list.
✅ Node Structure
Each Node contains:
javaT data; // The value
Node<T> next; // Pointer to next node
✅ LinkedList Operations
1. Add (at end) – add(T data)
-
Create a new node.
-
If the list is empty, set
head = newNode. -
Otherwise, traverse to the last node and set its
nextto the new node.
2. Remove (by value) – remove(T data)
-
Check if head is null → return false.
-
If
head.data == data, remove it byhead = head.next. -
Otherwise, traverse until
current.next.data == data, then skip it bycurrent.next = current.next.next.
✅ Java Code – Custom LinkedList
javapublic class CustomLinkedList<T> {
private Node<T> head;
private static class Node<T> {
T data;
Node<T> next;
Node(T data) {
this.data = data;
}
}
// Add element at end
public void add(T data) {
Node<T> newNode = new Node<>(data);
if (head == null) {
head = newNode;
} else {
Node<T> current = head;
while (current.next != null) {
current = current.next;
}
current.next = newNode;
}
}
// Remove element by value
public boolean remove(T data) {
if (head == null) return false;
if (head.data.equals(data)) {
head = head.next;
return true;
}
Node<T> current = head;
while (current.next != null && !current.next.data.equals(data)) {
current = current.next;
}
if (current.next == null) return false;
current.next = current.next.next;
return true;
}
// Display list
public void display() {
Node<T> current = head;
while (current != null) {
System.out.print(current.data + " -> ");
current = current.next;
}
System.out.println("null");
}
}
✅ Main Method to Test
javapublic class Main {
public static void main(String[] args) {
CustomLinkedList<Integer> list = new CustomLinkedList<>();
list.add(10);
list.add(20);
list.add(30);
list.display(); // 10 -> 20 -> 30 -> null
list.remove(20);
list.display(); // 10 -> 30 -> null
}
}Key Differences:
Feature
ArrayList
LinkedList
Memory
Less overhead
More overhead (due to node pointers)
Access Time (get)
Fast (O(1))
Slow (O(n))
Insertion/Deletion
Slow (O(n))-needs shifting
Fast (O(1)) if position is known
Internal Structure
Dynamic array
Doubly linked list
Better For
Frequent access
Frequent insertions/deletions
2)failfast, failsafe in java
In Java, Fail-Fast and Fail-Safe are two mechanisms used by iterators to handle concurrent modifications (changes to a collection while it is being iterated). ✅ 1. Fail-Fast Iterator
A Fail-Fast iterator in Java immediately throws a ConcurrentModificationException if the collection is structurally modified after the iterator is created (except through the iterator’s own methods).
It is not safe in multi-threaded environments..
Collections: ArrayList, HashMap, HashSet, etc.
Use Case: Used when thread safety is not required or single-threaded iteration is fine.
Mechanism: Uses an internal modCount to detect structural changes.
🔹 Example:
java
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
for (String s : list) {
list.add("C"); // Structural modification during iteration
// Throws ConcurrentModificationException
}//Use Java 8 forEach + Consumerlist.forEach(s -> {
list.add("C"); // Still throws ConcurrentModificationException ❌
});
✅ 2. Fail-Safe Iterator
Definition: Allows concurrent modifications while iterating. It does not throw ConcurrentModificationException.
Collections: CopyOnWriteArrayList, ConcurrentHashMap, etc.
Use Case: Used in multi-threaded environments where safe concurrent access is needed.
Mechanism: Iterates over a clone or a snapshot of the collection, so changes don't affect the iteration.
🔹 Example:
java
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("A");
list.add("B");
for (String s : list) {
list.add("C"); // No exception; works safely
}🔹 Example for ConcurrentHashMap:import java.util.concurrent.ConcurrentHashMap;
public class FailSafeExample {
public static void main(String[] args) {
ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>();
map.put(1, "Apple");
map.put(2, "Banana");
map.put(3, "Cherry");
// Iterate and modify concurrently
for (Integer key : map.keySet()) {
System.out.println(key + " => " + map.get(key));
if (key == 2) {
map.put(4, "Date"); // Adding a new key while iterating (safe)
map.remove(1); // Removing an existing key (safe)
}
}
System.out.println("Final Map: " + map);
}
}
🔄 Comparison Table:
Feature Fail-Fast Fail-Safe
Throws Exception? Yes (ConcurrentModificationException) No
Thread-Safe? No Yes
Iterates on live data? Yes No (snapshot or copy)
Examples ArrayList, HashMap CopyOnWriteArrayList, ConcurrentHashMap
3) What is a Red-Black Tree?
A Red-Black Tree is a binary search tree (BST) with an extra bit of data — the color red or black — assigned to each node to ensure the tree remains approximately balanced during insertions and deletions. approximately balanced during insertions and deletions.
🔒 Red-Black Tree Properties:
Every node is either RED or BLACK.
The root is always BLACK.
Red nodes cannot have red children (no two reds in a row).
Every path from a node to its descendant NULL nodes contains the same number of BLACK nodes.
New nodes are inserted as RED.
🔄 Why Red-Black Tree?
It ensures that the height of the tree is always O(log n) — meaning operations like insert, delete, search are efficient.
📦 Used in Java:
Java Class Internally uses Red-Black Tree
TreeMap ✅ Yes
TreeSet ✅ Yes
NavigableMap ✅ Yes
🔧 Example:
java
Map<Integer, String> map = new TreeMap<>();
map.put(10, "Ten");
map.put(5, "Five");
map.put(20, "Twenty");
// Internally uses Red-Black Tree for sorted keys
🧠 Key Benefit:
Keeps operations predictably fast (O(log n)) even after many insertions/deletions.
Ensures balanced tree without needing full rebalancing like AVL trees.
4)override in streams in java
In Java Streams, the term "override" is not directly applicable like in OOP (method overriding).But, we can handle handle duplicates, override values, or merge results in collectors like Collectors.toMap()
✅ 1. Override in Collectors.toMap() using merge function
If two keys collide, you can "override" by choosing which value to keep.
🔸 Example:
javaList<String> names = Arrays.asList("Alice", "Bob", "Alice");
// Map<String, Integer>: key=name, value=length
Map<String, Integer> nameLengthMap = names.stream()
.collect(Collectors.toMap(
name -> name, // key mapper
name -> name.length(), // value mapper
(existing, replacement) -> replacement // merge function (override old with new)
));
System.out.println(nameLengthMap);
✅ Output:
java{Alice=5, Bob=3}
Here, Alice appears twice — second value overrides the first using (old, new) -> new.
✅ 2. Override values by latest occurrence (custom object)
javaclass Person {
String name;
int age;
// constructor + getter
}
List<Person> list = Arrays.asList(
new Person("John", 25),
new Person("John", 30), // newer one
new Person("Alice", 28)
);
Map<String, Person> map = list.stream()
.collect(Collectors.toMap(
Person::getName,
p -> p,
(oldVal, newVal) -> newVal // override old with new
));
map.forEach((k, v) -> System.out.println(k + " -> " + v.getAge()));
✅ Output:
rustJohn -> 30
Alice -> 28
✅ 3. Grouping vs Overriding
If you want all values for a key, use groupingBy():
javaMap<String, List<Person>> grouped = list.stream()
.collect(Collectors.groupingBy(Person::getName));
But if you want only one value per key (override others), use toMap() with a merge function.
✅ Summary: How to "override" in stream collectors
Case Use Duplicate keys, want latest .toMap(..., (old, new) -> new)Duplicate keys, want first .toMap(..., (old, new) -> old)Keep all (grouping) .collect(Collectors.groupingBy(...))
5)What is the output, here I'm trying to modify a Map<Integer, String> using a method called modify(map) and print the map before and after
main(){
Map<Integer, String> map = new HashMap<>();
map.put(1,"A");
map.put(3,"B");
map.put(4,"C");//5,E
map.put(2,"D");
System.out.println(map);
modify(map);
System.out.println(map);
}
private static void modify(Map<Integer, String> map) {
map.put(
map.put(5, "E"); // Example modification
map.remove(1); // Another example: remove key 1
}
Final Correct Code:
javaimport java.util.HashMap;
import java.util.Map;
public class ModifyMapExample {
public static void main(String[] args) {
Map<Integer, String> map = new HashMap<>();
map.put(1, "A");
map.put(3, "B");
map.put(4, "C");
map.put(2, "D");
System.out.println("Before modification: " + map);
modify(map);
System.out.println("After modification: " + map);
}
private static void modify(Map<Integer, String> map) {
map.put(5, "E"); // Example modification
map.remove(1); // Another example: remove key 1
}
}
6) ✅ Two parallel streams:
One does a write operation (e.g., generating or saving data)
One does a read operation (e.g., reading from a list or DB)
✅ 2nd operation output as input to a third stream
Which performs a final write operation (e.g., saving/logging/etc)
e.g., List<String> existingData = Arrays.asList("apple","banana","cherry");
✅ Realistic Java Example (with comments):
✅ Two parallel streams run independently:
Write Stream → generates or saves data
Read Stream → reads data (from a list/DB)
✅ The output of the 2nd (read) stream is passed as input to a third stream,
which does a final write operation (like save/log/transform)
✅ Final Structure Overview
pgsql
Step 1: Parallel Stream 1 - Generate new data (write)
Step 2: Parallel Stream 2 - Read existing data (read)
Step 3: Use output of Step 2 (read data) as input to another Stream
→ perform final write (save/log/update)
✅ Java Code Example
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.*;
public class StreamPipelineExample {
public static void main(String[] args) throws Exception {
// Simulate DB read data
List<String> existingData = Arrays.asList("apple", "banana", "cherry");
// Thread pool for async parallel streams
ExecutorService executor = Executors.newFixedThreadPool(2);
// 1. Parallel Stream 1 – Write operation (generates new data)
CompletableFuture<List<String>> writeFuture = CompletableFuture.supplyAsync(() -> {
return IntStream.range(1, 6).parallel()
.mapToObj(i -> "Generated_" + i)
.peek(data -> System.out.println("Writing new data: " + data))
.collect(Collectors.toList());
}, executor);
// 2. Parallel Stream 2 – Read operation (reads data)
CompletableFuture<List<String>> readFuture = CompletableFuture.supplyAsync(() -> {
return existingData.parallelStream()
.peek(data -> System.out.println("Reading: " + data))
.collect(Collectors.toList());
}, executor);
// 3. Final Stream: uses read output, does a write/save transformation
CompletableFuture<Void> finalOperation = readFuture.thenAccept(readList -> {
readList.stream()
.parallel()
.map(String::toUpperCase)
.peek(data -> System.out.println("Final write: " + data))
.forEach(result -> {
// Final save/log operation simulation
});
});
// Wait for all
CompletableFuture.allOf(writeFuture, readFuture, finalOperation).get();
executor.shutdown();
}
}
✅ Sample Output (Order may vary due to parallelism):
yaml
Writing new data: Generated_3
Writing new data: Generated_4
Writing new data: Generated_2
Writing new data: Generated_1
Writing new data: Generated_5
Reading: banana
Reading: cherry
Reading: apple
Final write: BANANA
Final write: CHERRY
Final write: APPLE
7)String s1 = "abc";
String s2= new String("abc");
String s3 = s2.intern();
String s4 = new String("abc")
========output=========
s1==s2 //false
s3==s1 // true
s2==s4 //false
public class Main {
public static void main(String[] args) {
String s1 = "abc"; // string literal, goes to string pool
String s2 = new String("abc"); // new object in heap
String s3 = s2.intern(); // refers to string pool version ("abc")
String s4 = new String("abc"); // another new object in heap
System.out.println(s1 == s2); // false: pool vs heap
System.out.println(s3 == s1); // true: both refer to string pool "abc"
System.out.println(s2 == s4); // false: different heap objects
}
}
8) Merge two unsorted List of integers into single sorted list by using streams apiList<Integer> l1 = Arrrays.asList(1,3,5,6);
List<Integer> l2 = Arrrays.asList(1,8,9,3);
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Test122 {
public static void main(String[] args) {
List<Integer> l1 = Arrays.asList(1, 3, 5, 6);
List<Integer> l2 = Arrays.asList(1, 8, 9, 3);
List<Integer> merged = Stream.concat(l1.stream(), l2.stream()).sorted().collect(Collectors.toList());
System.out.println(merged); //[1, 1, 3, 3, 5, 6, 8, 9]
List<Integer> union = Stream.concat(l1.stream(), l2.stream()).sorted().collect(Collectors.toList());
System.out.println(union); //[1, 1, 3, 3, 5, 6, 8, 9]
//Intersection (Common Elements)
List<Integer> intersection = l1.stream().filter(l2::contains).sorted().collect(Collectors.toList());
System.out.println(intersection); //[1, 3]
// Sum of All Elements
int sum = Stream.concat(l1.stream(), l2.stream()).mapToInt(Integer::intValue).sum();
System.out.println(sum); 36
}
}
9)HTTP Status code errors
✅ HTTP Status Code Summary for REST APIs
Situation HTTP Code Meaning / Use Case ✅ Success Responses Record Found 200 OKRequest successful; data returned Empty List or No Body 204 No ContentRequest successful; no data to return Resource Created 201 CreatedNew resource successfully created (used with POST) Accepted but processing later 202 AcceptedRequest accepted, but not yet processed Partial Content 206 Partial ContentUsed when returning partial data (pagination, range headers, etc.) ⚠️ Client Errors Bad Input / Validation Fail 400 Bad RequestClient sent invalid or malformed data Unauthorized 401 UnauthorizedAuthentication required or failed Forbidden 403 ForbiddenAuthenticated but access is denied Not Found 404 Not FoundResource does not exist Method Not Allowed 405 Method Not AllowedMethod (GET, POST, etc.) not allowed on this endpoint Conflict 409 ConflictRequest could not be completed due to a conflict (e.g., duplicate ID) Unprocessable Entity 422 Unprocessable EntitySemantic error in request, though syntactically correct Too Many Requests 429 Too Many RequestsRate-limiting: client has sent too many requests in a short time ❌ Server Errors Internal Server Error 500 Internal Server ErrorGeneric server-side error Not Implemented 501 Not ImplementedServer does not support the requested functionality Bad Gateway 502 Bad GatewayInvalid response from upstream server (e.g., microservice error) Service Unavailable 503 Service UnavailableServer temporarily unavailable (overload, maintenance) Gateway Timeout 504 Gateway TimeoutServer timeout while waiting for another service
10)Runnable,Serializable
✅ 1. Runnable Interface🔹 Definition:
Runnable is a functional interface used to define a task to be executed by a thread. It contains a single method:
java
public interface Runnable {
void run();
}
🔹 Use Case:
When you want to create a thread or execute code in parallel.
✅ Example:
java
public class Main {
public static void main(String[] args) {
Runnable task = () -> System.out.println("Running in a thread using lambda");
Thread thread = new Thread(task);
thread.start();
//new Thread(() -> System.out.println("Running in a thread (inline lambda)")).start();
}
}
✅ Output:
Running in a thread
✅ 2. Serializable Interface
🔹 Definition:
Serializable is a marker interface (no methods) used to indicate that an object can be converted into a byte stream — so it can be saved to a file or sent over a network.
java
public interface Serializable {
// no methods
}
🔹 Use Case:
Used in object persistence, caching, session storage, or remote communication (RMI, sockets).
✅ Example:
java
import java.io.*;
public class Employee implements Serializable {
private int id;
private String name;
public Employee(int id, String name) {
this.id = id;
this.name = name;
}
}
Now Employee objects can be serialized:
java
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("emp.ser"));
out.writeObject(new Employee(101, "John"));// If not implements with implements Serializable then❌ Throws NotSerializableExceptionout.close();
🔁 Key Differences Between Runnable and Serializable
Feature RunnableSerializableType Functional Interface Marker Interface Purpose Used for threading Used for object serialization Contains Methods Yes (run()) No methods Common Use Multithreading Save/send object data Package java.langjava.io
11)class loader subsystems in javaIn Java, the Class Loader Subsystem is a vital part of the Java Virtual Machine (JVM) responsible for loading, linking, and initializing classes into memory when required.
✅ Overview: Class Loader Subsystem in Java
The Class Loader Subsystem performs three major activities:
1. Loading
2. Linking
3. Initialization
✅ 1. Class Loading (Loading Phase)
It loads the .class file (bytecode) into the JVM memory.
Converts the bytecode into a Class object.
Done using different types of class loaders.
🔸 Types of Class Loaders:
Loader Type Description
Bootstrap ClassLoader Loads core Java classes from rt.jar (e.g., java.lang.*)
Extension ClassLoader Loads classes from ext directory (lib/ext)
Application ClassLoader Loads classes from application's classpath (src, .jar)
Custom/User-defined ClassLoader Developer-defined class loading logic
✅ 2. Class Linking (Verification, Preparation, Resolution)
After loading, the class goes through:
🔹 a) Verification
Checks if bytecode is valid and safe (no memory violations).
Ensures JVM security.
🔹 b) Preparation
Allocates memory for static variables and assigns default values.
🔹 c) Resolution
Replaces symbolic references (e.g., class names, method names) with actual memory addresses.
✅ 3. Class Initialization
Static blocks and static variables are initialized.
Done in the order of declaration inside the class.
✅ Class Loading Flow Diagram (Simplified)
csharp
Copy
Edit
.class file
↓
[Bootstrap ClassLoader]
↓
[Extension ClassLoader]
↓
[Application ClassLoader]
↓
[Custom ClassLoader (optional)]
↓
JVM Memory (Method Area - Class Info)
✅ Example to Print Class Loader Info
java
public class Demo {
public static void main(String[] args) {
System.out.println("ClassLoader of String: " + String.class.getClassLoader());
System.out.println("ClassLoader of Demo: " + Demo.class.getClassLoader());
}
}
✅ Output:
javascript
ClassLoader of String: null // Bootstrap loader
ClassLoader of Demo: sun.misc.Launcher$AppClassLoader
✅ Summary
Phase Responsibility
Loading Loads .class into memory via class loaders
Linking Verifies, prepares, and resolves classes
Init Executes static blocks and initializes fields
12)largest number in the array Integer[] in = {23, 56, 1, 98, 34};, you can use Java 8 Stream or a traditional loop.import java.util.Arrays;
public class MaxValueStream {
public static void main(String[] args) {
Integer[] arr = {23, 56, 1, 98, 34};
int max = Arrays.stream(arr)
.max(Integer::compare)
.orElseThrow(() -> new RuntimeException("No maximum value found"));
System.out.println("Maximum value: " + max);
}
}
Output:
Maximum value: 98
✅ Using Traditional Loop:
java
public class MaxValueLoop {
public static void main(String[] args) {
Integer[] in = {23, 56, 1, 98, 34};
int max = in[0];
for (int i = 1; i < in.length; i++) {
if (in[i] > max) {
max = in[i];
}
}
System.out.println("Maximum value: " + max);
}
}
13)To filter the list and get only the strings that start with "S", you can use Java 8 Stream API like this:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<String> lst = Arrays.asList("Swamy", "Java", "Simple", "JPA");
List<String> result = lst.stream()
.filter(s -> s.startsWith("S"))
.collect(Collectors.toList());
System.out.println(result);
}
}
14)Find Count Palindromes in given String
import java.util.*;
import java.util.stream.*;
public class Main {
public static void main(String[] args) {
String str = "I am learning springboot";
long count = Arrays.stream(str.split(" "))
.filter(word -> isPalindrome(word))
.count();
System.out.println("Number of palindromes: " + count);
}
private static boolean isPalindrome(String word) {
String reversed = new StringBuilder(word).reverse().toString();
return word.equalsIgnoreCase(reversed);
}
}
15)load() and get() in hibernate
In Hibernate, both load() and get() methods are used to retrieve data from the database, but they behave differently in several important ways.
✅ 1. get() Method
Definition: Immediately hits the database to fetch the object.
Returns: null if the object is not found.
Usage: When you are not sure if the object exists and want to handle the absence gracefully.
java
Employee emp = session.get(Employee.class, 101);
if (emp != null) {
System.out.println(emp.getName());
} else {
System.out.println("Employee not found");
}
Key Points:
Executes a SQL SELECT immediately.
Useful when you want to check existence or use the actual data right away.
✅ 2. load() Method
Definition: Returns a proxy object without hitting the database immediately.
Returns: A proxy object; throws ObjectNotFoundException if the actual object is accessed but not found.
Usage: When you are sure the object exists and want to delay the fetch until it’s needed (lazy loading).
java
Employee emp = session.load(Employee.class, 101);
System.out.println(emp.getName()); // DB hit occurs here if not already loaded
🔁 Comparison Table
Feature get()load()DB Hit Immediately Lazy (when object accessed) Return if Not Found nullThrows ObjectNotFoundException Proxy Usage No Yes Use Case When unsure if object exists When sure object exists
16)session & sessionFactory in hibernate
In Hibernate, Session and SessionFactory are core interfaces used for interacting with the database, but they serve different purposes.
✅ 1. SessionFactory (Hibernate)
🔹 Definition:
SessionFactory is a thread-safe, heavyweight object in Hibernate used to create Session objects. It is a factory for Session instances and represents a single database configuration (usually per database).
🔹 Key Points:
Created once during application startup (singleton pattern).
Heavyweight: Holds DB connection info, SQL dialect, entity mappings, etc.
Internally maintains a pool of database connections and prepared statements.
Immutable – once configured, cannot be changed.
🔹 Creation Example:
java
Configuration cfg = new Configuration().configure("hibernate.cfg.xml").addAnnotatedClass(Student.class); // <-- Annotation-based mapping;
SessionFactory factory = cfg.buildSessionFactory();
🔹 Use Case:
Use SessionFactory to open multiple sessions that interact with the database.
✅ 2. Session (Hibernate)
🔹 Definition:
A Session is a lightweight, non-thread-safe object used to interact with the database in Hibernate. It wraps a JDBC connection and provides CRUD methods (save, update, delete, etc.).
🔹 Key Points:
Created from SessionFactory.
Not thread-safe: one session per thread/request.
Represents a unit of work (typically one database transaction).
Maintains a first-level cache (stores recently accessed entities).
Provides methods like save(), get(), load(), update(), delete().
🔹 Usage Example:
java
Session session = factory.openSession();
Transaction tx = session.beginTransaction();
Employee emp = new Employee(101, "John", 5000);
session.save(emp);
tx.commit();
session.close();
🧠 Differences between Session and SessionFactory
Feature SessionFactory Session Type Heavyweight, Thread-safe Lightweight, Not thread-safe Lifecycle Application-wide,singleton Per request or transaction Purpose Create Session objects Interact with DB (CRUD operations) Caching Holds second-level cache Holds first-level cache Creation Cost Expensive to create Cheap to create
🔄 Typical Flow in Hibernate App:
Load config (hibernate.cfg.xml)
Create SessionFactory
Open Session
Begin Transaction
Perform CRUD operations
Commit Transaction
Close Session
Close SessionFactory at app shutdown
17)Profiles in springboot
🔹 1. What is a Profile in Spring Boot?
A profile in Spring Boot is a way to segregate different parts of your application configuration and beans, so that they are only activated in specific environments, such as dev, test, prod, etc.
🔹 2. Why are Profiles used?
To run the same application in different environments with different configurations.
Avoid hardcoding values specific to one environment (like database URLs, credentials, logging levels).
Enable or disable specific beans conditionally.
🔹 3. Where do you define Profiles?
➤ In application properties/yaml:
properties# application.properties
spring.profiles.active=dev
Or using command-line:
bash
-Dspring.profiles.active=prod
Or in application.yml:
yaml
spring:
profiles:
active: dev
🔹 4. How to use Profiles?
✅ a) Separate application-<profile>.properties/yml files:
properties
# application-dev.properties
server.port=8081
db.url=jdbc:mysql://localhost:3306/devdb
# application-prod.properties
server.port=8082
db.url=jdbc:mysql://prodhost:3306/proddb
✅ b) Use @Profile annotation with beans:
java
@Configuration
public class AppConfig {
@Bean
@Profile("dev")
public DataSource devDataSource() {
// Return dev datasource
}
@Bean
@Profile("prod")
public DataSource prodDataSource() {
// Return prod datasource
}
}
✅ c) Use @Profile on component classes:
java
@Component
@Profile("test")
public class TestMailService implements MailService {
public void sendEmail() {
System.out.println("Fake email sent (Test profile)");
}
}
🔹 5. Default Profile
If no profile is set, Spring Boot uses the default profile.
🔹 6. Activate Multiple Profiles
properties
spring.profiles.active=dev,debug
✅ Summary Table:
Environment File Active Beans
dev application-dev.yml Beans with @Profile("dev")
prod application-prod.yml Beans with @Profile("prod")
test application-test.yml Beans with @Profile("test")
18)@ManyToOne in Hibernate (JPA) — Full Explanation
🔹 1. What is @ManyToOne?
@ManyToOne is a JPA annotation used to define a many-to-one relationship between two entities.
It means many records in one entity (child) are associated with one record in another entity (parent).
🔹 2. Use Case Example
Let’s say you have:
Many Employees belonging to one Department
⚙️ Relation:
One Department → many Employees
Each Employee → one Department
🔹 3. Entity Example:
✅ Department Entity (Parent)
java
Copy
Edit
import jakarta.persistence.*;
import java.util.List;
@Entity
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String deptName;
// Optional - for bidirectional mapping
@OneToMany(mappedBy = "department")
private List<Employee> employees;
// Getters and setters
}
✅ Employee Entity (Child)
java
Copy
Edit
import jakarta.persistence.*;
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String empName;
@ManyToOne
@JoinColumn(name = "dept_id") // foreign key in Employee table
private Department department;
// Getters and setters
}
🔹 4. Generated Tables
Hibernate will create:
department(id, dept_name)
employee(id, emp_name, dept_id) ← with dept_id as foreign key
🔹 5. Saving Data Example
java
Department dept = new Department();
dept.setDeptName("IT");
Employee e1 = new Employee();
e1.setEmpName("Alice");
e1.setDepartment(dept);
Employee e2 = new Employee();
e2.setEmpName("Bob");
e2.setDepartment(dept);
entityManager.persist(dept); // Persist parent first
entityManager.persist(e1);
entityManager.persist(e2);
🔹 6. Key Points
Feature
Behavior
Annotation used
@ManyToOne
Side of the relation
Declared in the child (many side)
Foreign key
Automatically managed by JPA (@JoinColumn)
Optional fetch type
FetchType.LAZY by default for @ManyToOne
19) print values in reverse order based on keys in Map
Map<Integer, String> mp = new HashMap<>();
mp.put(1, "Z");
mp.put(12, "Y");
mp.put(8, "X");
import java.util.HashMap;
import java.util.Map;
public class Test2 {
public static void main(String[] args) {
Map<Integer, String> mp = new HashMap<>();
mp.put(1, "Z");
mp.put(12, "Y");
mp.put(8, "X");
mp.entrySet().stream().sorted((e1, e2) -> e2.getKey().compareTo(e1.getKey())).forEach(System.out::println);
}
}
Output:
12=Y
8=X
1=Z
20)why you need kafka in your project
We needed Kafka in our project to support asynchronous, reliable communication between microservices.
Our services had to interact in real-time without being tightly coupled, especially in high-load scenarios
like order processing and user notifications. Kafka allowed us to build an event-driven architecture
that was scalable, fault-tolerant, and supported message replay for auditing and recovery
21)Calculates the next 3 working days (i.e., skipping weekends) from today's
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
public class NextWorkingDays {
public static void main(String[] args) {
LocalDate today = LocalDate.now();
List<LocalDate> workingDays = new ArrayList<>();
int count = 0;
LocalDate date = today.plusDays(1); // start from tomorrow
while (count < 3) {
DayOfWeek day = date.getDayOfWeek();
if (day != DayOfWeek.SATURDAY && day != DayOfWeek.SUNDAY) {
workingDays.add(date);
count++;
}
date = date.plusDays(1);
}
System.out.println("Next 3 working days from today:");
workingDays.forEach(System.out::println);
}
}
22)complete runnable Java 8+ code reconstructed from your handwritten notes, including:
Employee, Project, and EmployeeDatabase classes
Example with Runnable to print:
Maximum salary in HR department
Maximum salary department-wise
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.stream.Collectors;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
class Project {
private String projectCode;
private String name;
private String client;
private String projectLeadName;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
class Employee {
private int id;
private String name;
private String department;
private List<Project> projects;
private double salary;
private String gender;
}
class EmployeeDatabase {
public static List<Employee> getAllEmployees() {
Project p1 = new Project("P001", "Alpha", "ABC Corp", "Sumit");
Project p2 = new Project("P002", "Beta", "XYZ Corp", "John");
Employee e1 = new Employee(1, "John", "Development", Arrays.asList(p1, p2), 80000, "Male");
Employee e2 = new Employee(2, "Lisa", "HR", Collections.singletonList(p1), 55000, "Female");
Employee e3 = new Employee(3, "Raj", "HR", Arrays.asList(p1, p2), 75000, "Male");
Employee e4 = new Employee(4, "Priya", "Development", Collections.singletonList(p2), 90000, "Female");
Employee e5 = new Employee(5, "Amit", "Finance", Arrays.asList(p1), 70000, "Male");
return Arrays.asList(e1, e2, e3, e4, e5);
}
}
public class Main {
public static void main(String[] args) {
List<Employee> employees = EmployeeDatabase.getAllEmployees();
// a) Maximum salary in the "HR" department
Runnable hrMaxSalary = () -> {
Employee maxHrEmp = employees.stream()
.filter(e -> "HR".equalsIgnoreCase(e.getDepartment()))
.max(Comparator.comparingDouble(Employee::getSalary))
.orElseThrow(() -> new RuntimeException("No employee found in HR"));
System.out.println("Highest paid in HR: " + maxHrEmp.getName() + " → ₹" + maxHrEmp.getSalary());
};
// b) Maximum salary department-wise
Runnable deptWiseMaxSalary = () -> {
Map<String, Optional<Employee>> maxByDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.maxBy(Comparator.comparingDouble(Employee::getSalary))
));
System.out.println("\nHighest paid employee department-wise:");
maxByDept.forEach((dept, empOpt) -> {
empOpt.ifPresent(emp ->
System.out.println(dept + " → " + emp.getName() + " (₹" + emp.getSalary() + ")")
);
});
};
// Execute both runnables
hrMaxSalary.run();
deptWiseMaxSalary.run();
}
}23) What is the out put?
interface A{
void a();
void b();
}
class B implements A{
void a(){
}
void b(){
}
}
❌ Compilation Error
This code will NOT compile because: You are reducing the visibility of the inherited methods
a() and b() from public (in the interface) to package-private (default).
When implementing an interface, all methods must be public.
✅ Fix:
You must mark a() and b() as public:
class B implements A {
public void a() {
}
public void b() {
}
}
24)Exception Propagation in Java
Exception Propagation refers to the process by which an exception is thrown up the call stack until it is caught by a suitable catch block.
🔷 Key Concept:
If an exception occurs in a method and is not caught there, it propagates to the caller of that method. This continues up the call stack until the exception is either caught or causes program termination.
✅ Example:
java
public class PropagationExample {
static void methodA() {
int result = 10 / 0; // ArithmeticException
}
static void methodB() {
methodA(); // No try-catch here
}
static void methodC() {
try {
methodB();
} catch (ArithmeticException e) {
System.out.println("Caught in methodC: " + e);
}
}
public static void main(String[] args) {
methodC();
}
}
▶️ Output:
csharp
Caught in methodC: java.lang.ArithmeticException: / by zero
🧠 Flow:
methodA() throws ArithmeticException
No try-catch in methodA() → it goes to methodB()
No try-catch in methodB() → it goes to methodC()
methodC() has a matching catch → exception is handled here
🔸 Applies to:
Only unchecked exceptions (like NullPointerException, ArithmeticException)
Checked exceptions must be either caught or declared using throws
✅ Checked Exception Propagation Example:
java
void methodX() throws IOException {
throw new IOException("File not found");
}
void methodY() throws IOException {
methodX();
}
void methodZ() {
try {
methodY();
} catch (IOException e) {
System.out.println("Caught: " + e);
}
}
Summary Table:
Exception Type Propagation Allowed Without Declaration? Needs throws? Unchecked ✅ Yes ❌ No Checked ❌ No (unless declared) ✅ Yes
25) What is try-with-resources?
try-with-resources is a Java feature (introduced in Java 7) that makes it easy and safe to work with resources like files, streams, connections, etc.
➡️ These resources are automatically closed after the try block, without needing a finally block.
🔷 Why Use It?
Because forgetting to close resources like files or database connections can cause:
Memory leaks
File locks
Slow performance
✅ Simple Example
javaimport java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class TryWithResourceExample {
public static void main(String[] args) {
try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
String line = br.readLine();
System.out.println("First line: " + line);
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
}
}
}
🧠 Explanation:
BufferedReader is the resource that reads from a file.
The resource is declared inside the try().
After the block ends, Java automatically closes BufferedReader even if an exception occurs.
🔷 Requirements:
The resource must implement the AutoCloseable interface (or Closeable which extends it).
Examples: FileReader, BufferedReader, Scanner, Connection, InputStream, OutputStream, etc.
✅ try With Multiple Resources
javatry (
FileReader fr = new FileReader("data.txt");
BufferedReader br = new BufferedReader(fr)
) {
System.out.println(br.readLine());
} catch (IOException e) {
e.printStackTrace();
}
🔷 What Happens Internally?
Equivalent to:
java
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("data.txt"));
// use br
} catch (IOException e) {
// handle exception
} finally {
if (br != null) {
br.close(); // manually closing
}
}
➡️ try-with-resources does this automatically, and more safely.
✅ Custom Resource Example
javaclass MyResource implements AutoCloseable {
public void doSomething() {
System.out.println("Working...");
}
@Override
public void close() {
System.out.println("Closing resource...");
}
}
public class CustomResourceTest {
public static void main(String[] args) {
try (MyResource res = new MyResource()) {
res.doSomething();
}
}
}
▶ Output:
mathematicaWorking...
Closing resource...
🔚 Summary
Feature Description Introduced in Java 7 Closes resources Automatically at end of try block Resource requirement Must implement AutoCloseable or Closeable Reduces boilerplate No need for finally block Common use cases Files, Streams, JDBC, Sockets
26)print values in reverse order based on keys in Map
Output:
Output:
12=Y
8=X
1=Z
import java.util.HashMap;
import java.util.Map;
public class Test2 {
public static void main(String[] args) {
Map<Integer, String> mp = new HashMap<>();
mp.put(1, "Z");
mp.put(12, "Y");
mp.put(8, "X");
mp.entrySet().stream().sorted((e1, e2) -> e2.getKey().compareTo(e1.getKey())).forEach(System.out::println);
}
}
27) Calculate average of the below code
List<List<String>> nlst =List.of(
List.of("1","2","4"),
List.of("2","5","9"));
package sample;
import java.util.List;
public class Test4 {
public static void main(String[] args) {
List<List<String>> nlst =List.of(
List.of("1","2","4"),
List.of("2","5","9"));
double avg= nlst.stream().flatMap(List::stream).mapToInt(Integer::parseInt).average().orElse(0);
System.out.println(avg);
}
}
//Output: 3.8333333333333335
28) a)Merge 2 below 2 lists, b) Calculate sum of 2 list numbers
List<Integer> l1 = Arrays.asList(1, 3, 5, 6);
List<Integer> l2 = Arrays.asList(1, 8, 9, 3);
package sample;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Test122 {
public static void main(String[] args) {
List<Integer> l1 = Arrays.asList(1, 3, 5, 6);
List<Integer> l2 = Arrays.asList(1, 8, 9, 3);
List<Integer> merged = Stream.concat(l1.stream(), l2.stream()).sorted().collect(Collectors.toList());
System.out.println(merged);
//Intersection (Common Elements)
List<Integer> intersection = l1.stream().filter(l2::contains).sorted().collect(Collectors.toList());
System.out.println(intersection);
// Sum of All Elements
int sum = Stream.concat(l1.stream(), l2.stream()).mapToInt(Integer::intValue).sum();
System.out.println(sum);
}
}
29)Count no.of duplicate characters
package sample;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Sampl1 {
public static void main(String[] args) {
String str = "BairuLingaswamy";
Map<Character, Long> map = str.chars().mapToObj(c->(char)c).collect(Collectors.groupingBy(Function.identity(),Collectors.counting()))
.entrySet().stream()
.filter(entry -> entry.getValue() > 1) // only repeated characters
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
System.out.println(map);
}
}
30)What is the output for below program
import java.util.HashMap;
import java.util.Map;
public class Test5 {
public static void main(String[] args) {
Map<String, String> testing = new HashMap<String, String>();
testing.put(new String("a"), "aaa");
testing.put(new String("a"), "bbb");
System.out.println(testing);
}
}
//output {a=bbb}
31)What is the output for below program
interface House{
public default String getAddress(){
return "House adress House Class "; //
}
}
interface Bungalow extends House{
public default String getAddress(){
return "Bungalow address Bungalow Class ";
}
}
class MyHouse implements Bungalow, House{
}
/*class MyHouse2 implements Bungalow2, House2 {
@Override
public String getAddress() {
return Bungalow2.super.getAddress(); // or House2.super.getAddress() this gives error
}
}
//Output: Bungalow address Bungalow Class
*/
public class TestClass {
public static void main(String[] args) {
House ci = new MyHouse();
System.out.println(ci.getAddress());
}
}
//Output: Bungalow address Bungalow Class
32)What is the output for below program
interface A {
default void show() {
System.out.println("A");
}
}
interface B {
default void show() {
System.out.println("B");
}
}
class DiamindC implements A, B {
// Must override show() to resolve conflict
public void show() {
System.out.println("C");
A.super.show(); // or B.super.show();
}
public static void main(String[] args) {
DiamindC obj = new DiamindC();
obj.show(); // Output: C
}
}
//Output
C
A
33)What is the output for below program
int[] arr = {3, 2, 1};
for (int i : arr) {
System.out.println(arr[i]);
}
⚠️ This will throw ArrayIndexOutOfBoundsException
❌ Why?
You're using enhanced for-loop (for (int i : arr)), which iterates over the values of the array, not the indexes.
So the loop values will be:
java
i = 3 → arr[3] ❌ // Out of bounds
i = 2 → arr[2] ✅
i = 1 → arr[1] ✅
But arr[3] is invalid → exception.
✅ Fix Option 1: Just print the values
java
for (int val : arr) {
System.out.println(val);
}
✅ Fix Option 2: Use traditional for-loop for indexes
java
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
34)Write the logic for auto-generate a custom Employee ID like INFY1001, INFY1002, etc.Employee.java
-----------
@Data
@NoArgConstructor
@AllArgConstructor
@Entity
public class Employee{
@ID
@GeneratedValue(strategy = GenerationType.AUTO) //Custom Auto generation like INFY+ID
private Long EmployeeId;
private String EmployeeName;
private Long phoneNo;
private LocalDate dateOfJoin;
//
}
1️⃣ Employee.java
package com.example.demo.entity;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.GenericGenerator;
import java.time.LocalDate;
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
@Id
@GeneratedValue(generator = "emp_id_generator")
@GenericGenerator(
name = "emp_id_generator",
strategy = "com.example.demo.generator.EmployeeIdGenerator"
)
private String employeeId;
private String employeeName;
private Long phoneNo;
private LocalDate dateOfJoin;
}
Note: Change Long employeeId → String employeeId to hold values like INFY1001.
2️⃣ Custom ID Generator – EmployeeIdGenerator.java
package com.example.demo.generator;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.Statement;
public class EmployeeIdGenerator implements IdentifierGenerator {
@Override
public Serializable generate(SharedSessionContractImplementor session, Object object) {
try {
Statement stmt = session.connection().createStatement();
ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM Employee");
int count = 0;
if (rs.next()) {
count = rs.getInt(1);
}
return "INFY" + String.format("%04d", count + 1); // INFY0001, INFY0002...
} catch (Exception e) {
throw new RuntimeException("Failed to generate custom employee ID", e);
}
}
}
35)Spring boot initializers
In Spring Boot, initializers refer to components or mechanisms that run some code at application startup—before your application begins serving requests.
They’re commonly used for tasks like:
Loading seed data into the database.
Preloading configuration or caches.
Running migrations.
Setting up logging or metrics.
Types of Initializers in Spring Boot
1. ApplicationRunner
Runs after the Spring application context is loaded and the application has started.
Gives access to application arguments.
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class MyAppRunner implements ApplicationRunner {
@Override
public void run(org.springframework.boot.ApplicationArguments args) {
System.out.println("Application started with arguments: " + args.getNonOptionArgs());
}
}
2. CommandLineRunner
Similar to ApplicationRunner, but receives raw String[] args.
Often used for quick startup logic.
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) {
System.out.println("CommandLineRunner executed at startup!");
}
}
Difference:
ApplicationRunner → works with parsed ApplicationArguments object.
CommandLineRunner → works with raw String[] arguments.
3. ApplicationContextInitializer
Runs before the Spring context is refreshed.
Useful for modifying the context or environment early.
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
public class MyContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext context) {
System.out.println("ApplicationContextInitializer called before refresh!");
}
}
Registration in SpringApplication:
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MySpringBootApp.class);
app.addInitializers(new MyContextInitializer());
app.run(args);
}
4. @PostConstruct
Method-level annotation to run code after bean initialization.
import jakarta.annotation.PostConstruct;
import org.springframework.stereotype.Component;
@Component
public class MyInitializerBean {
@PostConstruct
public void init() {
System.out.println("@PostConstruct executed after bean creation");
}
}
5. @EventListener(ApplicationReadyEvent.class)
Runs after the application is fully started and ready to serve requests.
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class StartupListener {
@EventListener(ApplicationReadyEvent.class)
public void afterStartup() {
System.out.println("Application is fully ready!");
}
}
✅ When to use what
Database seed or initial cache load → CommandLineRunner / ApplicationRunner
Modify Spring context before refresh → ApplicationContextInitializer
Bean-specific setup → @PostConstruct
Run after app fully ready → @EventListener(ApplicationReadyEvent.class)
36) What is parent dependency and why in spring boot?
In Spring Boot, a parent dependency usually refers to a parent POM in Maven (or parent Gradle configuration in Gradle) that your project inherits from.
In Maven, you can only have one parent POM, and in Spring Boot projects that’s usually:
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.0</version> <!-- Example version -->
<relativePath/> <!-- lookup from the repository -->
</parent>
What is the Parent Dependency?
It’s not a “normal” dependency that gets packaged into your project.
Instead, it’s a base Maven configuration that your project inherits from.
It’s like a template that provides:
Default versions of libraries.
Preconfigured build settings.
Plugin configurations (e.g., Maven Compiler Plugin).
Dependency management section for Spring Boot starters.
Why Use the Parent Dependency in Spring Boot?
1. Dependency Management
Without the parent POM, you’d have to manually specify versions for every Spring and third-party dependency.
With it, Spring Boot’s parent manages a BOM (Bill of Materials) so you can just write:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
…and Maven automatically picks the correct version.
2. Default Plugin Configuration
Configures Maven Compiler Plugin to use Java 17 (or your chosen java.version).
Configures Surefire Plugin for running tests.
Configures Spring Boot Maven Plugin for packaging as a runnable JAR.
3. Sensible Defaults
UTF-8 encoding.
Reproducible builds.
Default resource filtering.
4. Easier Upgrades
When you change the <version> in the parent POM, all managed dependencies and plugin versions update consistently.
When You Might Not Use It
If your project already has a corporate or multi-module parent POM, you might skip spring-boot-starter-parent and instead import the Spring Boot dependency BOM manually:
xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.3.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
37)finds all pairs of numbers from the array whose sum equals a given target (here 7).
int[] in = {2, 4, 3, 5, 7, 8, -1};
int target = 7;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
public class PairSumExample {
public static void main(String[] args) {
int[] in = {2, 4, 3, 5, 7, 8, -1};
int target = 7;
// Brute force with streams
IntStream.range(0, in.length)
.forEach(i ->
IntStream.range(i + 1, in.length)
.filter(j -> in[i] + in[j] == target)
.forEach(j -> System.out.println("(" + in[i] + ", " + in[j] + ")"))
);
}
}
38)Find the employee with the second highest salary using streams.
Print their first name + last name concatenated.
package abc2;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
class Employee {
private String firstName;
private String lastName;
private int salary;
public Employee(String firstName, String lastName, int salary) {
this.firstName = firstName;
this.lastName = lastName;
this.salary = salary;
}
public String getFirstName() { return firstName; }
public String getLastName() { return lastName; }
public int getSalary() { return salary; }
}
public class SecondHSal {
public static void main(String[] args) {
List<Employee> lst = Arrays.asList(
new Employee("John", "Doe", 5000),
new Employee("Jane", "Smith", 7000),
new Employee("Alex", "Johnson", 6000),
new Employee("Mike", "Brown", 7000) // duplicate salary
);
// Find second highest salary employee and print full name
String secondHighestName = lst.stream()
.sorted(Comparator.comparingInt(Employee::getSalary).reversed())
.map(emp -> emp.getFirstName() + " " + emp.getLastName())
.distinct()
.skip(1) // skip highest
.findFirst()
.orElse("No Employee Found");
System.out.println("Second Highest Salary Employee: " + secondHighestName);
}
}
39)lazy and eagar loading in Hibernate
Lazy Loading
Definition:
Lazy Loading in Hibernate is a fetching strategy where associated entities or collections are not loaded immediately with the parent object but are fetched only when accessed.
Example:
If an Employee has a list of Projects, the projects are loaded from the database only when getProjects() is called.
(Default fetching type for @OneToMany and @ManyToMany is LAZY).
Eager Loading
Definition:
Eager Loading in Hibernate is a fetching strategy where associated entities or collections are fetched immediately along with the parent object, regardless of whether they are accessed or not.
Example:
If an Employee has a list of Projects, both employee and projects are loaded together in a single query (using JOIN).
(Default fetching type for @OneToOne and @ManyToOne is EAGER).