Java Senior Developer Interview
Preparation Guide
50 Interview Questions & Answers
Core Java & Language Fundamentals
Q1: What are the key differences between Java 8 and
Java 11/17? Explain Stream API improvements.
Answer:
Java 8 introduced functional programming paradigms, while Java
11/17 enhanced performance and security[1].
Key Differences:
Streams API (Java 8): Introduced functional-style operations on
collections using map(), filter(), reduce()
Module System (Java 9): Project Jigsaw introduced modules for
better code organization
Local Variable Type Inference (Java 10): var keyword for local
variables
Records (Java 14+): Immutable data classes with minimal
boilerplate
Sealed Classes (Java 15+): Restrict class inheritance for better
type safety
Text Blocks (Java 13+): Multiline strings with proper formatting
Pattern Matching (Java 16+): Simplified control flow with
pattern matching
Stream API Improvements:
// Java 8
List<String> names = [Link]()
.filter(p -> [Link]() > 18)
.map(Person::getName)
.collect([Link]());
// Java 16+ with pattern matching
if (obj instanceof String s) {
[Link]([Link]());
}
Q2: Explain the concept of Functional Interfaces and
provide examples.
Answer:
A Functional Interface is an interface with exactly ONE abstract
method. It enables functional programming in Java.
Characteristics:
Can have multiple default methods or static methods
Only ONE abstract method (SAM - Single Abstract Method)
Marked with @FunctionalInterface annotation (optional but
recommended)
Common Built-in Functional Interfaces:
Predicate<T>: Takes T, returns boolean
Function<T, R>: Takes T, returns R
Consumer<T>: Takes T, returns void
Supplier<T>: Takes nothing, returns T
BiFunction<T, U, R>: Takes T and U, returns R
Example Implementation:
@FunctionalInterface
public interface Calculator {
int calculate(int a, int b);
}
// Usage with Lambda
Calculator add = (a, b) -> a + b;
int result = [Link](5, 3); // Returns 8
// With Stream API
List<Integer> numbers = [Link](1, 2, 3, 4, 5);
[Link]()
.filter(n -> n > 2) // Predicate
.map(n -> n * 2) // Function
.forEach([Link]::println); // Consumer
Q3: What are the differences between abstract classes
and interfaces? When would you use each?
Answer:
While both define contracts, they serve different purposes:
Aspect Abstract Class Interface
Construct
Can have Cannot have
or
Can have instance Only constants (Java 8+: can
State
variables have private fields)
Can be public,
Methods Public/private (Java 9+)
protected, private
Inheritanc
Single inheritance Multiple inheritance
e
Access Can use all Methods are public by
Modifiers modifiers default
Use Case "IS-A" relationship "CAN-DO" capability
When to Use Abstract Class:
When classes share common code/state
When you need non-public members
When you want to control access to inherited members
When to Use Interface:
Define a contract/capability for unrelated classes
Multiple inheritance needed
Define constants for related classes
Practical Example:
// Abstract Class - IS-A
abstract class Animal {
private String name;
abstract void makeSound();
public void sleep() { [Link]("Sleeping..."); }
}
// Interface - CAN-DO
interface Flyable {
void fly();
}
class Bird extends Animal implements Flyable {
@Override
public void makeSound() { [Link]("Tweet!"); }
@Override
public void fly() { [Link]("Flying..."); }
}
Q4: Explain the difference between checked and
unchecked exceptions. Provide examples.
Answer:
Exceptions in Java are categorized based on whether they must be
handled at compile-time.
Checked Exceptions:
Must be caught or declared in method signature
Subclass of Exception (but not RuntimeException)
Examples: IOException, SQLException, ClassNotFoundException
Checked at compile-time
Unchecked Exceptions:
Don't need to be caught or declared
Subclass of RuntimeException
Examples: NullPointerException,
ArrayIndexOutOfBoundsException, ClassCastException
Checked at runtime
Code Example:
// Checked Exception - MUST handle
public void readFile(String path) throws IOException {
FileReader reader = new FileReader(path); // Throws IOException
[Link]();
}
// Unchecked Exception - Not required to handle
public void accessArray(int[] arr, int index) {
[Link](arr[index]); // May throw
ArrayIndexOutOfBoundsException
}
// Best Practice - Use try-catch for both
try {
readFile("[Link]");
} catch (IOException e) {
[Link]("File read failed", e);
}
Spring Framework & Dependency Injection
Q5: Explain Dependency Injection (DI) and its benefits.
How does Spring implement it?
Answer:
Dependency Injection is a design pattern that provides dependencies
to an object rather than having the object create them. This promotes
loose coupling and testability.
Types of DI in Spring:
1. Constructor Injection (Recommended)
@Component
public class UserService {
private final UserRepository repository;
@Autowired
public UserService(UserRepository repository) {
[Link] = repository;
}
}
2. Setter Injection
@Component
public class UserService {
private UserRepository repository;
@Autowired
public void setRepository(UserRepository repository) {
[Link] = repository;
}
}
3. Field Injection (Not recommended - harder to test)
@Component
public class UserService {
@Autowired
private UserRepository repository;
}
Benefits:
Loose Coupling: Components don't depend on concrete
implementations
Testability: Easy to inject mock dependencies
Flexibility: Dependencies can be swapped easily
Maintainability: Changes don't cascade through code
Inversion of Control: Framework manages object lifecycle
Q6: What is the Spring Bean lifecycle? Explain the
different phases.
Answer:
Spring Bean lifecycle consists of several phases from instantiation to
destruction.
Phases:
1. Instantiation: Container creates bean instance
2. Populate Properties: Dependency injection occurs
3. Aware Methods: If bean implements *Aware interfaces
[Link]()
[Link]()
4. Post-Processing Before Init:
[Link]()
5. Init Methods:
@PostConstruct annotation
[Link]()
Custom init-method
6. Post-Processing After Init:
[Link]()
7. Usage: Bean available for use
8. Destruction:
@PreDestroy annotation
[Link]()
Custom destroy-method
Code Example:
@Component
public class MyBean implements InitializingBean, DisposableBean {
@PostConstruct
public void init() {
[Link]("Post construct");
}
@Override
public void afterPropertiesSet() throws Exception {
[Link]("After properties set");
}
@PreDestroy
public void cleanup() {
[Link]("Pre destroy");
}
@Override
public void destroy() throws Exception {
[Link]("Destroy");
}
}
Q7: Explain Spring Bean Scopes. What's the difference
between singleton and prototype?
Answer:
Bean scope defines the lifecycle and visibility of bean instances.
Available Scopes:
1. Singleton (Default)
One instance per Spring container
Thread-safe if beans are stateless
Suitable for stateless services
@Component
@Scope("singleton")
public class UserService { }
2. Prototype
New instance created each time
Not managed by container after creation
Suitable for stateful beans
@Component
@Scope("prototype")
public class RequestContext { }
3. Web Scopes (Web applications only)
Request: New instance per HTTP request
Session: Instance per user session
Application: Singleton at servlet context level
WebSocket: One instance per WebSocket session
Comparison:
// Singleton - Safe for stateless operations
@Service // Singleton by default
public class UserService {
private UserRepository repository;
public User findById(Long id) {
return [Link](id);
}
}
// Prototype - For stateful objects
@Component
@Scope("prototype")
public class RequestProcessor {
private String requestData;
public void process(String data) {
[Link] = data;
}
}
Q8: What is Aspect-Oriented Programming (AOP) in
Spring? Provide real-world examples.
Answer:
AOP allows you to modularize cross-cutting concerns (logging,
security, transactions) separate from business logic.
Key Concepts:
Aspect: Module containing cross-cutting concern
Pointcut: Expression to identify join points
Join Point: Execution point (method call)
Advice: Action taken at join point (Before, After, Around, etc.)
Weaving: Process of applying aspects to objects
Real-World Examples:
1. Logging Aspect
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* [Link]..(..))")
public void logBeforeMethod(JoinPoint joinPoint) {
String methodName = [Link]().getName();
[Link]("Executing method: " + methodName);
}
}
2. Performance Monitoring
@Aspect
@Component
public class PerformanceAspect {
@Around("execution(* [Link]..(..))")
public Object monitorPerformance(ProceedingJoinPoint joinPoint)
throws Throwable {
long startTime = [Link]();
Object result = [Link]();
long endTime = [Link]();
long duration = endTime - startTime;
[Link]("Method " + [Link]().getName() +
" took " + duration + "ms");
return result;
}
}
3. Transaction Management (Declarative)
@Service
public class UserService {
@Transactional
public void saveUser(User user) {
[Link](user);
}
}
Advice Types:
@Before: Execute before method
@After: Execute after method (always)
@AfterReturning: Execute after successful return
@AfterThrowing: Execute if exception thrown
@Around: Execute before and after method
Q9: Explain Spring Boot and its auto-configuration. How
does it differ from traditional Spring?
Answer:
Spring Boot simplifies Spring application development through
intelligent defaults and automated configuration.
Key Differences:
Aspect Spring Spring Boot
XML/Java config
Configuration Auto-configuration
required
Dependency Starter
Manual
Management dependencies
Need external Embedded
Embedded Server
server (Tomcat/Jetty)
Deployment WAR file Executable JAR
Performance Slower startup Faster startup
Auto-Configuration in Spring Boot:
@SpringBootApplication // Combines:
// @Configuration
// @ComponentScan
// @EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
[Link]([Link], args);
}
}
How Auto-Configuration Works:
1. Spring Boot scans classpath for specific libraries
2. Applies @ConditionalOn* annotations
3. Activates pre-configured beans if conditions met
4. User can override with custom configuration
Example Auto-Configuration:
// In [Link]
@Configuration
@ConditionalOnClass([Link])
@ConditionalOnProperty(name = "[Link]")
public class DataSourceAutoConfiguration {
@Bean
public DataSource dataSource() {
// Automatically creates DataSource
}
}
Overriding Auto-Configuration:
[Link]
[Link]=jdbc:mysql://localhost:3306/mydb
[Link]-class-name=[Link]
Q10: Explain Spring MVC request/response lifecycle.
How does DispatcherServlet work?
Answer:
Spring MVC follows Front Controller pattern where DispatcherServlet
handles all requests.
Request Processing Flow:
1. HTTP Request: Browser sends request
2. DispatcherServlet: Receives request
3. HandlerMapping: Identifies appropriate controller
4. Handler: Controller method processes request
5. Model: Controller returns model with data
6. View: ViewResolver determines view template
7. Rendering: View renders with model data
8. HTTP Response: Browser receives response
Code Example:
@Controller
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public String getUser(@PathVariable Long id, Model model) {
User user = [Link](id);
[Link]("user", user);
return "user-detail"; // View name
}
@PostMapping
public String createUser(@ModelAttribute User user) {
[Link](user);
return "redirect:/users";
}
}
DispatcherServlet Configuration:
dispatcher
[Link]
dispatcher /
Software Design Patterns
Q11: Explain the Singleton pattern and its thread-safety
implications.
Answer:
Singleton ensures only ONE instance of a class exists throughout the
application lifetime.
Implementation Approaches:
1. Eager Initialization (Thread-Safe but wastes memory)
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() { }
public static Singleton getInstance() {
return instance;
}
}
2. Lazy Initialization with Synchronized (Slow)
public class Singleton {
private static Singleton instance;
private Singleton() { }
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3. Double-Checked Locking (Recommended)
public class Singleton {
private static volatile Singleton instance;
private Singleton() { }
public static Singleton getInstance() {
if (instance == null) {
synchronized ([Link]) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
4. Bill Pugh Singleton (Best - Thread-safe and lazy)
public class Singleton {
private Singleton() { }
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return [Link];
}
}
5. Enum Singleton (Most secure, prevents reflection attacks)
public enum Singleton {
INSTANCE;
public void doSomething() {
[Link]("Doing something");
}
}
// Usage
[Link]();
Q12: Compare Factory pattern, Builder pattern, and
Abstract Factory pattern.
Answer:
Factory Pattern: Creates objects without specifying exact classes.
interface Animal { void makeSound(); }
class Dog implements Animal {
public void makeSound() { [Link]("Woof!"); }
}
class Cat implements Animal {
public void makeSound() { [Link]("Meow!"); }
}
class AnimalFactory {
public static Animal createAnimal(String type) {
return "DOG".equals(type) ? new Dog() : new Cat();
}
}
// Usage
Animal dog = [Link]("DOG");
Builder Pattern: Constructs complex objects step by step.
public class House {
private String foundation;
private String walls;
private String roof;
public static class Builder {
private String foundation;
private String walls;
private String roof;
public Builder foundation(String f) {
[Link] = f;
return this;
}
public Builder walls(String w) {
[Link] = w;
return this;
}
public Builder roof(String r) {
[Link] = r;
return this;
}
public House build() {
House house = new House();
[Link] = [Link];
[Link] = [Link];
[Link] = [Link];
return house;
}
}
}
// Usage
House house = new [Link]()
.foundation("Concrete")
.walls("Brick")
.roof("Tiles")
.build();
Abstract Factory Pattern: Creates families of related objects.
interface UIFactory {
Button createButton();
TextField createTextField();
}
class WindowsUIFactory implements UIFactory {
public Button createButton() { return new WindowsButton(); }
public TextField createTextField() { return new WindowsTextField(); }
}
class MacUIFactory implements UIFactory {
public Button createButton() { return new MacButton(); }
public TextField createTextField() { return new MacTextField(); }
}
Comparison:
Factory: Single object creation
Builder: Complex object construction step-by-step
Abstract Factory: Family of related objects
Q13: Explain the Strategy pattern and provide practical
examples.
Answer:
Strategy pattern allows selecting algorithm behavior at runtime.
Implementation:
// Strategy Interface
public interface PaymentStrategy {
void pay(double amount);
}
// Concrete Strategies
public class CreditCardPayment implements PaymentStrategy {
public void pay(double amount) {
[Link]("Paid " + amount + " via Credit Card");
}
}
public class UPIPayment implements PaymentStrategy {
public void pay(double amount) {
[Link]("Paid " + amount + " via UPI");
}
}
public class CryptoPayment implements PaymentStrategy {
public void pay(double amount) {
[Link]("Paid " + amount + " via Cryptocurrency");
}
}
// Context
public class ShoppingCart {
private PaymentStrategy strategy;
public void setPaymentStrategy(PaymentStrategy strategy) {
[Link] = strategy;
}
public void checkout(double totalAmount) {
[Link](totalAmount);
}
}
// Usage
ShoppingCart cart = new ShoppingCart();
[Link](new CreditCardPayment());
[Link](5000); // Paid 5000 via Credit Card
[Link](new UPIPayment());
[Link](3000); // Paid 3000 via UPI
Real-World Use Cases:
Payment methods in e-commerce
Sorting algorithms
Caching strategies
Compression algorithms
Authentication methods
Q14: Explain the Observer pattern. How is it
implemented in Java and Spring?
Answer:
Observer pattern defines one-to-many relationship where subjects
notify observers of state changes.
Core Implementation:
// Observer Interface
public interface Observer {
void update(String event);
}
// Subject (Observable)
public class EventPublisher {
private List<Observer> observers = new ArrayList<>();
public void attach(Observer observer) {
[Link](observer);
}
public void detach(Observer observer) {
[Link](observer);
}
public void notifyObservers(String event) {
for (Observer observer : observers) {
[Link](event);
}
}
public void publishEvent(String event) {
notifyObservers(event);
}
}
// Concrete Observers
public class EmailObserver implements Observer {
@Override
public void update(String event) {
[Link]("Sending email: " + event);
}
}
public class LogObserver implements Observer {
@Override
public void update(String event) {
[Link]("Logging: " + event);
}
}
Spring Event Implementation (Preferred):
// Custom Event
public class UserCreatedEvent extends ApplicationEvent {
private String userId;
public UserCreatedEvent(Object source, String userId) {
super(source);
[Link] = userId;
}
public String getUserId() { return userId; }
}
// Publisher
@Service
public class UserService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void createUser(User user) {
[Link](user);
[Link](new UserCreatedEvent(this,
[Link]()));
}
}
// Listeners
@Component
public class EmailListener {
@EventListener
public void onUserCreated(UserCreatedEvent event) {
[Link]("Sending welcome email for user: " +
[Link]());
}
}
@Component
public class NotificationListener {
@EventListener
public void onUserCreated(UserCreatedEvent event) {
[Link]("Creating notification for user: " +
[Link]());
}
}
Testing & Code Quality
Q15: Explain unit testing best practices. How would you
test Spring beans?
Answer:
Unit testing ensures code reliability and maintainability.
Best Practices:
1. Arrange-Act-Assert (AAA): Setup test data, execute code, verify
results
2. One assertion per test (or closely related assertions)
3. Descriptive test names: Test methods should indicate what
they test
4. Isolation: Tests should be independent
5. Mock external dependencies: Use mocks for databases, APIs,
etc.
Testing Spring Beans:
@ExtendWith([Link])
public class UserServiceTest {
@Mock
private UserRepository repository;
@InjectMocks
private UserService service;
@Test
void testFindUserById() {
// Arrange
Long userId = 1L;
User expectedUser = new User(userId, "John Doe", "
john@[Link]");
when([Link](userId)).thenReturn([Link](expectedU
ser));
// Act
User actualUser = [Link](userId);
// Assert
assertEquals([Link](), [Link]());
assertEquals("John Doe", [Link]());
verify(repository, times(1)).findById(userId);
}
@Test
void testFindUserByIdNotFound() {
// Arrange
Long userId = 999L;
when([Link](userId)).thenReturn([Link]());
// Act & Assert
assertThrows([Link], () -> [Link](userId));
}
@Test
void testSaveUser() {
// Arrange
User user = new User(null, "Jane Doe", "jane@[Link]");
User savedUser = new User(1L, "Jane Doe", "jane@[Link]");
when([Link](any([Link]))).thenReturn(savedUser);
// Act
User result = [Link](user);
// Assert
assertNotNull([Link]());
assertEquals("Jane Doe", [Link]());
verify(repository, times(1)).save(any([Link]));
}
}
Integration Testing:
@SpringBootTest
public class UserServiceIntegrationTest {
@Autowired
private UserService userService;
@Autowired
private UserRepository userRepository;
@BeforeEach
void setup() {
[Link]();
}
@Test
void testCreateAndRetrieveUser() {
// Arrange
User user = new User(null, "Test User", "test@[Link]");
// Act
User savedUser = [Link](user);
User retrievedUser = [Link]([Link]());
// Assert
assertEquals([Link](), [Link]());
assertEquals("Test User", [Link]());
}
}
Database & Data Access
Q16: Explain the difference between JPA/Hibernate and
JDBC. When would you use each?
Answer:
Both provide database access but at different abstraction levels.
JPA/Hibernate (ORM - Object-Relational Mapping):
Pros: Object-oriented, automatic mapping, less boilerplate
Cons: Performance overhead, N+1 query problems, query
debugging harder
Use When: Standard CRUD operations, complex mappings, rapid
development
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = [Link])
private Long id;
@Column(name = "email")
private String email;
@OneToMany(mappedBy = "user")
private List<Order> orders;
}
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByEmail(String email);
}
// Usage
User user = [Link]("john@[Link]");
JDBC (Low-level):
Pros: Fine-grained control, predictable SQL, better performance
Cons: More boilerplate, manual mapping, prone to errors
public class UserJdbcRepository {
@Autowired
private JdbcTemplate jdbcTemplate;
public User findById(Long id) {
String sql = "SELECT * FROM users WHERE id = ?";
return [Link](sql, new Object[]{id},
new UserRowMapper());
}
public void save(User user) {
String sql = "INSERT INTO users (email, name) VALUES (?, ?)";
[Link](sql, [Link](), [Link]());
}
}
class UserRowMapper implements RowMapper<User> {
public User mapRow(ResultSet rs, int rowNum) throws SQLException
{
User user = new User();
[Link]([Link]("id"));
[Link]([Link]("email"));
[Link]([Link]("name"));
return user;
}
}
Comparison:
Feature JPA/Hibernate JDBC
Abstraction High Low
Boilerplate Low High
Performance Good (with tuning) Excellent
Flexibility Limited High
Learning Curve Steep Gentle
Q17: Explain Spring Data JPA and write a complex query
example.
Answer:
Spring Data JPA simplifies data access layer with automatic
repository implementation.
Features:
Automatic query generation from method names
JPQL and native SQL query support
Pagination and sorting
Custom repository implementations
Complex Query Example:
@Repository
public interface OrderRepository extends JpaRepository<Order,
Long> {
// Query method
List<Order> findByUserIdAndStatusOrderByCreatedDateDesc(Long
userId, String status);
// JPQL Query
@Query("SELECT o FROM Order o WHERE [Link] = ?1 AND [Link]
= ?2")
List<Order> findUserOrders(Long userId, String status);
// Native SQL Query
@Query(value = "SELECT * FROM orders WHERE user_id = ?1 AND
amount > ?2",
nativeQuery = true)
List<Order> findHighValueOrders(Long userId, BigDecimal amount);
// With Named Parameters
@Query("SELECT o FROM Order o WHERE [Link] = :userId " +
"AND [Link] BETWEEN :startDate AND :endDate")
List<Order> findOrdersInPeriod(
@Param("userId") Long userId,
@Param("startDate") LocalDateTime startDate,
@Param("endDate") LocalDateTime endDate
);
// Pagination Example
@Query("SELECT o FROM Order o WHERE [Link] = ?1")
Page<Order> findUserOrdersPaginated(Long userId, Pageable
pageable);
// Custom Implementation
@Query("SELECT new map([Link] as orderId, COUNT(oi) as itemCount,
SUM([Link]) as totalQty) " +
"FROM Order o JOIN [Link] oi " +
"WHERE [Link] = ?1 " +
"GROUP BY [Link] " +
"HAVING SUM([Link]) > ?2")
List<Map<String, Object>> getOrderSummary(Long userId, Integer
minQuantity);
}
// Usage
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
public List<Order> getCompletedOrders(Long userId) {
return
[Link](us
erId, "COMPLETED");
}
public Page<Order> getUserOrdersPaginated(Long userId, int page,
int size) {
Pageable pageable = [Link](page, size, [Link]
("createdDate").descending());
return [Link](userId, pageable);
}
}
Performance & Scalability
Q18: Explain transaction management in Spring. Discuss
propagation levels.
Answer:
Transactions ensure data consistency by grouping related operations
that either all succeed or all fail.
Declarative Transaction Management:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private AuditRepository auditRepository;
@Transactional
public User createUser(User user) {
User savedUser = [Link](user);
[Link]("User created: " + [Link]());
return savedUser;
}
}
Propagation Levels:
1. REQUIRED (Default)
Use existing transaction if available, create new if not
@Transactional(propagation = [Link])
public void method() { }
2. REQUIRES_NEW
Always create new transaction, suspend existing if any
@Transactional(propagation =
Propagation.REQUIRES_NEW)
public void auditOperation() {
// Executes in separate transaction
}
3. NESTED
Create nested transaction (savepoint)
@Transactional(propagation = [Link])
public void nestedOperation() { }
4. MANDATORY
Method must execute within transaction, throws exception
otherwise
@Transactional(propagation = [Link])
public void criticalOperation() { }
5. NOT_SUPPORTED
Suspend current transaction, execute non-transactionally
@Transactional(propagation =
Propagation.NOT_SUPPORTED)
public void readOnly() { }
6. NEVER
Throw exception if transaction is active
@Transactional(propagation = [Link])
public void mustBeNonTransactional() { }
Isolation Levels:
@Transactional(isolation = [Link])
public void criticalOperation() { }
READ_UNCOMMITTED: Dirty reads allowed
READ_COMMITTED: Dirty reads prevented
REPEATABLE_READ: Dirty and non-repeatable reads prevented
SERIALIZABLE: All concurrency issues prevented (slowest)
Rollback Configuration:
@Transactional(rollbackFor = [Link], noRollbackFor =
[Link])
public void complexOperation() {
// Rolls back on any Exception except ValidationException
}
Q19: How do you optimize Spring applications? Discuss
caching strategies.
Answer:
Performance optimization involves identifying bottlenecks and
implementing efficient solutions.
Caching in Spring:
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("users", "orders");
}
}
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
// Cache method result
@Cacheable(value = "users", key = "#id")
public User findById(Long id) {
[Link]("Fetching user from database: " + id);
return [Link](id).orElse(null);
}
// Update cache
@CachePut(value = "users", key = "#[Link]")
public User update(User user) {
return [Link](user);
}
// Invalidate cache
@CacheEvict(value = "users", key = "#id")
public void delete(Long id) {
[Link](id);
}
// Conditional caching
@Cacheable(value = "users", key = "#id",
condition = "#id > 0", unless = "#[Link]() == false")
public User findByIdConditional(Long id) {
return [Link](id).orElse(null);
}
}
Lazy Loading vs Eager Loading:
@Entity
public class User {
@Id
private Long id;
private String name;
// Lazy Loading - Loads when accessed
@OneToMany(mappedBy = "user", fetch = [Link])
private List<Order> orders;
// Eager Loading - Loads immediately
@ManyToOne(fetch = [Link])
private Department department;
}
Query Optimization:
// N+1 Query Problem - BAD
public List<User> getUsersWithOrders() {
List<User> users = [Link](); // 1 query
for (User user : users) {
[Link]().size(); // N queries
}
return users;
}
// Solution 1 - Eager Loading
@Query("SELECT DISTINCT u FROM User u LEFT JOIN FETCH
[Link]")
public List<User> getUsersWithOrdersEager();
// Solution 2 - Join Query
@Query("SELECT u FROM User u JOIN [Link] o WHERE [Link] IS NOT
NULL")
public List<User> getUsersWithOrdersJoin();
// Solution 3 - Batch Fetching
@Entity
public class User {
@OneToMany(mappedBy = "user", fetch = [Link])
@BatchSize(size = 10)
private List<Order> orders;
}
CI/CD & Deployment
Q20: Explain CI/CD pipeline with Jenkins and Docker.
Answer:
CI/CD automates building, testing, and deploying applications,
ensuring code quality and reliability.
CI/CD Pipeline Stages:
1. Source Code Management: Code pushed to Git
2. Build: Compile code, run unit tests, create artifacts
3. Test: Run integration/end-to-end tests
4. Analysis: Code quality checks (SonarQube)
5. Package: Create Docker image
6. Deploy: Deploy to staging/production
7. Monitor: Health checks, logging
Jenkins Pipeline Example:
pipeline {
agent any
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build') {
steps {
sh 'mvn clean compile'
}
}
stage('Unit Tests') {
steps {
sh 'mvn test'
}
}
stage('Code Quality') {
steps {
sh 'mvn sonar:sonar -[Link]=myapp'
}
}
stage('Build Docker Image') {
steps {
script {
sh '''
docker build -t myapp:${BUILD_NUMBER} .
docker tag myapp:${BUILD_NUMBER} myapp:latest
'''
}
}
}
stage('Push to Registry') {
steps {
sh '''
docker login -u ${DOCKER_USER} -p ${DOCKER_PASS}
docker push myapp:${BUILD_NUMBER}
'''
}
}
stage('Deploy to Dev') {
steps {
sh 'kubectl apply -f k8s/dev/[Link]'
}
}
stage('Integration Tests') {
steps {
sh 'mvn verify -Pit'
}
}
stage('Deploy to Prod') {
when {
branch 'main'
}
steps {
sh 'kubectl apply -f k8s/prod/[Link]'
}
}
}
post {
always {
junit 'target/surefire-reports/*.xml'
publishHTML([
reportDir: 'target/site/jacoco',
reportFiles: '[Link]',
reportName: 'Code Coverage'
])
}
}
}
Dockerfile:
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/[Link] [Link]
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "[Link]"]
Kubernetes Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:latest
ports:
- containerPort: 8080
env:
- name: JAVA_OPTS
value: "-Xmx512m"
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "1Gi"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
Additional Advanced Topics
Q21: Explain microservices architecture. How do you
handle service-to-service communication?
Answer:
Microservices break applications into small, independent services,
each with specific business capability.
Synchronous Communication (REST):
@Service
public class OrderService {
@Autowired
private RestTemplate restTemplate;
public Order createOrder(OrderRequest request) {
Order order = new Order();
[Link]([Link]());
// Call User Service
User user = [Link](
"[Link] + [Link](),
[Link]
);
if (user == null) {
throw new UserNotFoundException();
}
return [Link](order);
}
}
Asynchronous Communication (Message Queue):
@Service
public class OrderService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void createOrder(Order order) {
[Link](order);
// Send message to queue
[Link]("[Link]", "[Link]",
new OrderCreatedEvent([Link](), [Link]()));
}
}
@Component
public class OrderEventListener {
@RabbitListener(queues = "[Link]")
public void handleOrderCreated(OrderCreatedEvent event) {
[Link]("Order created: " + [Link]());
// Trigger email notification, update analytics, etc.
}
}
Service Discovery:
@Configuration
public class ServiceRegistry {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
.interceptors((request, body, execution) -> {
// Add service discovery logic
return [Link](request, body);
})
.build();
}
}
Service-to-Service Authentication:
@Component
public class ServiceAuthenticator {
public String getServiceToken() {
// OAuth2 token exchange
return [Link]();
}
}
Q22: Explain the N+1 query problem and solutions.
Answer:
N+1 problem occurs when accessing related entities triggers
additional queries.
Problem Example:
// BAD - Causes N+1 queries
public List<User> getUsersWithOrders() {
List<User> users = [Link](); // 1 query
for (User user : users) {
[Link]([Link]()); // N additional queries
}
return users;
}
// Results in: 1 query (users) + N queries (for each user's orders)
Solution 1: JPQL Join Fetch
@Query("SELECT DISTINCT u FROM User u LEFT JOIN FETCH
[Link]")
public List<User> findAllWithOrdersEager();
// Usage - Only 1 query
List<User> users = [Link]();
Solution 2: Named Entity Graph
@NamedEntityGraph(name = "[Link]",
attributeNodes = @NamedAttributeNode("orders"))
@Entity
public class User { }
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
@EntityGraph("[Link]")
List<User> findAll();
}
Solution 3: Batch Fetching
@Entity
public class User {
@OneToMany(mappedBy = "user", fetch = [Link])
@BatchSize(size = 20)
private List<Order> orders;
}
// Results in: 1 query (users) + 1 query per 20 users (batched orders)
Solution 4: DTO Projection
public interface UserOrderDTO {
Long getId();
String getName();
List<Order> getOrders();
}
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT u FROM User u JOIN FETCH [Link] WHERE
[Link] = 'ACTIVE'")
List<UserOrderDTO> findActiveUsersWithOrders();
}
Q23: What is the SOLID principle? Apply to Java design.
Answer:
SOLID principles promote maintainable, scalable, and robust
software design.
S - Single Responsibility Principle
// BAD - Multiple responsibilities
public class User {
public void save(User user) { /* DB logic / }
public void sendEmail(String email) { / Email logic / }
public void generateReport() { / Report logic */ }
}
// GOOD - Single responsibility
public class User {
private String email;
private String name;
}
public class UserRepository {
public void save(User user) { /* DB logic */ }
}
public class EmailService {
public void sendEmail(String email) { /* Email logic */ }
}
public class ReportGenerator {
public void generateReport() { /* Report logic */ }
}
O - Open/Closed Principle
// BAD - Closed for extension
public class PaymentProcessor {
public void process(String type, double amount) {
if ("CREDIT_CARD".equals(type)) {
// Credit card logic
} else if ("UPI".equals(type)) {
// UPI logic
}
}
}
// GOOD - Open for extension
public interface PaymentMethod {
void process(double amount);
}
public class CreditCardPayment implements PaymentMethod {
public void process(double amount) { }
}
public class UPIPayment implements PaymentMethod {
public void process(double amount) { }
}
public class PaymentProcessor {
private PaymentMethod paymentMethod;
public void process(double amount) {
[Link](amount);
}
}
L - Liskov Substitution Principle
// BAD - Rectangle and Square violation
public class Rectangle {
protected int width, height;
public void setWidth(int w) { [Link] = w; }
public void setHeight(int h) { [Link] = h; }
}
public class Square extends Rectangle {
@Override
public void setWidth(int w) {
[Link] = w;
[Link] = w; // Forces equal sides
}
}
// Problem: Square violates Rectangle contract
// GOOD - Proper hierarchy
public interface Shape {
int getArea();
}
public class Rectangle implements Shape {
private int width, height;
public Rectangle(int width, int height) {
[Link] = width;
[Link] = height;
}
public int getArea() { return width * height; }
}
public class Square implements Shape {
private int side;
public Square(int side) { [Link] = side; }
public int getArea() { return side * side; }
}
I - Interface Segregation Principle
// BAD - Fat interface
public interface Worker {
void work();
void eat();
void sleep();
}
public class Robot implements Worker {
public void work() { }
public void eat() { } // Irrelevant for robot
public void sleep() { } // Irrelevant for robot
}
// GOOD - Segregated interfaces
public interface Worker {
void work();
}
public interface Eater {
void eat();
}
public interface Sleeper {
void sleep();
}
public class Human implements Worker, Eater, Sleeper {
public void work() { }
public void eat() { }
public void sleep() { }
}
public class Robot implements Worker {
public void work() { }
}
D - Dependency Inversion Principle
// BAD - High-level depends on low-level
public class UserService {
private MySQLDatabase database = new MySQLDatabase();
public void saveUser(User user) {
[Link](user);
}
}
// GOOD - Both depend on abstraction
public interface Database {
void save(User user);
}
public class MySQLDatabase implements Database {
public void save(User user) { }
}
public class UserService {
private Database database;
public UserService(Database database) {
[Link] = database;
}
public void saveUser(User user) {
[Link](user);
}
}
Q24: Explain thread safety in Java. How do you make
collections thread-safe?
Answer:
Thread safety ensures that shared data is accessed correctly by
multiple threads.
Synchronization Approaches:
1. Synchronized Methods
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
2. Synchronized Blocks
public class Counter {
private int count = 0;
private final Object lock = new Object();
public void increment() {
synchronized (lock) {
count++;
}
}
}
3. AtomicInteger
public class Counter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
[Link]();
}
public int getCount() {
return [Link]();
}
}
4. Thread-Safe Collections
// [Link]
Map<String, String> map = [Link](new
HashMap<>());
// ConcurrentHashMap (better for high concurrency)
Map<String, String> concurrentMap = new ConcurrentHashMap<>();
// CopyOnWriteArrayList
List<String> list = new CopyOnWriteArrayList<>();
// BlockingQueue
BlockingQueue<String> queue = new LinkedBlockingQueue<>();
5. ReentrantLock
public class Counter {
private int count = 0;
private final ReentrantLock lock = new ReentrantLock();
public void increment() {
[Link]();
try {
count++;
} finally {
[Link]();
}
}
}
6. ReadWriteLock
public class Cache {
private Map<String, Object> cache = new HashMap<>();
private final ReadWriteLock lock = new
ReentrantReadWriteLock();
public Object get(String key) {
[Link]().lock();
try {
return [Link](key);
} finally {
[Link]().unlock();
}
}
public void put(String key, Object value) {
[Link]().lock();
try {
[Link](key, value);
} finally {
[Link]().unlock();
}
}
}
Q25: Explain RESTful API design principles and best
practices.
Answer:
REST (Representational State Transfer) provides guidelines for
building scalable, stateless web services.
Core Principles:
1. Resources: Everything is a resource (users, orders, products)
2. Representations: JSON/XML represent resource states
3. Statelessness: Each request contains all needed information
4. HTTP Methods: Use appropriate verbs
Best Practices:
1. Resource URIs (Nouns, not verbs)
✓ GET /api/users
✓ GET /api/users/123
✓ POST /api/users
✗ GET /api/getUsers
✗ POST /api/createUser
2. HTTP Status Codes
200 OK - Successful GET, PUT, PATCH
201 Created - Successful POST
204 No Content - Successful DELETE
400 Bad Request - Invalid input
401 Unauthorized - Authentication needed
403 Forbidden - Not permitted
404 Not Found - Resource doesn't exist
500 Internal Server Error - Server error
3. Spring REST Controller Example
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
// GET all users with pagination
@GetMapping
public Page<UserDTO> getAllUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
return [Link](page, size);
}
// GET single user
@GetMapping("/{id}")
public ResponseEntity<UserDTO> getUserById(@PathVariable Long
id) {
UserDTO user = [Link](id);
return [Link](user);
}
// POST create user
@PostMapping
public ResponseEntity<UserDTO> createUser(@Valid @RequestBody
CreateUserRequest request) {
UserDTO user = [Link](request);
return [Link]([Link]).body(user);
}
// PUT update user
@PutMapping("/{id}")
public ResponseEntity<UserDTO> updateUser(
@PathVariable Long id,
@Valid @RequestBody UpdateUserRequest request) {
UserDTO user = [Link](id, request);
return [Link](user);
}
// PATCH partial update
@PatchMapping("/{id}")
public ResponseEntity<UserDTO> partialUpdate(
@PathVariable Long id,
@RequestBody Map<String, Object> updates) {
UserDTO user = [Link](id, updates);
return [Link](user);
}
// DELETE user
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
[Link](id);
return [Link]().build();
}
}
4. Versioning Strategy
// URL Versioning
@RestController
@RequestMapping("/api/v1/users")
public class UserControllerV1 { }
@RestController
@RequestMapping("/api/v2/users")
public class UserControllerV2 { }
// Header Versioning
@RestController
@RequestMapping("/api/users")
@GetMapping(headers = "API-Version=1")
public List<UserDTOV1> getUsersV1() { }
@GetMapping(headers = "API-Version=2")
public List<UserDTOV2> getUsersV2() { }
5. Error Handling
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler([Link])
public ResponseEntity<ErrorResponse> handleUserNotFound(
UserNotFoundException ex) {
ErrorResponse error = new ErrorResponse(
"USER_NOT_FOUND",
[Link](),
[Link]()
);
return [Link](HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler([Link])
public ResponseEntity<ErrorResponse> handleValidationError(
MethodArgumentNotValidException ex) {
String message = [Link]()
.getFieldErrors()
.stream()
.map(f -> [Link]() + ": " + [Link]())
.collect([Link](", "));
ErrorResponse error = new ErrorResponse(
"VALIDATION_ERROR",
message,
[Link]()
);
return [Link](HttpStatus.BAD_REQUEST).body(error);
}
}
Q26-50: Additional Core Questions (Continued)
[Due to length constraints, here are the remaining topics with brief
outlines]
Q26: Explain the difference between ArrayList and LinkedList. When
to use each?
ArrayList: Backed by array, fast random access, slow
insertion/deletion
LinkedList: Doubly-linked list, slow random access, fast
insertion/deletion
Q27: What are garbage collection algorithms in Java?
Mark and Sweep, Mark and Compact, Copying, Generational GC
Q28: Explain volatile keyword and memory visibility in Java.
Ensures visibility across threads, prevents caching in registers
Q29: What is the difference between equals() and hashCode()?
equals(): Compares object content; hashCode(): Returns hash
value for HashMap/Set
Q30: Explain immutability in Java. How do you create immutable
objects?
Final class, final fields, defensive copying, private constructor
Q31: What is the difference between pass-by-value and pass-by-
reference?
Java is pass-by-value; object references are passed by value
Q32: Explain exception hierarchy. How would you create custom
exceptions?
Extend Exception or RuntimeException class
Q33: What is composition over inheritance? Provide examples.
Prefer object composition to inheritance for flexibility
Q34: Explain the Builder pattern in practice.
Construct complex objects step by step with fluent API
Q35: What is the Decorator pattern? Provide Java I/O examples.
BufferedInputStream decorates InputStream for buffering
Q36: Explain the Template Method pattern.
Define algorithm skeleton in superclass, let subclasses override
steps
Q37: What is the Command pattern? Real-world use cases.
Encapsulate requests as objects, enable queuing/undo
Q38: Explain the Chain of Responsibility pattern.
Pass request along chain of handlers until one handles it
Q39: What is the Proxy pattern? When would you use it?
Provide placeholder/surrogate for another object
Q40: Explain Stream API advantages over traditional iteration.
Declarative, functional, enables parallel processing
Q41: What is the difference between flatMap() and map()?
map(): Transform each element; flatMap(): Transform and
flatten
Q42: How do you handle optional values in Java 8+?
Use [Link](), [Link](), [Link]()
Q43: What is StringBuffer vs StringBuilder?
StringBuffer: Synchronized (thread-safe); StringBuilder: Not
synchronized
Q44: Explain String interning and string pool.
Intern string literals in memory pool for memory efficiency
Q45: What are the differences between interface and abstract class in
Java 8+?
Interfaces: Multiple default methods; Abstract class: Single
inheritance
Q46: Explain marker interfaces and their purpose.
Serializable, Cloneable - Signal capability to JVM
Q47: What is method overloading vs method overriding?
Overload: Same name, different parameters; Override: Same
signature in subclass
Q48: Explain the Comparator vs Comparable interface.
Comparable: Within class; Comparator: External comparison
logic
Q49: What is the difference between [Link]() and
[Link]()?
Arrays: For arrays; Collections: For collections
Q50: How would you approach debugging a memory leak in Java?
Use profilers, check long-lived object references, analyze heap
dumps
References
[1] Oracle. (2024). Java Language and Virtual Machine Specifications.
[Link]
[2] Spring Project. (2024). Spring Framework Documentation. [Link]
[Link]/projects/spring-framework
[3] Baeldung. (2024). Spring Interview Questions. [Link]
[Link]/spring-interview-questions
[4] Gang of Four. (1994). Design Patterns: Elements of Reusable Object-
Oriented Software. Addison-Wesley.
[5] Martin, R. C. (2008). Clean Code: A Handbook of Agile Software
Craftsmanship. Prentice Hall.
Document prepared: February 2026
Target Position: Senior Java Developer
Experience Level: 5+ Years with Java/Spring
Good luck with your interview! Remember to provide real examples
from your experience and discuss your problem-solving approach.