📚 Welcome to Java & Spring Boot
Java is the world's most widely deployed programming language — running on 3 billion+ devices. It powers Android apps, enterprise back-end systems, banking platforms, e-commerce sites and cloud applications. Java developers are among the highest paid in India — ₹6–40 LPA depending on experience and skills.
Spring Boot is the most popular Java framework for building production-ready applications fast. Together, Java + Spring Boot is the #1 skill combination for back-end and full-stack Java roles at companies like TCS, Infosys, Wipro, Cognizant and top product companies.
💡 How to use this page: Click any topic in the left sidebar. All notes load instantly — no page reloads. Every section has real working Java code examples. Use Prev / Next to go in order from beginner to advanced.
☕ Java Basics & Syntax
BeginnerJava Platform — JDK, JRE, JVM
| Component | Full Name | Contains | Used By |
|---|---|---|---|
| JDK | Java Development Kit | JRE + Compiler (javac) + Tools (jar, javadoc) | Developers — to write and compile Java code |
| JRE | Java Runtime Environment | JVM + Core Libraries (java.lang, java.util, etc.) | End users — to run Java applications |
| JVM | Java Virtual Machine | Bytecode interpreter, JIT compiler, Garbage Collector | Executes .class bytecode files on any OS |
💡 Write Once, Run Anywhere: Java source code (.java) → compiled by javac to bytecode (.class) → JVM reads bytecode and executes it on ANY platform. This is why Java runs identically on Windows, Mac, Linux and Android.
Data Types, Variables & Control Flow
// Primitive data types
int age = 25;
double salary = 85000.50;
boolean isActive = true;
char grade = 'A';
long bigNum = 9876543210L;
// String — not primitive, it's an object
String name = "Hari Krishna";
String msg = "Hello, " + name; // Concatenation
String info = String.format("Name: %s, Age: %d", name, age);
// Control flow
if (age >= 18) {
System.out.println("Adult");
} else {
System.out.println("Minor");
}
// Enhanced switch (Java 14+)
String dayType = switch (day) {
case "MON", "TUE", "WED", "THU", "FRI" -> "Weekday";
case "SAT", "SUN" -> "Weekend";
default -> "Invalid";
};Methods & Varargs
// Method with return type
public static int add(int a, int b) {
return a + b;
}
// Method overloading — same name, different params
public static double add(double a, double b) {
return a + b;
}
// Varargs — variable number of arguments
public static int sum(int... numbers) {
int total = 0;
for (int n : numbers) total += n;
return total;
}
sum(1, 2, 3); // 6
sum(10, 20); // 30
sum(5, 5, 5, 5); // 20String Methods — Most Asked in Interviews
String s = " Hello World ";
s.length() // 15
s.trim() // "Hello World"
s.toUpperCase() // " HELLO WORLD "
s.toLowerCase() // " hello world "
s.contains("World") // true
s.replace("World", "Java") // " Hello Java "
s.split(" ") // ["", "", "Hello", "World", "", ""]
s.charAt(2) // 'H'
s.substring(2, 7) // "Hello"
s.indexOf("World") // 8
s.isEmpty() // false
s.isBlank() // false (Java 11+)
// == vs .equals() — CRITICAL interview question!
String a = new String("hello");
String b = new String("hello");
a == b // false — different objects in heap
a.equals(b) // true — same content
// String Pool — literal strings are cached
String c = "hello";
String d = "hello";
c == d // true — same pool reference🎲 Object-Oriented Programming — 4 Pillars
Beginner → IntermediateOOP is the foundation of Java. Every interview tests OOP deeply — not just definitions, but real code examples and when to use each concept. Master these 4 pillars completely.
1. Encapsulation — Data Hiding
public class BankAccount {
private double balance; // Private — cannot be accessed directly
private String accountNumber;
// Controlled access via getter/setter
public double getBalance() {
return balance;
}
public void deposit(double amount) {
if (amount > 0) { // Business validation before setting
balance += amount;
} else {
throw new IllegalArgumentException("Amount must be positive");
}
}
}
// Cannot do: account.balance = -5000 → compile error
// Must do: account.deposit(-5000) → throws exception safely2. Inheritance — Code Reuse
public class Employee {
protected String name;
protected double baseSalary;
public Employee(String name, double salary) {
this.name = name;
this.baseSalary = salary;
}
public double calculatePay() {
return baseSalary;
}
}
public class Manager extends Employee { // Inherits all Employee fields/methods
private double bonus;
public Manager(String name, double salary, double bonus) {
super(name, salary); // Call parent constructor
this.bonus = bonus;
}
@Override
public double calculatePay() { // Override — runtime polymorphism
return baseSalary + bonus;
}
}3. Polymorphism — Many Forms
// Compile-time polymorphism — Method Overloading
public class Calculator {
public int add(int a, int b) { return a + b; }
public double add(double a, double b) { return a + b; }
public String add(String a, String b) { return a + b; }
}
// Runtime polymorphism — Method Overriding (dynamic dispatch)
Employee emp = new Manager("Hari", 50000, 10000);
// emp is Employee type but points to Manager object
emp.calculatePay(); // Calls Manager's calculatePay() — not Employee's!
// JVM decides at RUNTIME which implementation to call4. Abstraction — Abstract Classes & Interfaces
// Abstract class — can have abstract + concrete methods
public abstract class Shape {
private String color;
public abstract double area(); // Must be implemented by subclass
public abstract double perimeter();
public void describe() { // Concrete method — shared logic
System.out.println("Area: " + area());
}
}
// Interface — Java 8+ allows default and static methods
public interface Payable {
double calculatePay(); // Abstract by default
default void printPayslip() { // Default method (Java 8+)
System.out.println("Pay: " + calculatePay());
}
}
// Class can extend ONE abstract class + implement MULTIPLE interfaces
public class Circle extends Shape implements Payable, Drawable {
private double radius;
public double area() { return Math.PI * radius * radius; }
public double perimeter() { return 2 * Math.PI * radius; }
public double calculatePay() { return 0; }
}Abstract Class vs Interface — Key Differences
| Feature | Abstract Class | Interface |
|---|---|---|
| Instantiation | Cannot instantiate directly | Cannot instantiate directly |
| Methods | Abstract + concrete methods | Abstract + default + static methods (Java 8+) |
| Variables | Instance variables (any type) | public static final constants only |
| Constructor | Has constructors | No constructors |
| Inheritance | Single inheritance (extends one) | Multiple implementation (implements many) |
| Access Modifiers | Any (private, protected, public) | public and default only |
| When to use | IS-A relationship with shared state | CAN-DO (capability contract) — Flyable, Serializable |
📚 Collections Framework
IntermediateThe Java Collections Framework is one of the most heavily tested topics in every Java interview. You must know not just what each collection is — but how it works internally, its time complexity and exactly when to use it.
Collections Hierarchy Overview
| Interface | Key Impl. | Ordered? | Duplicates? | Thread-Safe? | Time: get/put | Best Use |
|---|---|---|---|---|---|---|
| List | ArrayList | Yes (index) | Yes | No (use CopyOnWriteArrayList) | O(1) get, O(n) add(mid) | Ordered list with duplicates — most common choice |
| List | LinkedList | Yes (index) | Yes | No | O(n) get, O(1) add/remove ends | Frequent insertion/deletion at beginning or middle |
| Set | HashSet | No | No | No (use ConcurrentHashMap.newKeySet()) | O(1) avg | Unique items, fast lookup, order does not matter |
| Set | LinkedHashSet | Insertion | No | No | O(1) avg | Unique items in insertion order |
| Set | TreeSet | Sorted | No | No | O(log n) | Unique items always kept sorted (natural order or Comparator) |
| Map | HashMap | No | Keys: No | No (use ConcurrentHashMap) | O(1) avg | Key-value pairs, fastest general purpose — most used Map |
| Map | LinkedHashMap | Insertion | Keys: No | No | O(1) | Key-value with predictable iteration order |
| Map | TreeMap | Sorted by key | Keys: No | No | O(log n) | Key-value always sorted by key |
HashMap — How It Works Internally
// HashMap uses array of buckets (default 16) + LinkedList/Tree
// put("key", value) process:
// 1. Call key.hashCode() → get hash integer
// 2. Spread: hash ^ (hash >>> 16)
// 3. Index = hash & (capacity - 1)
// 4. If bucket empty → new Node. If occupied → use .equals()
// 5. When bucket size > 8 → convert LinkedList to Red-Black Tree (Java 8+)
// 6. When size > capacity * loadFactor(0.75) → resize (double capacity)
Map<String, Integer> scores = new HashMap<>();
scores.put("Ravi", 92);
scores.put("Priya", 88);
scores.get("Ravi"); // 92 — O(1) average
scores.getOrDefault("Unknown", 0); // 0 (safe — no NPE)
scores.containsKey("Priya"); // true
scores.putIfAbsent("Ravi", 100); // ignored — Ravi already exists
// Iterating
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// Java 8+ forEach with lambda
scores.forEach((k, v) -> System.out.println(k + ": " + v));💥 Exception Handling
IntermediateException Hierarchy
| Type | Superclass | Must Handle? | Examples | Represents |
|---|---|---|---|---|
| Checked Exception | Exception | YES — compile error if not handled | IOException, SQLException, ClassNotFoundException | Recoverable external conditions — file not found, network failure |
| Unchecked Exception | RuntimeException | NO — optional to handle | NullPointerException, ArrayIndexOutOfBoundsException, IllegalArgumentException | Programming bugs — should be fixed, not caught |
| Error | Error | NO — do not catch | OutOfMemoryError, StackOverflowError | JVM-level problems — application cannot recover |
try-catch-finally & Custom Exceptions
// Custom exception
public class InsufficientFundsException extends Exception {
private double amount;
public InsufficientFundsException(double amount) {
super("Insufficient funds. Short by: ₹" + amount);
this.amount = amount;
}
}
public void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) {
throw new InsufficientFundsException(amount - balance);
}
balance -= amount;
}
// try-with-resources (Java 7+) — auto closes resources
try (FileReader fr = new FileReader("data.txt");
BufferedReader br = new BufferedReader(fr)) {
String line = br.readLine();
System.out.println(line);
} catch (FileNotFoundException e) {
System.err.println("File not found: " + e.getMessage());
} catch (IOException e) {
System.err.println("IO Error: " + e.getMessage());
} finally {
// Runs ALWAYS — file auto-closed by try-with-resources above
System.out.println("Processing complete");
}⚡ Streams & Lambda — Java 8+
Intermediate → AdvancedJava 8 (2014) was the biggest update in Java's history. Lambda expressions and the Stream API transformed Java from verbose OOP-only to supporting functional programming style. These are asked in every senior Java interview.
Lambda Expressions & Functional Interfaces
// Traditional anonymous class (verbose)
Runnable r1 = new Runnable() {
public void run() { System.out.println("Running"); }
};
// Lambda equivalent — same thing, much cleaner
Runnable r2 = () -> System.out.println("Running");
// Built-in functional interfaces
Function<String, Integer> strLen = s -> s.length(); // T → R
Predicate<Integer> isEven = n -> n % 2 == 0; // T → boolean
Consumer<String> printer = s -> System.out.println(s);
Supplier<String> greeter = () -> "Hello!";
// Method reference — even cleaner than lambda
Consumer<String> printer2 = System.out::println; // same as aboveStream API — Complete Operations
List<String> courses = List.of("SAP Security", "Python AI", "SQL", "Java", "Cloud");
// filter + map + collect — most common pattern
List<String> shortCourses = courses.stream()
.filter(c -> c.length() <= 5) // keep only short names
.map(String::toUpperCase) // transform each
.sorted() // alphabetical order
.collect(Collectors.toList()); // ["CLOUD", "JAVA", "SQL"]
// Aggregate operations
long count = courses.stream().count();
Optional<String> first = courses.stream().findFirst();
boolean anyMatch = courses.stream().anyMatch(c -> c.contains("SAP"));
// Group by length — Collectors.groupingBy
Map<Integer, List<String>> byLength = courses.stream()
.collect(Collectors.groupingBy(String::length));
// flatMap — flatten nested lists
List<List<Integer>> nested = List.of(List.of(1,2), List.of(3,4));
List<Integer> flat = nested.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList()); // [1, 2, 3, 4]🍀 Spring Boot Fundamentals
IntermediateSpring Boot is an opinionated extension of the Spring Framework that makes it easy to create stand-alone, production-grade Spring applications. "Convention over configuration" — Spring Boot auto-configures most of what you need based on your dependencies.
Spring Core Concepts
| Concept | What It Is | Example |
|---|---|---|
| IoC Container | Inversion of Control — Spring creates and manages your objects (beans), not you | Spring creates StudentService and injects its dependencies automatically |
| Dependency Injection | Spring injects objects your class needs — via Constructor, Setter or Field | @Autowired StudentRepository repo — Spring injects the implementation |
| Bean | Any object managed by the Spring IoC container | @Service, @Repository, @Controller, @Component classes are all beans |
| @SpringBootApplication | Main annotation — enables @Configuration + @ComponentScan + @EnableAutoConfiguration | Put on your main class — Spring scans and configures everything |
| Auto-configuration | Spring detects classpath dependencies and configures beans automatically | Add H2 dependency → Spring auto-creates in-memory database and DataSource |
| application.properties | Central configuration file — DB URL, server port, JPA settings, logging | spring.datasource.url=jdbc:mysql://localhost:3306/mydb |
Spring Boot Application Structure
// Main class
@SpringBootApplication
public class CuesysApplication {
public static void main(String[] args) {
SpringApplication.run(CuesysApplication.class, args);
}
}
// Entity — maps to database table
@Entity
@Table(name = "students")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank
private String name;
@Email
private String email;
}
// Repository — data access layer
@Repository
public interface StudentRepository extends JpaRepository<Student, Long> {
List<Student> findByName(String name);
Optional<Student> findByEmail(String email);
}
// Service — business logic layer
@Service
public class StudentService {
private final StudentRepository repo;
public StudentService(StudentRepository repo) { // Constructor injection
this.repo = repo;
}
public Student enroll(Student student) {
return repo.save(student);
}
}🔗 REST API Development with Spring Boot
IntermediateComplete CRUD REST Controller
@RestController
@RequestMapping("/api/students")
public class StudentController {
private final StudentService service;
public StudentController(StudentService service) {
this.service = service;
}
// GET /api/students — get all
@GetMapping
public ResponseEntity<List<Student>> getAll() {
return ResponseEntity.ok(service.findAll());
}
// GET /api/students/5 — get one by ID
@GetMapping("/{id}")
public ResponseEntity<Student> getById(@PathVariable Long id) {
return service.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
// POST /api/students — create new
@PostMapping
public ResponseEntity<Student> create(@Valid @RequestBody Student s) {
Student saved = service.save(s);
URI location = URI.create("/api/students/" + saved.getId());
return ResponseEntity.created(location).body(saved);
}
// PUT /api/students/5 — update
@PutMapping("/{id}")
public ResponseEntity<Student> update(@PathVariable Long id,
@Valid @RequestBody Student s) {
return ResponseEntity.ok(service.update(id, s));
}
// DELETE /api/students/5
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
service.delete(id);
return ResponseEntity.noContent().build();
}
}
// Global exception handler
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
return ResponseEntity.status(404).body(new ErrorResponse(ex.getMessage()));
}
}HTTP Methods & Status Codes
| Method | Purpose | Success Code | Has Body? |
|---|---|---|---|
| GET | Retrieve resource(s) | 200 OK | Response only |
| POST | Create new resource | 201 Created (+ Location header) | Request + Response |
| PUT | Replace entire resource | 200 OK | Request + Response |
| PATCH | Partial update | 200 OK | Request + Response |
| DELETE | Delete resource | 204 No Content | No response body |
📈 JPA & Hibernate
Intermediate → AdvancedJPA (Java Persistence API) is the specification for ORM in Java. Hibernate is the most popular implementation. Together they map Java objects to database tables — eliminating most manual SQL for CRUD operations.
Entity Relationships
// @OneToMany — One course has many enrollments
@Entity
public class Course {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "course", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Enrollment> enrollments;
}
// @ManyToOne — Many enrollments belong to one course
@Entity
public class Enrollment {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "course_id")
private Course course;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "student_id")
private Student student;
private LocalDate enrollDate;
private Integer score;
}JPQL Queries & N+1 Problem
// JPQL — object-oriented query language
@Query("SELECT e FROM Enrollment e WHERE e.score >= :minScore ORDER BY e.score DESC")
List<Enrollment> findHighScorers(@Param("minScore") int minScore);
// N+1 Problem — BAD: 1 query for courses + N queries for enrollments
List<Course> courses = courseRepo.findAll();
courses.forEach(c -> c.getEnrollments().size()); // N+1 queries!
// FIX — JOIN FETCH loads everything in ONE query
@Query("SELECT c FROM Course c LEFT JOIN FETCH c.enrollments")
List<Course> findAllWithEnrollments();
// Or use @EntityGraph
@EntityGraph(attributePaths = {"enrollments"})
List<Course> findAll();💡 EAGER vs LAZY loading: LAZY (default for @OneToMany) = load related data only when accessed — better performance. EAGER = load everything immediately — can cause performance issues with large datasets. Always use LAZY and fix N+1 with JOIN FETCH or @EntityGraph when needed.
⚖️ Microservices with Spring Boot
AdvancedMicroservices is an architectural approach where a large application is broken into small, independent services — each with its own database, deployable separately. This is the dominant architecture at companies like Netflix, Amazon and Uber.
Monolith vs Microservices
| Aspect | Monolith | Microservices |
|---|---|---|
| Deployment | Deploy entire app for any change | Deploy only the changed service independently |
| Scaling | Scale entire application | Scale only the bottleneck service |
| Technology | One tech stack | Each service can use a different language/DB |
| Team Size | One large team | Small independent teams per service (2-pizza rule) |
| Failure | One bug can bring down everything | Failures are isolated to one service |
| Complexity | Simple to start | Complex — distributed systems problems |
| Database | One shared database | Each service has its own dedicated database |
Key Spring Cloud Components
💫 Design Patterns
AdvancedDesign patterns are proven, reusable solutions to commonly occurring software design problems. The "Gang of Four" (GoF) book documented 23 patterns in 1994. Senior Java interviews always test these — not just "what is it" but "when and why to use it."
Most Asked Patterns in Java Interviews
// Singleton — only ONE instance ever created
public class DatabaseConnection {
private static volatile DatabaseConnection instance; // volatile for thread safety
private DatabaseConnection() {} // Private constructor — prevents external instantiation
public static DatabaseConnection getInstance() {
if (instance == null) {
synchronized (DatabaseConnection.class) { // double-checked locking
if (instance == null) {
instance = new DatabaseConnection();
}
}
}
return instance;
}
}// Builder — construct complex objects step by step
public class CourseEnrollment {
private final String studentName;
private final String courseName;
private final String mode;
private final int duration;
private CourseEnrollment(Builder b) { // Private constructor
this.studentName = b.studentName;
this.courseName = b.courseName;
this.mode = b.mode;
this.duration = b.duration;
}
public static class Builder {
private String studentName, courseName;
private String mode = "Online"; // default
private int duration = 30; // default
public Builder studentName(String s) { this.studentName = s; return this; }
public Builder courseName(String c) { this.courseName = c; return this; }
public Builder mode(String m) { this.mode = m; return this; }
public Builder duration(int d) { this.duration = d; return this; }
public CourseEnrollment build() { return new CourseEnrollment(this); }
}
}
// Usage — readable, fluent
CourseEnrollment e = new CourseEnrollment.Builder()
.studentName("Ravi Kumar")
.courseName("Java & Spring Boot")
.mode("Onsite")
.duration(5)
.build();SOLID Principles — Clean Code Foundation
| Principle | Meaning | Violation Example | Fix |
|---|---|---|---|
| S — Single Responsibility | Every class should have ONE reason to change | UserService handles users, sends emails AND logs to DB | Separate: UserService, EmailService, AuditService |
| O — Open/Closed | Open for extension, closed for modification | Add if-else for each new payment type | Use Payment interface, add new PayPalPayment class |
| L — Liskov Substitution | Subclass should be replaceable by its superclass | Square extends Rectangle but breaks area calculation | Separate Square and Rectangle hierarchies |
| I — Interface Segregation | Clients should not depend on methods they do not use | One fat IAnimal interface with fly(), swim(), run() | Separate: IFlyable, ISwimmable, IRunnable |
| D — Dependency Inversion | Depend on abstractions, not concrete implementations | new MySQLDatabase() hardcoded in service | Inject Database interface — switch impl without changing service |
❓ Java & Spring Boot Interview Q&A
Most frequently asked questions at TCS, Infosys, Wipro, Cognizant, Accenture, Amazon, and product companies. Click to reveal model answers.
Model Answer:
== compares references — it checks if two variables point to the exact same object in memory. For primitive types (int, boolean etc.), == compares values. The .equals() method compares object content. Its default implementation in the Object class uses == (reference comparison), but String, Integer and most other classes override it to compare by value. Critical example: String a = new String("hello"); String b = new String("hello"); a == b is FALSE (different heap objects). a.equals(b) is TRUE (same content). However, String literals use the String Pool: String c = "hello"; String d = "hello"; c == d is TRUE (same pool reference). Rule: always use .equals() for object comparison. Use == only for primitive comparison and reference identity checks. For null-safe comparison: use Objects.equals(a, b) which handles null without NPE.
Model Answer:
HashMap uses an array of "buckets" (default initial capacity 16) combined with linked lists (and Red-Black Trees from Java 8+ when a bucket exceeds 8 entries). On put(key, value): (1) key.hashCode() is called. (2) Hash is further processed: hash ^ (hash >>> 16) to spread bits. (3) Bucket index = hash & (capacity-1). (4) If bucket is empty, create a new Node. (5) If occupied, .equals() checks if the key already exists — update value if yes, add new Node to chain if no — this is a collision. (6) Load Factor (default 0.75): when 75% of buckets are occupied, HashMap resizes — capacity doubles and all entries are rehashed. Java 8 improvement: when a single bucket has more than 8 nodes, the linked list converts to a Red-Black Tree, improving worst-case time from O(n) to O(log n). Always override both hashCode() and equals() together — if two objects are equal, they must have the same hashCode.
Model Answer:
ArrayList uses a dynamic array internally. It gives O(1) random access by index (array indexing), O(1) amortized add at the end, but O(n) for insertion or removal in the middle (elements must shift). Memory: compact, just an array. LinkedList uses a doubly linked list. It gives O(1) add/remove at both ends, O(n) for random access by index (must traverse from head). Memory: each node stores data + two pointers — higher overhead. When to use ArrayList: most cases — random access, mostly reading, appending at end. When to use LinkedList: frequent insertion/deletion at the beginning or middle, implementing a Queue or Deque. In practice, ArrayList outperforms LinkedList in most real scenarios due to CPU cache locality (contiguous memory). Rule of thumb: use ArrayList by default, switch to LinkedList only if profiling shows it is a bottleneck.
Model Answer:
Abstract class: can have abstract and concrete methods, instance variables, constructors, any access modifiers. A class can only extend ONE abstract class. Use when classes share state (instance variables) and some common implementation — IS-A relationship with partial implementation. Example: Vehicle abstract class with common fuel() method but abstract drive() method. Interface: all fields are public static final (constants), all methods are implicitly public. Since Java 8: can have default and static methods. A class can implement MULTIPLE interfaces. Use for capability contracts — what an object CAN DO regardless of its class hierarchy: Serializable, Comparable, Runnable. The key question: IS-A (abstract class) vs CAN-DO (interface). If you have a Dog and a Cat, both ARE-A Animal (abstract class). Both CAN-DO Trainable, Playable (interfaces). Real design tip: prefer interfaces for type definitions — they give more flexibility and support multiple implementation.
Model Answer:
Dependency Injection (DI) is a design pattern where objects receive their dependencies from outside rather than creating them internally. Benefits: decoupling, testability (can inject mock objects for testing), flexibility (change implementation without touching the class). Spring implements DI through its IoC container. Three types: (1) Constructor Injection — dependencies passed via constructor. Best practice — makes dependencies explicit and supports immutability. @Autowired on constructor (optional in Spring 4.3+ if single constructor). (2) Setter Injection — dependencies set via setter methods. Use for optional dependencies. (3) Field Injection — @Autowired directly on field. Convenient but not recommended — hides dependencies, makes testing harder. Spring prefers Constructor Injection. How Spring creates beans: scans @Component, @Service, @Repository, @Controller annotations. When your class has @Autowired, Spring checks its ApplicationContext for a matching bean and injects it. If multiple candidates exist, use @Qualifier("beanName") or @Primary to resolve ambiguity.
Model Answer:
The N+1 problem occurs when fetching a list of entities triggers N additional queries to load their related entities — one query for the parent list plus N queries for the child data. Example: fetch 100 courses (1 query), then access each course's enrollments in a loop — triggers 100 more queries = 101 total queries. This kills database performance. Solutions: (1) JOIN FETCH in JPQL: @Query("SELECT c FROM Course c LEFT JOIN FETCH c.enrollments") — loads everything in ONE query. (2) @EntityGraph: @EntityGraph(attributePaths = {"enrollments"}) on repository method — same effect as JOIN FETCH. (3) Use FetchType.EAGER — loads related entities in the initial query, but avoid this broadly as it can load more data than needed. (4) Batch fetching: @BatchSize(size=25) on the collection — loads children in batches instead of one by one. Detection: enable SQL logging (spring.jpa.show-sql=true) and count queries. Use tools like Hibernate Statistics or p6spy to measure.
Model Answer:
A microservice is a small, independently deployable service that focuses on a single business capability and has its own data store. Monolithic architecture packages all functionality (UI, business logic, data access) into a single deployable unit sharing one database. Key differences: Deployment — in a monolith, any change requires redeploying the entire application (risky for large systems). Microservices deploy each service independently. Scaling — monolith scales all-or-nothing. Microservices scale only the bottleneck service. Fault isolation — a bug in monolith can crash everything. A microservice failure is isolated. Technology — monolith uses one tech stack; each microservice can use the most appropriate technology. Complexity trade-off: monolith is simpler to start with, easier to debug, no distributed systems problems. Microservices introduce network latency, distributed transactions (Saga pattern), service discovery (Eureka), API gateway, distributed tracing (Zipkin). Recommendation: start with a monolith, extract microservices when teams grow and specific services need independent scaling.
Model Answer:
The Singleton pattern ensures a class has only ONE instance and provides a global access point to it. Use cases: database connection pool, logging, configuration manager, caching. Basic implementation problems: simple null check is not thread-safe in multi-threaded environments — two threads can both pass the null check simultaneously and create two instances. Thread-safe solutions: (1) Synchronized method: public static synchronized Singleton getInstance() — works but synchronization on every call is expensive. (2) Double-checked locking with volatile: check null outside synchronized block (fast path), check again inside. The volatile keyword ensures the instance is fully constructed before the reference is published (prevents instruction reordering). (3) Bill Pugh Singleton (best practice): use a static inner holder class — class loading is thread-safe by the JVM. The holder class is not loaded until getInstance() is called: private static class Holder { static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return Holder.INSTANCE; } This is the cleanest, most efficient thread-safe singleton implementation.
🏅 Certification Guide: For Oracle Certified Professional Java SE 17 (1Z0-829): focus on OOP (40%), Collections & Generics (20%), Exception Handling (10%), Streams & Lambda (15%), Concurrency (10%). For Spring Professional (VMware): Spring Core (DI, IoC), Spring MVC, Spring Data JPA, Spring Security and Spring Boot auto-configuration. These Q&As cover the most tested areas of both exams.