[Go to site: main page, start]

0% found this document useful (0 votes)
14 views87 pages

Java Int Questions

The document is a comprehensive list of Java interview questions covering various topics such as Java basics, OOP concepts, data types, exception handling, concurrency, Java 8+ features, Spring framework, and Kafka. It includes questions about key Java features, design patterns, memory management, and the differences between various Java components like JDK, JRE, and JVM. The document serves as a guide for candidates preparing for Java-related interviews.

Uploaded by

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

Java Int Questions

The document is a comprehensive list of Java interview questions covering various topics such as Java basics, OOP concepts, data types, exception handling, concurrency, Java 8+ features, Spring framework, and Kafka. It includes questions about key Java features, design patterns, memory management, and the differences between various Java components like JDK, JRE, and JVM. The document serves as a guide for candidates preparing for Java-related interviews.

Uploaded by

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

JAVA INTERVIEW

QUESTIONS
1. Java Basics
1. What are the main features of Java?
2. What is the difference between JDK, JRE, and JVM?
3. What is the difference between == and .equals()?
4. How does Java achieve platform independence?
5. What is the main method signature in Java?

2. OOP Concepts in Java


1. What are the SOLID Principles?
2. What are design patterns?
3. How is Java code loaded and run?
4. What is ACID?
5. What are the 4 pillars of Object-Oriented Programming?
6. Explain method overloading vs method overriding.
7. What is runtime vs compile-time polymorphism?
8. What is the use of super and this keywords?
9. Can you override a static method? (Ans: No — it's hidden, not overridden.)
[Link] is the difference between Composition vs Inheritance vs Aggregation

3. Data Types and Memory


1. Difference between int, Integer, and long.
2. What is autoboxing/unboxing?
3. What is the default value of a local variable? (Ans: Compilation error if
uninitialized.)
4. Explain stack vs heap memory.
5. What is the size and range of int, long, float, etc.?
6. What is the difference between primitive types and objects?
7. What is pass by value vs pass by reference?

4. Strings and Immutability


1. Why is String immutable in Java?
2. How does the String pool work?
3. Difference between StringBuilder, StringBuffer, and String.
4. What happens when you use == vs .equals() with Strings?

5. Collections Framework
1. Difference between ArrayList and LinkedList.
2. How does HashMap work internally?
3. Difference between HashMap, TreeMap, and LinkedHashMap.
4. What are HashSet, TreeSet, and their use cases?
5. Fail-fast vs fail-safe iterators?

6. Exception Handling
1. Difference between Checked and Unchecked exceptions.
2. What is the purpose of finally block?
3. Can you catch multiple exceptions in a single catch block?
4. Custom exceptions: how and when to create one?

7. Concurrency and Multithreading


1. What is the difference between Runnable and Thread?
2. What is synchronization? How does synchronized work?
3. What are race conditions and how do you avoid them?
4. What is ReentrantLock in Java?
5. What are volatile and atomic variables?
6. What is the difference between wait() and sleep()?
7. Explain ExecutorService and thread pools.
8. What is the difference between Callable and Runnable?
9. What are Thread Pools?
[Link] between CompletableFuture vs Future?
11. What is a Lock Interface?

8. Java Keywords and Modi ers


1. What is the difference between final, finally, and finalize()?
2. Can a constructor be final?
3. What does the static keyword mean?
4. What is transient and volatile?

9. Interfaces vs Abstract Classes


1. Key differences between interface and abstract class?
2. Can interfaces have default methods?
3. Can you implement multiple interfaces? Extend multiple classes?

10. Java 8+ Features (Modern Java)


1. What are lambdas and functional interfaces?
2. What is the Stream API and how is it used?
3. What are method references?
4. Difference between map() and flatMap()?
5. Optional class — why and how is it used?
fi
11. Spring Framework
1. What is @Component, @Service, @Repository?
2. What is Dependency Injection?
3. Explain Spring Boot annotations (@RestController, @RequestMapping)?
4. What is Spring Context, Profiles?
5. What is the context in Spring?

12. Miscellaneous
1. What is serialization? How does it work?
2. How is memory managed in Java (GC, heap, stack)?
3. Explain class loading process.
4. What is reflection?
5. What are enums and how are they useful?
6. What Is CAS?
7. When will you use AtomicReferences?
8. Explain Testing with Mockito or JUnit?
9. What is the difference between a mock and spy bean?
10. What’s an Observable?
11. What is a S3?
12. How to access S3?

12. Kafka
1. What is Apache Kafka?
2. What are the main components of Kafka?
3. What is a Kafka Topic and Partition?
4. How does Kafka ensure fault tolerance?
5. What is the role of Zookeeper in Kafka?
6. What is a Kafka Consumer Group?
7. What is Kafka’s delivery guarantee?
8. How does Kafka handle message retention?
9. How does Kafka ensure high throughput?
[Link] are Kafka producers and how do they work?
[Link] Kafka’s ISR (In-Sync Replicas).
[Link] is Kafka Stream API vs Kafka Consumer API?
[Link] is Kafka Connect?
[Link] happens when a Kafka broker fails?
[Link] is log compaction in Kafka?
[Link] would you scale Kafka for a high-throughput system?
[Link] is showing consumer lag. What do you do?
[Link] do you ensure message ordering in Kafka?
[Link] do Kafka transactions work?
[Link] are some real-world use cases of Kafka?
1. Java Basics
What are the main features of Java?

1. Platform Independent
Java code is compiled into bytecode (.class files) that runs on the Java Virtual Machine (JVM).
"Write once, run anywhere" — the same bytecode can run on any platform with a compatible JVM.

2. Object-Oriented
Everything in Java is treated as an object (except primitives).
Emphasizes principles like encapsulation, inheritance, polymorphism, and abstraction.

3. Simple and Familiar


Syntax is similar to C/C++, but with many complexities (like pointers, multiple inheritance)
removed.
Easy to learn if you’re familiar with other C-style languages.

4. Secure
Provides a secure runtime environment by:
Restricting access to memory.
Running code inside the JVM sandbox.
Eliminating pointer arithmetic.
Having features like classloaders, bytecode verification, and a security manager.

5. Robust
Emphasizes error handling via exceptions.
Automatic garbage collection to manage memory.
Strong type checking at compile time and runtime.

6. Multithreaded
Java has built-in support for multithreading, making it easy to build highly concurrent
applications.
The Thread class and [Link] package provide tools for concurrent programming.

7. High Performance
While not as fast as C/C++, Java’s performance is improved with Just-In-Time (JIT) compilers.
JVM optimizations make Java suitable for performance-critical applications.

8. Distributed
Java provides tools to build networked and distributed applications, e.g. using:
[Link] package (sockets, URLs)
RMI (Remote Method Invocation)
Web services, REST APIs (via Spring, etc.)

9. Dynamic
Java supports dynamic loading of classes during runtime.
Reflection and runtime polymorphism make it highly flexible.

10. Rich Standard Library


Java provides a vast collection of APIs:
Collections framework
File I/O and NIO
Networking, security, GUI (Swing, JavaFX)
Database connectivity (JDBC)

What is the di erence between JDK, JRE, and JVM?

JDK – Java Development Kit


What it is: A full-featured software development kit for Java.

Contains:
JRE (Java Runtime Environment)
Development tools: compiler (javac), debugger, JavaDoc, etc.
Use case: Needed by developers to write, compile, and run Java applications.
If you’re writing Java code, you need the JDK.

JRE – Java Runtime Environment


What it is: A package that provides everything needed to run Java programs.

Contains:
JVM
Core Java libraries
Supporting files
Use case: Needed by users to run Java applications, but not to develop them.
JRE does not include the compiler (javac), so you can’t write or compile code with it.

JVM – Java Virtual Machine


What it is: An abstract machine that runs Java bytecode.

Responsibilities:
Converts bytecode into machine-specific code.
Handles memory management, garbage collection, and security.
Ensures platform independence.
Use case: Every Java program runs inside a JVM, regardless of platform.
The JVM is what makes Java "Write Once, Run Anywhere" possible.

How They Relate

JDK = JRE + Development Tools


JRE = JVM + Core Libraries

Hierarchy Summary:
JDK
└── JRE
└── JVM

🔍 Quick Comparison Table

Feature JDK JRE JVM


ff
Compile Java? ✅ Yes ❌ No ❌ No

Run Java? ✅ Yes ✅ Yes ✅ Yes (via bytecode)

Includes JVM? ✅ Yes ✅ Yes N/A

Includes Compiler? ✅ Yes ❌ No ❌ No

Target Audience Developers End Users Internal Component

What is the di erence between == and .equals()?

== Operator
Purpose: Compares references (memory addresses) for objects.

For primitive types (int, char, etc.), it compares values directly.


For objects, it returns true only if both references point to the exact same object in memory.

Example:
String s1 = new String("hello");
String s2 = new String("hello");
[Link](s1 == s2); // false — different objects in memory

.equals() Method
Purpose: Compares contents (logical equality) of objects.

Defined in Object class, but often overridden in classes like String, Integer, List, etc. to
compare data meaningfully.
For strings, .equals() checks if the sequence of characters is the same.

Example:
String s1 = new String("hello");
String s2 = new String("hello");
[Link]([Link](s2)); // true — same content

Summary Table
Comparison == .equals()

Compares Reference (memory address) Logical content equality

Works for Primitives and objects Objects (overridden for content)

Default behavior Object references Same as == unless overridden

Example result false for two new String("hi") true for two strings with same chars

What is the contract between the equals() and hashCode()?

The equals() and hashCode() Contract


1. If two objects are equal according to equals(), then they must have the same hashCode()
value.

if [Link](b) == true, then [Link]() == [Link]()


ff
2. If two objects have the same hashCode(), they are not necessarily equal.
Different objects can have hash collisions, so:

if [Link]() == [Link](), equals() may be true or false.

3. If equals() returns false, hash codes can be the same or different.


But having distinct hash codes for unequal objects improves performance in hash tables.

Why is this contract important?


Hash-based collections like HashMap and HashSet use hashCode() to find the "bucket" where the
object might be stored.
Then, they use equals() to check for exact matches within that bucket.
Violating the contract breaks the collections' behaviour, causing objects to be lost or
duplicated.

How to follow the contract:


Whenever you override equals(), also override hashCode() to produce consistent hash codes.
Use the same fields in both methods.

Example:
class Person {
String name;
int age;
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != [Link]()) return false;
Person other = (Person) obj;
return age == [Link] && [Link](name, [Link]);
}
@Override
public int hashCode() {
return [Link](name, age);
}
}

Summary:

Condition Requirement

[Link](b) is true [Link]() == [Link]()

[Link]() == [Link]() [Link](b) may be true or false

How does Java achieve platform independence?

1. Compile Once – Bytecode


Java source code (.java files) is compiled by the Java compiler (javac) into bytecode (.class
files).
Bytecode is not specific to any operating system or hardware.
Think of bytecode as a universal set of instructions — like an international language for
computers.

2. Run Anywhere – JVM


The JVM (Java Virtual Machine) is installed on each operating system (Windows, Linux, Mac,
etc.).
It reads and interprets or compiles the bytecode into native machine code for the specific
platform.
So, the same .class file can run on any device with a compatible JVM — no recompilation needed.

3. Standard Libraries Abstract OS Differences


Java provides built-in libraries for:
File I/O
Networking
Threads
Graphics, etc.
These APIs hide platform-specific details, ensuring consistent behavior.

4. No OS-Specific Features Like Pointers


Java avoids using platform-dependent features (like memory pointers).
This ensures your code behaves the same way on any JVM.

Summary

Component Role in Platform Independence

Bytecode Platform-neutral intermediate code

JVM Platform-speci c interpreter for bytecode

Java APIs Abstract platform-speci c operations

Language Design No platform-dependent features (e.g., no pointers)

What is the main method signature in Java?

public static void main(String[] args)

Breakdown:
public — so JVM can access it from anywhere.
static — so JVM can call it without creating an instance of the class.
void — it doesn’t return any value.
main — the name JVM looks for as the entry point.
String[] args — array of command-line arguments passed as strings.
fi
fi
2. OOP Concepts in Java
What are the SOLID Principles?

S – Single Responsibility Principle (SRP)


A class should have only one reason to change.

Example use: I split a large service class handling both DB queries and business logic into two
separate components — one for persistence, another for domain logic.

O – Open/Closed Principle (OCP)


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

Example use: I used an interface and a base class to support multiple notification methods
(Email, SMS, Slack) without modifying the existing notification logic.

L – Liskov Substitution Principle (LSP)


Subtypes can be substituted without altering the program behavior.

Example use: I avoided violating LSP by ensuring all implementations of a PaymentProcessor


interface conformed to expected behavior, e.g., processPayment() didn’t throw unsupported
operation exceptions in any subclass.

I – Interface Segregation Principle (ISP)


No client should be forced to depend on methods it does not use.

Example use: I split a large UserService interface into smaller ones (UserReader, UserWriter) to
ensure that consumers only implemented what they needed.

D – Dependency Inversion Principle (DIP)


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

Example use: In a Spring Boot project, I injected services via interfaces and used dependency
injection, making it easier to swap implementations and mock services in tests.

How I've Applied Them in Practice:


In a microservices architecture for a logistics platform, we refactored a tightly coupled
shipping module. I introduced interfaces for each shipping provider (FedEx, UPS, DHL) using the
Strategy pattern to follow OCP and DIP.
The main service relied only on a ShippingProvider interface, allowing new providers to be added
with no changes to the core logic. This reduced regression bugs and made the system much easier
to test.
We also split out a ShippingCalculator that previously had multiple responsibilities
(validation, pricing, tracking) to follow SRP, which simplified testing and improved cohesion.

What are Design Patterns?

• Design patterns are proven, reusable solutions to common problems in software design.
• They are like templates or best practices that help solve design challenges in a
consistent and efficient way.
• Not complete code — more like a blueprint you adapt to your needs.

Why Use Design Patterns?


• Promote code reuse and maintainability.
• Improve communication among developers (common vocabulary).
• Help avoid common pitfalls and reinventing the wheel.
• Make your design more flexible and scalable.
Categories of Design Patterns
1. Creational Patterns (Object creation mechanisms)
2. Structural Patterns (Organizing classes/objects)
3. Behavioral Patterns (Communication between objects)

CREATIONAL PATTERNS
Focus on how objects are created and instantiated.

Pattern Purpose Example

Singleton Ensure only one instance of a class exists Logger, Con guration

Factory Method Create objects based on a condition, without [Link]("PDF")


exposing the instantiation logic
Abstract Factory Create families of related objects without UI toolkit for multiple OS themes
specifying concrete classes
Builder Construct complex objects step-by-step StringBuilder, creating an HTTP request

Prototype Clone existing objects instead of creating new Creating copies of objects in game engines
ones

STRUCTURAL PATTERNS
Deal with object composition, how classes and objects are structured.

Pattern Purpose Example

Adapter Bridge between incompatible interfaces Wrapping an old API to match a new interface

Decorator Add responsibilities to objects dynamically Adding features like encryption or logging to a
stream
Facade Provide a simpli ed interface to a complex [Link], Spring RestTemplate
subsystem
Proxy Control access to an object (e.g., for lazy Hibernate’s lazy-loaded entities
loading, security)
Composite Treat individual and group of objects File system trees: Files and Directories
uniformly
Bridge Decouple abstraction from implementation Remote controls for di erent TVs

Flyweight Share common object data to save memory Font character caching, game tiles

BEHAVIORAL PATTERNS
Deal with communication between objects, or how they interact over time.

Pattern Purpose Example

Strategy Select an algorithm at runtime Payment methods, compression strategies

Observer Notify multiple objects about state changes Event listeners, UI updates

Command Encapsulate a request as an object Undo/Redo, menu actions

Chain of Responsibility Pass request along a chain of handlers Servlet lters, exception handling

State Allow object to change behavior when TCP connection: Open, Closed, Listening
internal state changes
Template Method De ne the skeleton of an algorithm, defer Abstract classes in frameworks
steps to subclasses
Mediator Coordinate interaction between multiple Chatroom example: mediator between users
objects
Memento Save and restore object state (undo) Saving game state

Iterator Sequential access to elements without Iterator pattern in collections


exposing structure
Visitor Add operations to objects without changing File system traversal with actions
their classes
fi
fi
fi
fi
ff
Bonus: Real-World Java Examples

Pattern Java Example

Singleton [Link]()

Builder StringBuilder, [Link]()

Factory [Link](), DocumentBuilderFactory

Observer [Link] (deprecated now), or EventListener

Strategy Comparator used in [Link]()

Decorator Bu eredInputStream, HttpServletRequestWrapper

Command Runnable, ActionListener in GUI

How is Java code loaded and run?

1. Write the Source Code


You write a .java file — for example:
public class HelloWorld {
public static void main(String[] args) {
[Link]("Hello, world!");
}
}

2. Compilation: Java → Bytecode


You run:
bash javac [Link]
• The Java Compiler (javac) compiles the .java file into bytecode.
• It generates a .class file — in this case: [Link].
• Bytecode is an intermediate representation — not human-readable, but platform-
independent.

3. Class Loading (by JVM)


When you run:
bash java HelloWorld
The Java Virtual Machine (JVM) starts up and begins class loading:
• The ClassLoader loads [Link] into memory.
• The Bootstrap ClassLoader loads core Java classes ([Link], etc.).
• Optionally, custom classloaders may load user or third-party classes.

4. Bytecode Verification
Before execution, the JVM verifies the bytecode:
• Ensures there are no illegal code operations.
• Protects against bytecode-level attacks or corruption.
• Fails if the class file is malformed or violates JVM rules.

5. Just-In-Time Compilation (JIT)


• JVM uses an interpreter to start running the bytecode line-by-line.
• Frequently-used methods (hot code) are then compiled at runtime by the JIT compiler into
native machine code for better performance.
ff
• JVM may optimize code dynamically — like inlining, loop unrolling, or dead code
elimination.

6. Execution in JVM
• The native code runs on your OS and CPU via the JVM.
• The JVM runtime manages:
• Memory allocation (Heap, Stack)
• Garbage Collection
• Threading & synchronization
• Exception handling
• Security and sandboxing

JVM Internals Overview

Component Purpose

ClassLoader Loads .class les

Bytecode Veri er Checks for security and correctness

Interpreter Executes bytecode line-by-line initially

JIT Compiler Compiles hot code to machine code

GC (Garbage Collector) Frees unused memory in the heap

Runtime Data Areas Stack, Heap, Method Area, etc.

Execution Engine Manages execution of bytecode

Example Timeline
1. You compile: javac [Link] → [Link]
2. You run: java MyApp
3. JVM starts, loads class, verifies bytecode.
4. Interpreter begins running main method.
5. JIT compiles parts to native code for performance.
6. GC manages memory during execution.

What is ACID?

Letter Meaning Description

A Atomicity Transactions are “all or nothing.” Either


everything in a transaction succeeds, or
nothing does. No partial completion.
C Consistency Transactions bring the database from one
valid state to another, maintaining all
prede ned rules, constraints, and data
integrity.
I Isolation Concurrent transactions don’t interfere with
each other. Intermediate states of a
transaction are invisible to others.
D Durability Once a transaction commits, its changes are
permanent, even if the system crashes
afterward.

Quick example:
• You transfer money between bank accounts.
• Atomicity: Either both debit and credit happen, or neither.
• Consistency: Total money remains the same, no negative balance.
fi
fi
fi
• Isolation: If two transfers run concurrently, they don’t corrupt balances.
• Durability: Once transfer confirmed, data won’t be lost even if power fails.

What are the 4 pillars of Object-Oriented Programming? / What is runtime vs


compile-time polymorphism?

1. Encapsulation
Bundling data (fields) and methods that operate on the data into a single unit (class).
Hides internal object details and exposes only necessary parts via access modifiers (private,
public, protected).
Protects object integrity by restricting direct access to some components.

2. Inheritance
Mechanism where one class (subclass/child) inherits fields and methods from another class
(superclass/parent).
Enables code reuse and hierarchical classification.
Supports method overriding to customise behavior.

3. Polymorphism
Ability of objects to take multiple forms.
Two types:
Compile-time polymorphism (method overloading)
Runtime polymorphism (method overriding)
Allows the same interface to be used for different underlying data types.

4. Abstraction
Hiding complex implementation details and showing only essential features.
Achieved using abstract classes and interfaces.
Helps reduce programming complexity and increases efficiency.

Explain method overloading vs method overriding.

Method Overloading (Compile-time Polymorphism)


Same method name, but different parameter list (type, number, or order) within the same class.
Resolved at compile time.

Key Points:
Happens in the same class.
Parameters must be different.
Return type can be the same or different (but return type alone cannot distinguish overloads).

Example:
class Calculator {
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
int add(int a, int b, int c) {
return a + b + c;
}
}

Method Overriding (Runtime Polymorphism)


Subclass provides its own implementation of a method defined in its superclass.
Resolved at runtime via dynamic dispatch.

Key Points:
Happens in two classes in an inheritance hierarchy.
Method must have:
Same name
Same parameters
Same return type (or covariant)
The method in the subclass replaces (overrides) the method in the parent class.
Access modifier in the subclass cannot be more restrictive.

Example:

class Animal {
void sound() {
[Link]("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
void sound() {
[Link]("Dog barks");
}
}

Comparison Table

Feature Overloading Overriding

Where it occurs Same class Subclass and superclass

Parameters Must di er Must be exactly the same

Return type Can di er Must be same or covariant

Access modi er No restrictions Cannot be more restrictive

Polymorphism type Compile-time Runtime

Annotation used ❌ Optional ✅ Recommended: @Override

Analogy
• Compile-time polymorphism: Like choosing which tool to use before you start work.
• Runtime polymorphism: Like selecting the right tool while working, based on the situation.

What is the use of super and this keywords?

this Refers to the current object (the instance of the class where the code is running).
Refer to current class instance variables:

class Car {
String model;
ff
ff
fi
Car(String model) {
[Link] = model; // distinguish between field and parameter
}
}

Call another constructor in the same class:

class Car {
Car() {
this("Default Model");
}
Car(String model) {
[Link](model);
}
}

Pass current object as a parameter:

class A {
void display() {
[Link]("Hello");
}
void callDisplay() {
helper(this); // passes current object
}
void helper(A obj) {
[Link]();
}
}

super keyword
Refers to the parent (superclass) object of the current object.

Call superclass constructor:

class Animal {
Animal(String name) {
[Link]("Animal: " + name);
}
}
class Dog extends Animal {
Dog() {
super("Dog"); // calls Animal constructor
}
}

Access superclass method or field:

class Animal {
void speak() {
[Link]("Animal speaks");
}
}
class Dog extends Animal {
void speak() {
[Link](); // calls parent method
[Link]("Dog barks");
}
}

Summary

Keyword Refers To Common Use Cases

this Current object Access elds, call constructors

super Parent class Call superclass constructor or method

Can you override a static method?

Ans: No — it's hidden, not overridden.

Di erences between composition, inheritance, and aggregation?

1. Inheritance (IS-A relationship)


Concept:
• One class inherits properties and behavior (methods) from another class.
• The subclass is a type of the superclass.
Syntax Example:
class Animal {
void eat() {}
}
class Dog extends Animal {
void bark() {}
}
Use When:
• There is a clear "is-a" relationship.
• You want to reuse code and possibly override behavior.
Drawbacks:
• Tightly couples classes.
• Can lead to fragile code (changes in superclass affect all subclasses).
• Less flexible than composition.

2. Composition (HAS-A relationship – strong ownership)


Concept:
• One class contains another class.
• The contained object is created and owned by the container.
Syntax Example:
class Engine {
void start() {}
}
ff
fi
class Car {
private Engine engine = new Engine(); // composition
void startCar() {
[Link]();
}
}
Use When:
• You want to reuse behavior without inheritance.
• Prefer flexibility, encapsulation, and decoupling.
• The container controls the lifecycle of the component.

Aggregation (HAS-A relationship – weak ownership)


Concept:
• Similar to composition, but the contained object can exist independently of
the container.
• The container does not own the object.
Syntax Example:
class Student {}

class School {
private List<Student> students; // aggregation

void setStudents(List<Student> students) {


[Link] = students;
}
}
Use When:
• The part can exist without the whole.
• You want to associate objects without tightly coupling them.

🔄 Summary Table:

Feature Inheritance Composition Aggregation

Relationship IS-A HAS-A (strong) HAS-A (weak)

Coupling Tight Loose Loose

Reusability Through extension Through delegation Through association

Lifecycle Shared Contained (owns) Independent

Flexibility Low High High

Example Dog extends Animal Car has Engine School has Students
3. Data Types and Memory
Di erence between int, Integer, and long.

int (primitive type) 32-bit signed primitive data type. Stores whole numbers from -2³¹ to 2³¹ -
1.
More memory-efficient and faster than wrapper classes.

Default value: 0
int a = 10;

Integer (wrapper class) A class that wraps the primitive int in an object.
Part of [Link] package. Used when you need an object (e.g., with collections like
List<Integer>).
Supports methods like parseInt(), compareTo(), etc.
Supports autoboxing/unboxing: automatic conversion between int and Integer.

Integer a = 10; // autoboxing


int b = a; // unboxing

long (primitive type) 64-bit signed primitive data type.


Stores whole numbers from -2⁶³ to 2⁶³ - 1.
Used when int is not big enough (e.g., timestamps, large IDs).

Default value: 0L
long id = 123456789012345L;

Summary Table
Feature int Integer long

Type Primitive Object (Wrapper Class) Primitive

Size 32 bits 32 bits (wraps int) 64 bits

Range ±2.1 billion ±2.1 billion ±9 quintillion

Default value 0 null (if uninitialized) 0L

Collections ❌ Not allowed ✅ Allowed (e.g., List<Integer>) ❌ Not allowed

Autoboxing ❌ ✅ Yes ❌

What is autoboxing/unboxing?

Java provides a convenient way to convert between primitive types (int, char, etc.) and their
wrapper classes (Integer, Character, etc.). This is known as:

Autoboxing Automatically converting a primitive type into its corresponding wrapper class.
Example:
int num = 10;
Integer boxed = num; // autoboxing: int → Integer

Unboxing Automatically converting a wrapper class object back into its corresponding primitive
type.
Example:
ff
Integer boxed = 20;
int num = boxed; // unboxing: Integer → int

Why is it useful?
Allows you to use primitive values in places where objects are required (like in collections):

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


[Link](5); // autoboxes int 5 → Integer
int x = [Link](0); // unboxes Integer → int

Performance Note:
Autoboxing creates new objects, which adds memory and CPU overhead.
In performance-critical code (e.g., inside loops), prefer primitives if possible.

Summary
Feature Description Example

Autoboxing Primitive → Wrapper Integer i = 5;

Unboxing Wrapper → Primitive int x = i;

Introduced in Java 5 (via enhanced for-loops, generics)

What is the default value of a local variable? (Ans: Compilation error if uninitialized.)

There is none — local variables must be explicitly initialized before use.


If you try to use a local variable without initializing it, the compiler will throw an error:

public class Example {


public static void main(String[] args) {
int x; // local variable

[Link](x); // ❌ Compilation error: variable x might not have been


initialized
}
}

Why?
Java does not assign default values to local variables (those declared inside methods,
constructors, or blocks).
This prevents bugs caused by assuming an uninitialized variable has a usable value.

Contrast: Instance and static variables


Do get default values:

Type Default Value

int 0

boolean false

Object null
Explain stack vs heap memory?

Stack Memory
Memory used for method execution and local variables.
Follows LIFO (Last In, First Out) order.

Key characteristics:
Fast access
Automatic allocation/deallocation — memory is released when the method finishes
Stores:
Primitive variables (e.g., int, boolean)
References to objects (not the objects themselves)

Example:
void example() {
int x = 10; // stored in stack
String s = "Hi"; // reference in stack, object in heap
}

Heap Memory
Memory used to store objects and class variables (static fields).
Shared across all threads.

Key characteristics:
Slower access than stack
Managed by the Garbage Collector (GC)
Stores:
All objects (e.g., instances created with new)
Static variables

Example:
String s = new String("Hello"); // object in heap, reference in stack

Comparison Table
Feature Stack Heap

Memory Scope Per thread Shared across all threads

Allocation Automatic Manual (via new), GC-managed

Speed Faster Slower

Lifetime Short (tied to method call) Long (until no longer referenced)

Stores Local variables, references Objects, static elds

Access LIFO (Last In, First Out) Random access

Common Interview Insight:


Memory leaks in Java usually happen due to objects in the heap not being garbage collected,
often because they're still referenced.
fi
How stack over ow or heap out-of-memory errors happen?

Stack Overflow:
Happens when the call stack gets too deep, usually from Infinite recursion(Excessive method
calls without returning)

Example:
public class StackOverflowExample {
public static void recurse() {
recurse(); // infinite recursion!
}
public static void main(String[] args) {
recurse();
}
}

Why?
Every method call adds a frame to the stack.
The stack has limited size (e.g., ~1MB by default), so repeated calls cause it to overflow.

Avoid Stack Overflow:


Ensure recursion has a proper base case
Use iteration instead of recursion when deep calls are expected

OutOfMemoryError (Heap)
Happens when the heap memory is full and the Garbage Collector (GC) can't free up enough space.

Common causes:
Creating too many objects
Holding references unnecessarily (memory leak)
Loading large data sets into memory

Example:
import [Link].*;
public class OutOfMemoryExample {
public static void main(String[] args) {
List<int[]> memoryHog = new ArrayList<>();
while (true) {
[Link](new int[1_000_000]); // allocates ~4MB each loop
}
}
}

What happens:
This program keeps allocating large chunks of heap memory without releasing any.
Eventually, the JVM throws: Exception in thread "main" [Link]: Java heap
space

Tips to Avoid These Errors

Avoid Heap OOM:


Release unused references (set to null, use local scope)
fl
Use memory-efficient data structures
Monitor memory usage with tools like VisualVM or Java Flight Recorder
Tune JVM heap settings: -Xms, -Xmx

What is the size and range of int, long, oat, etc.?

Java Primitive Data Types: Size & Range


Type Size Range (approximate) Default Value

byte 8-bit –128 to 127 0

short 16-bit –32,768 to 32,767 0

int 32-bit –2³¹ to 2³¹–1 → –2,147,483,648 to 0


2,147,483,647

long 64-bit –2⁶³ to 2⁶³–1 → ±9 quintillion 0L

oat 32-bit ±3.4 × 10³⁸ (7 decimal digits 0.0f


precision)

double 64-bit ±1.8 × 10³⁰⁸ (15 decimal digits 0.0d


precision)

char 16-bit Unicode characters (0 to 65,535) '\u0000'

boolean ~1-bit (JVM-dependent) true or false false

Examples:
int age = 25;
long bigNumber = 1_000_000_000L;
float pi = 3.14f;
double precisePi = 3.141592653589793;
char letter = 'A';
boolean isJavaFun = true;

Tips:
Use **float** for less precision (e.g., graphics), and **double** for more accurate math.
**int** is the default for whole numbers.

Add suffixes:
L for long: long id = 10000000000L;
f for float: float pi = 3.14f;

What is the di erence between primitive types and objects?

Feature Primitive Types Objects (Reference Types)

De nition Basic data types built into the Instances of classes (created using new)
language
Stored in Stack memory Heap memory (reference stored in stack)

Examples int, char, boolean, Integer, String, List, CustomClass


double
Default values Zero-like values (e.g., 0, false) null

Can be null? ❌ No ✅ Yes


fl
fi
ff
fl
Methods? ❌ No methods ✅ Has methods (e.g., [Link]())

Mutability Immutable Depends on the object (e.g., String is immutable, ArrayList is


mutable)
Used in
❌ No (must box to object) ✅ Yes
collections?

Primitive Example:
int x = 5;

Object Example:
Integer y = [Link](5); // Object wrapper for int
String s = "Hello";

Autoboxing and Unboxing


Java automatically converts between primitives and their object wrappers:
Integer obj = 10; // Autoboxing (int → Integer)
int num = obj; // Unboxing (Integer → int)

🧠 Interview-ready Summary:

“Primitive types in Java are the basic data types like int, boolean, and double — they are
stored on the stack and don’t have methods. Objects are instances of classes and are stored on
the heap, allowing method calls and more flexibility. Java also provides wrapper classes like
Integer and Double to bridge primitives with collections via autoboxing.”

What is pass by value vs pass by reference?


Pass by Value:
• A copy of the variable is passed to the method.
• Changes inside the method do not affect the original variable outside (for primitives).
• Java always uses pass by value — even for objects!

Java is Pass by Value Only, but here’s the twist:

1. Primitive Types:
• The value (like a number) is copied.
• Changes to the parameter don’t affect the original variable.

void changePrimitive(int x) {
x = 10;
}
int a = 5;
changePrimitive(a);
// a is still 5

2. Objects (Reference Types):


• The value of the reference (memory address) is passed.
• So: the reference itself is copied, not the object.
• You can modify the object via the reference, but can’t reassign the original reference.
void modifyList(List<String> list) {
[Link]("Hi"); // ✅ Modifies original object

list = new ArrayList<>(); // ❌ Reassignment doesn't affect caller

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


modifyList(myList);
// myList now has "Hi"

🧠 Interview-ready Summary:

“Java is strictly pass-by-value. For primitives, the actual value is copied. For objects, the
value of the reference is copied, meaning we can modify the object's contents but not reassign
the original reference. So Java is not pass-by-reference — just pass-by-value of the reference.”
"In Java, everything is passed by value. For primitives, the actual value is passed, so changes
don’t affect the original. For objects, a copy of the reference is passed, so you can change the
object’s contents but not reassign the caller’s original reference."
4. Strings and Immutability
Why is String immutable in Java?

1. Security
Strings are widely used in security-sensitive contexts like usernames, passwords, file paths,
network connections.
Immutability ensures once created, the value can’t be changed, preventing malicious code from
altering critical data.

2. String Pooling and Performance


Java uses a String Pool (a special memory area) to store unique String literals.
Because Strings are immutable, the same String object can be shared safely among multiple
references without risk of modification.
This saves memory and improves performance.

3. Thread Safety
Immutable objects are inherently thread-safe since their state can’t change.
Multiple threads can access the same String instance without synchronization.

4. Hashcode Caching
Strings are often used as keys in hash-based collections like HashMap.
The immutability ensures that the hashcode remains constant during the object's lifetime,
preventing bugs related to data retrieval.

How Java enforces String immutability:


The String class:
Declares the internal character array private final char[] [Link] not provide any methods
that modify this [Link] like concat(), substring() return new String objects instead of
changing the original.
Summary
Reason Explanation

Security Prevents unauthorized modi cation of critical strings

Performance Enables String Pooling and sharing of immutable objects

Thread Safety Safe to share across threads without synchronization

Consistent Hashcode Critical for correct behavior in hash-based collections

How does the String pool work?

It's a special memory area inside the heap where Java stores unique String literals.
When you create a String literal, Java checks the pool first:
If the String already exists, it returns the reference to that existing object.
If it doesn't exist, it creates a new String object in the pool and returns it.

Why is this useful?


Saves memory by avoiding duplicate String objects.
Improves performance because String comparisons (==) can be faster for literals pointing to the
same pooled object.

How it works — Example:


String s1 = "hello"; // stored in String pool
fi
String s2 = "hello"; // refers to same pooled String object as s1
String s3 = new String("hello"); // new object on heap, NOT in pool
[Link](s1 == s2); // true (same reference)
[Link](s1 == s3);// false (different objects)
[Link]([Link](s3)); // true (same content)

Adding Strings to the pool manually


You can explicitly add a String object to the pool using:

String s4 = new String("world");


String s5 = [Link](); // adds "world" to pool if not present, returns pooled ref
[Link](s4 == s5); // false (s4 is new object, s5 is pooled)

Summary:
Concept Description

String Pool Special memory area for unique String literals

Reuse Avoids duplication of String objects

Immutable Strings are immutable, so sharing is safe

intern() Method to add Strings to the pool manually

Di erence between StringBuilder, StringBu er, and String.

1. String
Immutable: Once created, its value cannot be changed.
Operations like concatenation create a new String object each time.
Stored in String Pool if created as literals.

Thread-safe by nature (immutable).


Use when you have fixed strings or few modifications.

Example:
String s = "hello";
s = s + " world"; // creates new String objects internally

2. StringBuilder
Mutable: Can change value without creating new objects.
Not thread-safe — no synchronization.
Faster than StringBuffer because of no synchronization overhead.
Use when you need to build or modify strings quickly in a single-threaded environment.

Example:
StringBuilder sb = new StringBuilder("hello");
[Link](" world");
[Link]([Link]()); // prints "hello world"

3. StringBuffer
Mutable: Like StringBuilder, but thread-safe.
All methods are synchronized.
ff
ff
Slower than StringBuilder due to synchronization overhead.
Use when you need to modify strings in a multi-threaded environment where thread safety is
required.

Example:
StringBuffer sbf = new StringBuffer("hello");
[Link](" world");
[Link]([Link]()); // prints "hello world"

Summary Table
Feature String StringBuilder StringBu er

Mutability Immutable Mutable Mutable

Thread Safety Yes (immutable) No Yes (synchronized)

Performance Slow for many modi cations Fast Slower due to sync

Use Case Fixed strings Single-threaded string Multi-threaded string manipulation


manipulation

What happens when you use == vs .equals() with Strings?

1. == Operator
Compares reference equality (i.e., do both variables point to the exact same object in memory?)
Returns true if both references point to the same String object, else false.

2. .equals() Method
Compares content equality (i.e., do both Strings have the same sequence of characters?)
Returns true if contents are equal, regardless of whether they are the same object.

Why this matters


String literals ("hello") are stored in the String pool and reused.
New String objects created with new keyword are stored on the heap separately.
Using == on Strings often leads to bugs unless you’re specifically checking if two variables
point to the same object.
Always use .equals() to compare the actual text inside Strings.
ff
fi
5. Collections Framework
Di erence between ArrayList and LinkedList.

ArrayList vs LinkedList
Feature ArrayList LinkedList

Underlying Data Structure Resizable array Doubly linked list

Access Time (get/set) O(1) — direct index access O(n) — needs traversal

Insertion/Deletion O(n) — shifting elements O(1) — just adjust pointers

Memory Overhead Less (stores elements in contiguous More (stores data + 2 pointers per node)
array)

Better For Frequent random access Frequent insertions/deletions, especially


at ends

Iteration Speed Faster (better cache locality) Slower due to node traversal

Quick Example:
ArrayList<Integer> arrList = new ArrayList<>(); // ArrayList example
[Link](10);
int val = [Link](0); // fast access

LinkedList<Integer> linkedList = new LinkedList<>(); // LinkedList example


[Link](10);
int val2 = [Link](0); // slower access

When to Use Which?


Use ArrayList when you need fast random access and fewer insertions/deletions.
Use LinkedList when you do lots of insertions/deletions (especially at the beginning or middle).

How does HashMap work internally?

1. Basic Structure
HashMap stores key-value pairs.
Internally, it uses an array of buckets.
Each bucket holds a linked list or a balanced tree (since Java 8) of entries (key-value pairs).

2. Hashing
When you put a key-value pair, HashMap:
Calls [Link]() to get an integer hash code.
Applies a hash function (usually a bitwise operation) to spread the hash codes evenly.
Computes the index in the bucket array: index = hash % array_length (actually using
bitmasking for power-of-two sized arrays).

3. Handling Collisions
Multiple keys can hash to the same bucket index (collision).
Initially, entries in a bucket are stored as a linked list.
Since Java 8, if the list gets too long (over 8 entries), it’s converted to a balanced tree
(red-black tree) to optimize lookup time from O(n) to O(log n).

4. Putting a Key-Value Pair (put())


ff
Compute bucket index.
If bucket is empty, create a new node with the key-value.
If bucket contains nodes:
Traverse the list/tree.
If key exists (based on equals()), replace the value.
Otherwise, add new node at the end (linked list) or insert in tree.

5. Getting a Value (get())


Compute bucket index using hash.
Traverse nodes in that bucket.
Use equals() to compare keys.
Return the value if found, else null.

6. Resizing
When the number of entries exceeds the load factor threshold (default 0.75), the HashMap
resizes:
Creates a new, larger bucket array (usually double the size).
Rehashes all existing entries to new buckets.
This helps maintain efficient O(1) average access time.

Summary Table
Aspect Details

Data Structure Array of buckets, each with linked list/tree

Hash Function Uses hashCode() + bitwise operations

Collision Handling Linked list → tree (if bucket large)

Resizing Occurs when load factor exceeded (default 0.75)

Time Complexity Average O(1) for get and put

Quick Visual:
sql
CopyEdit
[Bucket Array]
|-- Bucket 0 → (key1,value1) → (key9,value9)
|-- Bucket 1 → (key2,value2)
|-- Bucket 2 → null
|-- Bucket 3 → (key3,value3) → (key11,value11) → ... (list/tree)
...

Di erence between HashMap, TreeMap, and LinkedHashMap.

HashMap vs TreeMap vs LinkedHashMap


Feature HashMap TreeMap LinkedHashMap

Ordering No guaranteed order Sorted order (by natural Insertion order (or access
ordering or Comparator) order if con gured)

Underlying Data Structure Hash table (array + linked list/ Red-Black tree (self- Hash table + doubly linked
tree) balancing BST) list

Performance (get/put) O(1) average, O(n) worst case O(log n) O(1) average, O(n) worst case
ff
fi
Null keys/values Allows one null key, multiple No null keys, allows multiple Allows one null key, multiple
null values null values null values

Use case Fast lookup without order Sorted map needed Maintain insertion/access
requirement order

Quick explanations:

HashMap
Fast, unordered map.
Good default choice for most use cases.
No order guarantee when iterating keys or entries.

TreeMap
Keeps keys in sorted order.
Uses a Red-Black tree, so operations take O(log n).
Useful when you need sorted traversal or range queries.

LinkedHashMap
Maintains insertion order (or access order if accessOrder=true).
Slightly slower than HashMap due to maintaining a linked list.
Useful for caches or when order matters.

Example:
Map<String, Integer> hashMap = new HashMap<>();
Map<String, Integer> treeMap = new TreeMap<>();
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();

[Link]("c", 3);
[Link]("a", 1);
[Link]("b", 2);

[Link]("c", 3);
[Link]("a", 1);
[Link]("b", 2);

[Link]("c", 3);
[Link]("a", 1);
[Link]("b", 2);

[Link]("HashMap: " + [Link]()); // Order unpredictable


[Link]("TreeMap: " + [Link]()); // [a, b, c]
[Link]("LinkedHashMap: " + [Link]()); // [c, a, b]

What are HashSet, TreeSet, and their use cases?

HashSet vs TreeSet
Feature HashSet TreeSet

Underlying Structure Backed by a HashMap Backed by a TreeMap (Red-Black tree)

Ordering No guaranteed order Sorted order (natural order or


Comparator)
Performance (add, contains, remove) O(1) average, O(n) worst case O(log n)

Null Elements Allows one null element Does not allow null element

Use Cases Fast lookup, unique elements without Sorted unique elements with order
order

HashSet
Unordered collection of unique elements.
Fast insertion, deletion, and lookup.
Great when order does not matter but you want to ensure no duplicates.

TreeSet
Stores elements in sorted order.
Implements NavigableSet, so supports operations like first(), last(), headSet(), tailSet().
Use when you need a sorted set or range-based operations.

Example:
Set<String> hashSet = new HashSet<>();
[Link]("banana");
[Link]("apple");
[Link]("orange");
[Link]("HashSet: " + hashSet); // Order unpredictable

Set<String> treeSet = new TreeSet<>();


[Link]("banana");
[Link]("apple");
[Link]("orange");
[Link]("TreeSet: " + treeSet); // [apple, banana, orange]

When to Use
Scenario Use HashSet Use TreeSet

You need fast operations and order ✔


doesn’t matter

You need sorted elements ✔

You want to perform range queries or ✔


navigations

Fail-fast vs fail-safe iterators?

Fail-Fast vs Fail-Safe Iterators


Aspect Fail-Fast Iterator Fail-Safe Iterator

Behavior on Concurrent Modi cation Throws Does not throw exception; works on a
ConcurrentModi cationException if the copy of the collection
collection is modi ed after iterator
creation (except via iterator’s own
remove() method)
fi
fi
fi
Example Collections Collections like ArrayList, HashMap, Collections from [Link]
HashSet (from [Link]) package, like CopyOnWriteArrayList,
ConcurrentHashMap

Performance Faster, because no copying Slower, due to copying of data

Use case Used when you want to detect Used in concurrent environments
concurrent modi cation bugs needing safe iteration

How Fail-Fast Works


Iterator keeps a modification count (modCount) of the collection.
If the collection is structurally modified after iterator creation, the modCount changes.
Iterator detects the change and throws ConcurrentModificationException.

How Fail-Safe Works


Iterator works on a separate copy of the collection’s data.
Changes to the original collection do not affect the iterator.
No exceptions thrown for concurrent modifications.

Example:
List<Integer> list = new ArrayList<>([Link](1, 2, 3));
for (Integer num : list) {
[Link](4); // Throws ConcurrentModificationException (fail-fast)
}

Vs

CopyOnWriteArrayList<Integer> cowList = new CopyOnWriteArrayList<>([Link](1, 2, 3));


for (Integer num : cowList) {
[Link](4); // No exception; iterator works on snapshot (fail-safe)
}

Explain use cases or internals of CopyOnWriteArrayList?

It's a thread-safe variant of ArrayList.


Designed for scenarios where reads vastly outnumber writes.

Uses a copy-on-write strategy:


Every time you modify (add, remove, update), it creates a new copy of the underlying array.
Iterators operate on a snapshot of the array at the time they were created — no
ConcurrentModificationException.

How It Works Internally


Internal array is immutable during iteration.
On write operations (add(), remove()), the entire underlying array is copied, modified, then
replaced.
Because iterators have their own snapshot, reads are lock-free and very fast.
Writes are expensive due to copying — so best used when writes are rare.

When to Use
Ideal for read-heavy, write-light workloads.
Useful when you want safe iteration without explicit synchronization.
fi
Examples:
Event listeners
Caches with rare updates
Maintaining lists of subscribers in multi-threaded apps

Example

CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();


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

for (String s : list) {


[Link](s);
[Link]("C"); // No ConcurrentModificationException
}

Pros and Cons


Pros Cons

Thread-safe without locks for reads Expensive memory and CPU on writes

Iterators never throw CME Not suitable for frequent modi cations

Simple to use in concurrent code Copying whole array on every write

ConcurrentHashMap internals or other concurrent collections?

Overview
A thread-safe, high-performance implementation of the Map interface.
Designed for high concurrency with minimal contention.
Allows concurrent read and write operations without locking the entire map.

How It Works Internally

1. Segmented Locking (Java 7 and earlier)


The map was divided into multiple segments (like smaller hash tables).
Each segment had its own lock.
Threads locking different segments could operate concurrently.

2. Lock-Free Reads and CAS (Java 8+)


Java 8 replaced segments with a more efficient design using CAS (Compare-And-Swap) and
synchronized blocks on bins when necessary.
Reads are mostly lock-free.
Writes use fine-grained locking on individual bins or nodes.

3. Data Structure
Uses a hash table with buckets (bins).
Each bin contains a linked list or, if large enough, a balanced tree (like HashMap).
Supports safe concurrent access to buckets.

Key Features
Concurrent Reads: No locking needed; very fast.
fi
Concurrent Writes: Lock only a small part (bin/node), not the whole map.
Size Estimation: Provides approximate size quickly without locking entire map.
No null keys or values allowed (unlike HashMap).

Example

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();


[Link]("A", 1);
[Link]("B", 2);
[Link]("C", k -> 3);
[Link]([Link]("A")); // 1

When to Use
Highly concurrent environments with lots of reads and writes.
Applications like web servers, caches, real-time analytics.
Where thread safety and performance are critical.

Summary Table
Feature ConcurrentHashMap HashMap (not thread-safe)

Thread Safety Yes No

Locking Fine-grained, bin-level locking None

Null keys/values Not allowed Allowed

Concurrency Level High Single-threaded

Iterators Weakly consistent (no CME) Fail-fast

Explain the di erence between weakly consistent iterators of ConcurrentHashMap


and fail-fast iterators?

Weakly Consistent Iterators vs Fail-Fast Iterators


Feature Weakly Consistent Iterator Fail-Fast Iterator

Modi cation Detection Does not throw Throws


ConcurrentModi cationException if the ConcurrentModi cationException if the
collection is modi ed during iteration collection is modi ed (except via
iterator’s own remove)
View of Collection Re ects some but not necessarily all Re ects collection state at the moment
modi cations made after the iterator of iterator creation and stops if modi ed
was created concurrently

Thread Safety Designed for use in concurrent Not thread-safe; requires external
environments without extra synchronization for safe concurrent use
synchronization

Performance Allows concurrent modi cations with Detects modi cations immediately but
minimal blocking requires locking for thread safety

Examples Iterators of ConcurrentHashMap, Iterators of ArrayList, HashMap,


CopyOnWriteArrayList LinkedList

What This Means Practically


fl
fl
fi
fi
fi
fi
fi
fi
fi
ff
fi
fi
Fail-Fast Iterators:
If you modify the collection during iteration, you get a quick exception — helps catch bugs
early.

Weakly Consistent Iterators:


They don’t fail if the collection changes during iteration. Instead, they provide a best-effort
snapshot: you may or may not see changes made during iteration. This behavior is ideal in
concurrent environments where you don’t want to lock or throw exceptions.

Example (ConcurrentHashMap)

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();


[Link]("A", 1);
[Link]("B", 2);

Iterator<String> it = [Link]().iterator();
[Link]("C", 3); // Modify map during iteration — no exception!

while ([Link]()) {
[Link]([Link]()); // May or may not include "C"
}

may or may not include the newly added element "C" when iterating over a ConcurrentHashMap’s
keySet iterator is because of the weakly consistent nature of its iterator.

Here’s why:
When you create an iterator from a ConcurrentHashMap (like [Link]().iterator()), the
iterator gets a snapshot view of the map’s internal structure at some point in time.
Because ConcurrentHashMap is designed for concurrency without locking the whole map,
modifications made after the iterator is created are allowed and don’t cause exceptions.
However, the iterator does not guarantee that these later modifications (like [Link]("C", 3))
will be immediately visible in the iteration.
So, when you call [Link](), the iterator may or may not return the newly added key "C". It
depends on whether that part of the internal data structure was already visited or incorporated
when the snapshot was taken.

Contrast with Fail-Fast Iterators


In a fail-fast iterator (like in ArrayList), if you modify the collection during iteration, you
get a ConcurrentModificationException immediately to prevent inconsistent behavior.
ConcurrentHashMap instead opts for weak consistency to allow concurrent modification and
iteration, trading off strict consistency for better concurrency.

Summary
May or may not include "C" = iterator reflects some changes but not guaranteed to see all
concurrent updates.
This makes iterating over ConcurrentHashMap safe without locks but without strong consistency
guarantees.
6. Exception Handling
Di erence between Checked and Unchecked exceptions.

Checked vs Unchecked Exceptions


Aspect Checked Exceptions Unchecked Exceptions

De nition Exceptions that are checked at Exceptions checked at runtime (not by


compile-time compiler)

Inheritance Subclass of Exception (excluding Subclass of RuntimeException


RuntimeException)

Handling Requirement Must be either caught or declared in No requirement to catch or declare


method signature (throws)

Examples IOException, SQLException, NullPointerException,


ClassNotFoundException ArrayIndexOutOfBoundsException,
IllegalArgumentException

When to Use For recoverable conditions, or expected For programming errors, bugs, or
exceptional situations unexpected conditions

Compiler Enforcement Yes, compiler enforces handling No, compiler does not enforce handling

Checked Exceptions force you to handle or declare them, encouraging you to think about error
handling.

Unchecked Exceptions usually indicate programming mistakes (like accessing a null object), which
should be fixed rather than caught.

Example:
// Checked Exception example
public void readFile() throws IOException {
// Must handle or declare IOException
FileReader file = new FileReader("[Link]");
}

// Unchecked Exception example


public void divide(int a, int b) {
int result = a / b; // May throw ArithmeticException (unchecked)
}

What is the purpose of nally block?

The finally block in Java is used to execute code that must run regardless of whether an
exception is thrown or caught.

Purpose of the finally block:


To release resources like files, database connections, or network sockets.
To ensure cleanup code runs no matter what happens in the try or catch blocks.
It always executes after the try and catch, except if the JVM exits (e.g., via [Link]()) or
the thread is killed.

Example:
try {
// Code that may throw an exception
int result = 10 / 0;
fi
ff
fi
} catch (ArithmeticException e) {
[Link]("Caught exception: " + e);
} finally {
[Link]("This always runs!");
}
Output:
Caught exception: [Link]: / by zero
This always runs!
Key points:
finally runs whether or not an exception occurs.
Useful for cleaning up resources (closing streams, freeing connections).
If both try and finally have return statements, the one in finally will override the return from
try.

Can you catch multiple exceptions in a single catch block?

Yes, you can catch multiple exceptions in a single catch block in Java (starting from Java 7)
using the multi-catch syntax.

How to catch multiple exceptions:


Use the pipe (|) character to separate exception types in one catch:

try {
// code that might throw IOException or SQLException
} catch (IOException | SQLException e) {
[Link]("Caught exception: " + e);
}

Key points:
Reduces code duplication when you want to handle different exceptions the same way.
The exceptions must not have a subclass-superclass relationship.
The exception variable (e) is effectively final (can't be reassigned inside the catch).

Custom exceptions: how and when to create one?

When to Create a Custom Exception

To represent domain-specific errors (e.g., InvalidUserInputException,


InsufficientFundsException)
To make your API more readable and meaningful
When standard exceptions don’t provide enough context
To help other developers handle errors more precisely

How to Create a Custom Exception


1. Extend Exception for a checked exception
or
2. Extend RuntimeException for an unchecked exception

Example – Checked Exception

public class InvalidAgeException extends Exception {


public InvalidAgeException(String message) {
super(message);
}
}

Usage

public void registerUser(int age) throws InvalidAgeException {


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

Example – Unchecked Exception


public class DatabaseUnavailableException extends RuntimeException {
public DatabaseUnavailableException(String message) {
super(message);
}
}
Usage:

if (![Link]()) {
throw new DatabaseUnavailableException("Cannot connect to DB.");
}

Best Practices
Inherit from the correct type: Exception vs RuntimeException.
Always include a constructor that accepts a message (and optionally a cause).
Use meaningful names to describe the error clearly.
Document what causes the exception and how to handle it.
7. Concurrency and Multithreading
What is the di erence between Runnable and Thread?

Feature Runnable Interface Thread Class

Type Interface Class (extends Object)

Inheritance Can be implemented alongside other Cannot extend another class if you
classes (no inheritance restriction) extend Thread

Design Principle Follows composition (preferred) Follows inheritance

How to Use Implement Runnable and pass to a Extend Thread and override run()
Thread object

Reusability Better — separates task logic from Less reusable — task logic and thread
thread management code are coupled

Flexibility More exible — can share the same Less exible — tied to a single thread
Runnable across threads instance

Example: Using Runnable (Preferred)

class MyTask implements Runnable {


public void run() {
[Link]("Running task...");
}
}

Runnable task = new MyTask();


Thread thread = new Thread(task);
[Link]();

This approach decouples the task logic (MyTask) from how the thread is managed.

Example: Extending Thread

class MyThread extends Thread {


public void run() {
[Link]("Running thread...");
}
}

Thread thread = new MyThread();


[Link]();

This works, but it's less flexible because you can’t extend another class.

When to Use What?


Use Runnable when:
You want better separation of concerns.
Your class already extends another class.
You're using thread pools or Executors.
fl
fl
ff
Use Thread when:
You need to override additional Thread behaviour (rare).
You're writing simple one-off scripts.

What is synchronization? How does synchronized work?

What is Synchronization in Java?


Synchronization is a mechanism in Java to control access to shared resources by multiple
threads, preventing race conditions and ensuring data consistency.
When multiple threads access a shared resource (like a variable or method), there's a risk of
them interfering with each other. Synchronization ensures that only one thread can access the
critical section at a time.

How synchronized Works:


The synchronized keyword can be applied to:
Instance methods
Static methods
Code blocks

It works by acquiring a lock (monitor) on an object or class before entering the synchronized
section.

1. Synchronized Instance Method


public synchronized void increment() {
count++;
}
Locks on the current instance (this).
Only one thread per object can execute this method at a time.

2. Synchronized Static Method


public static synchronized void log() {
// ...
}
Locks on the Class object, not the instance.
Only one thread per class can access this method at a time.

3. Synchronized Block
public void update() {
synchronized (this) {
// critical section
}
}
Allows you to lock on any object (including this or a separate lock object).
More flexible and efficient than synchronizing the entire method.

Example Problem: Race Condition

class Counter {
int count = 0;

public void increment() {


count++; // not thread-safe
}
}

If two threads call increment() at the same time, count may get corrupted.
Fix with synchronization:
public synchronized void increment() {
count++;
}

Key Points
Synchronization prevents thread interference and memory consistency errors.
It introduces performance overhead due to locking.
Always use it only on critical sections, not entire methods if possible.

What are race conditions and how do you avoid them?

A race condition occurs in a multithreaded environment when two or more threads access shared
data at the same time, and the final result depends on the timing of their execution.
This can lead to unpredictable, inconsistent, or corrupt results.

Example of a Race Condition


class Counter {
int count = 0;
public void increment() {
count++;
}
}

If two threads run increment() simultaneously, both may read the same count, increment it, and
write back the same value — one increment gets lost.

How to Avoid Race Conditions

1. Use synchronized
Synchronize critical sections so that only one thread can access them at a time.
public synchronized void increment() {
count++;
}

Use Lock (e.g., ReentrantLock)


Provides more control than synchronized.
Lock lock = new ReentrantLock();
public void increment() {
[Link]();
try {
count++;
} finally {
[Link]();
}
}

3. Use Atomic Variables


Like AtomicInteger from [Link].
AtomicInteger count = new AtomicInteger();
public void increment() {
[Link]();
}

4. Use Concurrent Collections


Use thread-safe collections like ConcurrentHashMap, CopyOnWriteArrayList, etc.

What doesn't prevent race conditions:


Declaring variables volatile (it only ensures visibility, not atomicity).
Using local variables only helps if no shared state is involved.
A race condition = multiple threads, shared data, no coordination → bugs
Avoid with: synchronized, Lock, Atomic classes, or concurrent data structures

What is ReentrantLock in Java?

ReentrantLock is a class in [Link] that provides explicit locking — an


alternative to Java's built-in synchronized keyword.
"Reentrant" means:
A thread can acquire the same lock multiple times without causing a deadlock, as long as it
releases it the same number of times.

[Link](); // First acquire


[Link](); // Allowed again (reentrant)
[Link]();
[Link]();

🔄 Basic Usage:

import [Link];
ReentrantLock lock = new ReentrantLock();
public void doSomething() {
[Link](); // Acquires the lock
try {
// Critical section (only one thread can be here)
} finally {
[Link](); // Always release in finally block
}
}

Key Features vs synchronized:

Feature synchronized ReentrantLock

Lock acquisition Implicit Explicit ([Link]())

Try-lock (non-blocking) ❌ Not supported ✅ tryLock()


Timeout support ❌ Not supported ✅ tryLock(timeout)

Interruptible locking ❌ No ✅ lockInterruptibly()

Fairness ❌ No ✅ Optional (new ReentrantLock(true))

Condition variables ❌ Intrinsic wait/notify ✅ Uses Condition objects

Advanced Feature: tryLock()


if ([Link]()) {
try {
// Work
} finally {
[Link]();
}
} else {
// Couldn't get the lock
}

Interview Summary:
"ReentrantLock is an explicit lock in Java that provides more flexible locking than
synchronized. It supports reentrancy, try-locks with timeouts, interruptible waits, and
condition variables. It’s useful when you need more control over lock behavior in multithreaded
applications."

What are volatile and atomic variables?

Volatile Keyword

What It Does:
Ensures visibility of changes to variables across threads.
Tells the JVM: “Do not cache this variable; always read it from main memory.”

What It Doesn’t Do:


It does not make operations atomic.
It doesn't prevent race conditions on compound operations like x++.

Example:
private volatile boolean running = true;
public void stop() {
running = false;
}

Without volatile, other threads may cache running and not see the updated value immediately.

Atomic Variables (e.g., AtomicInteger)


From [Link].*

What They Do:


Provide atomic (thread-safe) operations without using locks.
Internally use CAS (Compare-And-Swap) at the hardware level.
Example:
AtomicInteger count = new AtomicInteger(0);
[Link](); // Thread-safe atomic increment
Unlike int, this handles race conditions safely without synchronization.

Summary
Feature volatile AtomicInteger (and others)

Guarantees Visibility only Visibility and atomicity

Prevents Race Conditions ❌ No ✅ Yes

Use Case Simple ags or state changes Counters, accumulators, etc.

Lock-Free Yes Yes (via CAS)

When to Use What?


Use volatile for flags or state indicators (e.g., isRunning, shutdownRequested).
Use atomic variables for counters, updates, or any read-modify-write operations.

What is the di erence between wait() and sleep()?

wait() vs sleep()
Feature wait() sleep()

De ned in [Link] [Link]

Used for Thread coordination (e.g., producer- Pausing execution (e.g., delay)
consumer)

Releases lock? ✅ Yes, it releases the monitor lock ❌ No, it keeps any held locks

Must be inside synchronized block? ✅ Yes ❌ No

Can be interrupted? ✅ Yes, throws InterruptedException ✅ Yes, throws InterruptedException

Wakes up via notify() / notifyAll() Automatically after timeout

Example of sleep() (simple pause)

[Link](1000); // pauses for 1 second


No lock needed, just delays the thread.

Example of wait() (thread coordination)


synchronized (sharedObject) {
[Link](); // waits until notified
}

Requires synchronization, releases the lock until notified.

When to Use
Use sleep() when you just want to pause execution.
Use wait() when you're coordinating threads — like one thread waiting for another to produce
data.
fi
fl
ff
Explain ExecutorService and thread pools.

What is ExecutorService?
ExecutorService is part of the [Link] package. It’s a higher-level API for
managing and controlling thread execution compared to manually creating Thread objects.
Instead of manually starting threads, you:
Submit tasks (Runnable or Callable)
Let the service manage the thread lifecycle

What is a Thread Pool?


A thread pool is a group of pre-created threads that can be reused to execute multiple tasks.
This avoids the overhead of creating a new thread for each task.

Benefits:
Better performance: Reuses threads, reducing object creation/destruction overhead.
Control: Limits the number of concurrent threads.
Resource management: Avoids running too many threads and exhausting system resources.

Common Thread Pool Types (via Executors factory)

Method Description

[Link](n) Pool with a xed number of threads

[Link]() Grows/shrinks based on demand

[Link]() Single-threaded execution

[Link](n) For scheduled (delayed or periodic) tasks

Example: Using ExecutorService


ExecutorService executor = [Link](3);
Runnable task = () -> {
[Link]("Task run by " + [Link]().getName());
};

for (int i = 0; i < 5; i++) {


[Link](task); // or [Link](task);
}
[Link](); // graceful shutdown

Callable + Future
If you want the task to return a result:

Callable<Integer> task = () -> 42;


Future<Integer> future = [Link](task);
Integer result = [Link](); // blocks until result is ready

Shutting Down ExecutorService


Always shut down to free resources:

[Link](); // graceful
// OR
[Link](); // forceful
fi
Summary
ExecutorService = thread manager
Thread pool = pre-created threads reused for tasks
Runnable = no return; Callable = returns value
Use Future to get result or check task status
Always shut down the executor when done
ExecutorService is a high-level interface for executing asynchronous tasks. It internally uses a
thread pool to manage thread reuse and efficiency. So while a thread pool is the underlying
mechanism, ExecutorService is the abstraction that developers interact with to manage concurrent
execution."
8. Java Keywords and Modi ers
What is the di erence between nal, nally, and nalize()?

final (Keyword)
Used to declare constants, prevent overriding, or reassignment.

Use Cases:
Final variable: Cannot be reassigned.
final int x = 10;

// x = 20; // ❌ Compilation error

Final method: Cannot be overridden by subclasses.


public final void display() {}

Final class: Cannot be subclassed.


public final class MathUtils {}

finally (Block)
Used in exception handling to guarantee execution of a block of code (usually for cleanup)
regardless of whether an exception occurs.

Example:
try {
int result = 10 / 2;
} catch (ArithmeticException e) {
[Link]("Error!");
} finally {
[Link]("This will always run.");
}

Runs whether or not an exception is thrown


Common use: close files, release resources, etc.

finalize() (Method) [⚠ Deprecated]


Used to perform cleanup before garbage collection, called by the Garbage Collector (GC).

Example:
@Override
protected void finalize() throws Throwable {
[Link]("Object is being garbage collected");
}

⚠ Deprecated since Java 9 and removed in later versions — it's unreliable and discouraged.

Summary Table
Keyword Purpose Called by Use Case Example

nal Make variable/method/class Developer (compile-time) Prevent inheritance or


unchangeable reassignment

nally Always executes cleanup JVM (after try/catch) Release resources (e.g. close
code le)
fi
fi
fi
ff
fi
fi
fi
fi
nalize() Run before object is garbage JVM (GC) [Deprecated] Rarely used, bad practice
collected

Can a constructor be nal?

No, a constructor cannot be final in Java.

Why?
The final keyword in Java is used to prevent method overriding.
But constructors are not inherited, and therefore cannot be overridden.
Since there's no need to prevent overriding, marking a constructor final makes no sense — and
the compiler disallows it.

Example (will not compile):


public class MyClass {

public final MyClass() { // ❌ Compilation error


[Link]("Constructor");
}
}

Error: Illegal modifier for the constructor; only public, protected, private, or default are
permitted.

Valid Modifiers for Constructors:


public
protected
private
(package-private — no modifier)

🧠 Summary

Modi er Allowed for Constructors? Purpose

nal ❌ No Not applicable (constructors can't be


overridden)

static ❌ No Constructors are instance-speci c

abstract ❌ No Constructors must be concrete

synchronized ❌ No

What does the static keyword mean?

The static keyword in Java means "belongs to the class, not to an instance." It can be used with
variables, methods, blocks, and nested classes.

Key Uses of static

Element What It Means

static variable Shared across all instances of the class


fi
fi
fi
fi
fi
static method Can be called without creating an object

static block Executes once when the class is loaded

static class A nested class that doesn’t need a reference to an


outer instance

Examples
1. Static Variable
class Example {
static int count = 0;
Example() {
count++;
}
}
count is shared among all instances.
Used for constants and shared counters.

2. Static Method
class MathUtils {
static int square(int x) {
return x * x;
}
}
// Usage
int result = [Link](5); // No object needed
Cannot access non-static (instance) variables or methods.
Common example: main() method is static.

3. Static Block
class Config {
static {
[Link]("Class loaded!");
}
}
Executes once when the class is first loaded.
Often used for static initialization.

4. Static Nested Class


class Outer {
static class Nested {
void show() {
[Link]("Inside static nested class");
}
}
}
Doesn’t need an instance of Outer to be used.

Things to Remember
Static methods can't use this or super.
Static methods can't access non-static fields directly.

Summary
Use static for shared state or utility methods.
Avoid excessive use — it breaks OOP principles like encapsulation.

What is transient and volatile?

transient
Used with fields to exclude them from serialization.
When an object is serialized (converted to bytes), transient fields are skipped — their values
won’t be saved.
Useful for sensitive data (passwords) or fields that can be recalculated or aren’t needed in
serialized form.

Example:
class User implements Serializable {
private String username;
private transient String password; // won't be serialized
// constructors, getters, setters
}

volatile
Used with fields to ensure visibility of changes across threads.
Guarantees that when one thread updates a volatile variable, other threads see the latest value
immediately.
Does not guarantee atomicity for compound operations (like count++).

Example:
private volatile boolean running = true;
public void stop() {
running = false; // visible to other threads immediately
}

Summary
Modi er Purpose Use Case

transient Exclude eld from serialization Skip sensitive or irrelevant data

volatile Ensure visibility between threads Flags, status indicators in concurrency

What is the di erence between Callable and Runnable?

Feature Runnable Callable

Returns a result ❌ No (void run()) ✅ Yes (V call())

Throws checked exceptions ❌ No ✅ Yes

Interface method run() call()

Runnable only has void run() and can't return a result or throw checked [Link]<V>
has V call() that returns a value and can throw exceptions.
Example:
ExecutorService executor = [Link](2);
fi
fi
ff
Runnable runnableTask = () -> [Link]("Running");
Callable<String> callableTask = () -> {
[Link](1000);
return "Callable Result";
};

[Link](runnableTask);
Future<String> future = [Link](callableTask);
[Link]([Link]()); // prints "Callable Result"
[Link]();

What are Thread Pools?

• A ThreadPool is a collection of reusable threads for executing tasks.


• Managed by ExecutorService.
Why use it?
• Avoids creating new threads every time (which is costly).
• Controls the number of concurrent threads.
• Helps in managing system resources efficiently.

Example:
ExecutorService executor = [Link](3);
[Link](() -> [Link]("Runnable Task"));
Future<String> future = [Link](() -> "Callable Task");
[Link]([Link]()); // prints "Callable Task"
[Link]();

Types of Thread Pools:


• newFixedThreadPool(n) — fixed number of threads

• newCachedThreadPool() — grows as needed

• newSingleThreadExecutor() — one thread

• newScheduledThreadPool(n) — for scheduled tasks

Di erence between CompletableFuture vs Future?

Aspect Future CompletableFuture

Introduced in Java 5 ([Link]) Java 8 ([Link])


Blocking Can be non-blocking with callbacks (thenApply, thenAccept,
get() blocks until the result is ready
behavior etc.)
Limited — mainly for submitting async tasks
Async support Full support for async programming and chaining
but no chaining
Chaining / Supports chaining multiple futures and combining results
No support for chaining/composition
Composition (thenApply, thenCompose)
Exception
Manual, usually with try-catch on get() Built-in support (exceptionally, handle)
handling
Completeness Represents a pending result only Can be explicitly completed or combined with other futures

API complexity Simple interface with get(), cancel() Rich API with many methods for functional-style async programming
ff
In short:
• Future is a basic way to get a result from an async task, but you have to block and wait
(get()), and it doesn’t support chaining.
• CompletableFuture is a powerful, flexible tool for asynchronous programming that lets you
compose, combine, and handle async tasks without blocking.

Example Future (blocking):


ExecutorService executor = [Link]();
Future<String> future = [Link](() -> {
[Link](1000);
return "Hello";
});
[Link]([Link]()); // blocks until done
[Link]();

Example CompletableFuture (non-blocking chaining):


[Link](() -> "Hello")
.thenApply(s -> s + " World")
.thenAccept([Link]::println); // prints "Hello World" asynchronously

What are Thread Safety Patterns?

• Thread-safe means that code or data structures function correctly during simultaneous
execution by multiple threads.
• Without thread safety, threads can interfere with each other leading to bugs like lost
updates or inconsistent reads.

Common Thread Safety Patterns


1. Immutable Objects
• Make objects immutable so their state cannot change after creation.
• No synchronization needed because data cannot be modified.

final class ImmutablePerson {


private final String name;
private final int age;

public ImmutablePerson(String name, int age) {


[Link] = name;
[Link] = age;
}
// only getters, no setters
}

2. Synchronization
• Use the synchronized keyword to control access to critical sections.
public synchronized void increment() {
count++;
}
• Only one thread can execute a synchronized method/block at a time.

3. Explicit Locks
• Use Lock interface (e.g., ReentrantLock) for more flexible locking.
Lock lock = new ReentrantLock();
[Link]();
try {
// critical section
} finally {
[Link]();
}
• Allows features like try-lock, timed-lock, or interruptible lock acquisition.

4. Volatile Variables
• Declaring a variable volatile ensures visibility of changes across threads immediately.
private volatile boolean flag = false;
• Useful for flags or state checks, but does not guarantee atomicity.

5. Atomic Variables
• Use classes like AtomicInteger, AtomicBoolean for lock-free thread-safe operations.
AtomicInteger count = new AtomicInteger(0);
[Link]();
• Provides atomic read-modify-write operations without synchronization overhead.

6. Thread-safe Collections
• Use concurrent collections from [Link], e.g.:
◦ ConcurrentHashMap
◦ CopyOnWriteArrayList
◦ BlockingQueue
These are designed internally to handle concurrent access efficiently.

7. Thread Confinement (Local Variables)


• Keep data local to a thread (e.g., method variables or ThreadLocal) so no sharing
happens.
ThreadLocal<Integer> threadLocalValue = new ThreadLocal<>();

Summary

Pattern Use When... Bene ts

Immutable Objects Data doesn’t need to change Simple, no synchronization

Synchronization Critical sections need exclusive access Simple, but can cause blocking

Explicit Locks Need ne control over locking Flexible lock management

Volatile Need visibility for shared ags Lightweight visibility guarantee

Atomic Variables Need atomic read-modify-write without locks High performance

Thread-safe Collections Managing shared collections Safe concurrent access

Thread Con nement Data only needed by one thread No synchronization needed
fi
fi
fi
fl
What’s the Lock interface?

• Part of [Link] package.


• Provides more flexible and advanced thread synchronization than the traditional
synchronized keyword.
• Allows explicit locking and unlocking of critical sections.

Why use Lock over synchronized?

Feature synchronized Lock interface

Lock acquisition Implicit (block entry) Explicit ([Link]() and unlock())

Lock release Automatically on block exit Must manually unlock (unlock())

Flexibility Limited Can try locking, timeout, interruptible lock acquisition

Condition variables Only one per object Supports multiple Condition objects

Debugging & monitoring Hard Easier to debug (can check if locked)

Basic Usage of Lock


import [Link];
import [Link];

public class Counter {


private final Lock lock = new ReentrantLock();
private int count = 0;

public void increment() {


[Link](); // acquire the lock
try {
count++;
} finally {
[Link](); // always unlock in finally block
}
}
}
• You must unlock in a finally block to avoid deadlocks.

• ReentrantLock is the most commonly used implementation, allowing a thread to re-acquire


the lock it already holds.

Additional Features of Lock:


• tryLock(): Attempts to acquire the lock without waiting.
• lockInterruptibly(): Allows the thread to be interrupted while waiting for the lock.
• newCondition(): Creates a Condition object for more advanced thread signaling (like wait/
notify but better).

Interview-friendly summary:
“The Lock interface is an advanced alternative to the synchronized keyword for thread
synchronization. It gives explicit control over locking, allowing features like tryLock, timed
lock waits, and interruptible locks. ReentrantLock is a popular implementation that I use when I
need more flexibility than synchronized provides.”
9. Interfaces vs Abstract Classes
Key di erences between interface and abstract class?

Feature Interface Abstract Class

Purpose De nes a contract (methods to implement) Provides partial implementation and contract

Methods All abstract by default (Java 7 and earlier) Can have abstract and concrete methods
Since Java 8: default and static methods
allowed

Multiple Inheritance Supports multiple inheritance (a class can No multiple inheritance (a class can extend
implement multiple interfaces) only one abstract class)

Fields Only public static nal constants Can have instance variables, any access
modi er

Constructors No constructors Can have constructors

Access Modi ers for Methods Methods are implicitly public Methods can have any visibility (public,
protected, private)

When to Use When you want to specify a contract for When you want to share code among closely
unrelated classes related classes

Inheritance keyword implements extends

Quick Examples
Interface
interface Flyable {
void fly();
}

Abstract Class:
abstract class Bird {
void eat() {
[Link]("Eating");
}
abstract void fly();
}

Summary
Use interface to define capabilities or contracts without implementation.
Use abstract class to provide shared code and enforce some methods to be implemented.

Can interfaces have default methods?

Yes! Since Java 8, interfaces can have default methods.

What are Default Methods?


Methods with a default implementation inside an interface.
Allow you to add new methods to interfaces without breaking existing implementations.
Classes implementing the interface can use or override these default methods.

Syntax Example:

interface Vehicle {
fi
fi
ff
fi
fi
void start();
default void stop() {
[Link]("Vehicle stopped.");
}
}

class Car implements Vehicle {


public void start() {
[Link]("Car started.");
}
}

public class Test {


public static void main(String[] args) {
Vehicle v = new Car();
[Link](); // Output: Car started.
[Link](); // Output: Vehicle stopped.
}
}

Key Points
Default methods are concrete.
Help maintain backward compatibility when interfaces evolve.
Classes can override default methods.
Interfaces can also have static methods (since Java 8), but those belong to the interface and
aren’t inherited.

Can you implement multiple interfaces? Extend multiple classes?

Can you implement multiple interfaces?


Yes! Java allows a class to implement multiple interfaces.

Example:
interface Flyable {
void fly();
}
interface Swimmable {
void swim();
}
class Duck implements Flyable, Swimmable {
public void fly() {
[Link]("Duck is flying");
}
public void swim() {
[Link]("Duck is swimming");
}
}
Can you extend multiple classes?
No! Java does NOT support multiple inheritance of classes — a class can extend only one class.

Example:
class A { }
class B { }

// This will cause a compile-time error:


class C extends A, B { } // Not allowed

Why no multiple inheritance of classes?


To avoid ambiguity and the "Diamond Problem" where methods from multiple superclasses conflict.
Java uses interfaces to provide multiple inheritance of type (method signatures), but only one
class can be extended to inherit implementation.

Summary
Feature Allowed?

Implement multiple interfaces ✅ Allowed

Extend multiple classes ❌ Not allowed


10. Java 8+ Features (Modern Java)
What are lambdas and functional interfaces?

What is a Functional Interface?


An interface with exactly one abstract method.
Can have any number of default or static methods.
Used as the target type for lambda expressions.
Marked with the @FunctionalInterface annotation (optional but recommended).

Example:
@FunctionalInterface
interface Greeting {
void sayHello(String name);
}

What is a Lambda Expression?


A concise way to implement the single abstract method of a functional interface.
Enables writing anonymous functions (functions without a name).
Syntax: (parameters) -> expression or (parameters) -> { statements }

Example: Lambda with the Greeting interface


Greeting greet = (name) -> [Link]("Hello, " + name);
[Link]("Alice"); // Output: Hello, Alice

Why Use Lambdas?


Makes code more concise and readable.
Enables functional programming style.
Widely used with Java Streams API, event handling, etc.

Common Functional Interfaces in [Link]

Interface Description Method Signature

Predicate<T> Tests a condition (returns boolean) boolean test(T t)

Function<T,R> Maps input to output R apply(T t)

Consumer<T> Accepts input, returns nothing void accept(T t)

Supplier<T> Supplies a result (no input) T get()

Quick Example: Using Predicate


Predicate<Integer> isEven = (n) -> n % 2 == 0;
[Link]([Link](4)); // true

What is the Stream API and how is it used?

What is the Stream API?


A stream represents a sequence of elements supporting functional-style operations.
Streams do not store data; they operate on data sources like collections, arrays, or I/O
channels.
Supports lazy and parallel processing.
Provides operations like filter, map, reduce, collect, etc.

Key Characteristics of Streams

Characteristic Description

No storage Streams don't hold data themselves

Functional in nature Operations don't modify the source

Laziness Computation is deferred until result needed

Possibly in nite Streams can represent in nite sequences

Consumable Can only be traversed once

How to Use Streams? Basic Example


import [Link];
import [Link];
import [Link];

public class StreamExample {


public static void main(String[] args) {
List<String> names = [Link]("Alice", "Bob", "Charlie", "David");
List<String> filteredNames = [Link]() // create stream
.filter(name -> [Link]("A")) // intermediate operation
.map(String::toUpperCase) // intermediate operation
.collect([Link]()); // terminal operation

[Link](filteredNames); // Output: [ALICE]


}
}

Stream Operations:

Intermediate Operations (return a new Stream, lazy)


filter(Predicate) — keep elements that match condition
map(Function) — transform elements
sorted() — sort elements
distinct() — remove duplicates
limit(n) — limit the size
Terminal Operations (produce result or side-effect)
collect() — gather elements into a collection or other container
forEach() — perform an action for each element
reduce() — combine elements to a single value
count() — count elements

Why Use Streams?


More readable and concise than loops.
Enables parallel processing with .parallelStream().
Supports pipeline processing of data.
fi
fi
What are method references?

Method references are a neat shortcut in Java to use existing methods as lambda expressions
without writing the lambda code explicitly.

What Are Method References?


They refer to methods or constructors directly.
Syntax: ClassName::methodName or object::methodName.
Used when a lambda simply calls an existing method.
Makes code more readable and concise.

Types of Method References


Type Syntax Example Description

Static method ClassName::staticMethod Math::max Refers to a static method

Instance method of an object::instanceMethod [Link]::println Refers to an instance method


object of a particular object

Instance method of an ClassName::instanceMethod String::toUpperCase Calls instance method on


arbitrary object of a type input parameter

Constructor reference ClassName::new ArrayList::new Refers to a constructor

Examples
1. Static Method Reference
List<Integer> nums = [Link](3, 1, 4, 2);
[Link]()
.max(Integer::compare)
.ifPresent([Link]::println);

2. Instance Method of an Object


List<String> names = [Link]("Alice", "Bob");
[Link]([Link]::println);

3. Instance Method of Arbitrary Object


List<String> names = [Link]("alice", "bob");
List<String> upperNames = [Link]()
.map(String::toUpperCase)
.collect([Link]());

4. Constructor Reference
Supplier<List<String>> listSupplier = ArrayList::new;
List<String> list = [Link]();

When to Use?
When a lambda just calls an existing method.
Makes code cleaner and easier to read.
Di erence between map() and atMap()?

map() vs flatMap()
Aspect map() atMap()

Input Takes a function that transforms each Takes a function that transforms each
element to another object (can be any element into a Stream (or another at
type) structure)

Output Stream of transformed elements (e.g., Flattens multiple Streams into a single
Stream<R>) Stream (Stream<R>)

Use Case When you want to apply a one-to-one When you want to apply a one-to-many
transformation on elements transformation and atten the result

Resulting structure Nested streams if you return a Stream A single attened stream with all
inside map() elements combined

Example: map()
Suppose you have a list of strings and want their lengths:
List<String> words = [Link]("apple", "banana", "cherry");
List<Integer> lengths = [Link]()
.map(String::length) // one-to-one mapping: String -> Integer
.collect([Link]());
[Link](lengths); // Output: [5, 6, 6]

Example: flatMap()
Suppose you have a list of sentences and want all the words in a single stream:
List<String> sentences = [Link]("hello world", "java streams");
List<String> words = [Link]()
.flatMap(sentence -> [Link]([Link](" "))) // one-to-many: String ->
Stream<String>
.collect([Link]());
[Link](words); // Output: [hello, world, java, streams]

Here, flatMap flattens multiple streams of words into a single stream.

Summary
Use map() for simple transformations.
Use flatMap() when your transformation returns a stream or collection that needs to be flattened
into a single stream.

Optional class — why and how is it used?

The Optional class in Java helps you handle nullable values safely and avoid the dreaded
NullPointerException.

What is Optional?
A container object that may or may not contain a non-null value.
Introduced in Java 8 to represent optional values explicitly.
Forces you to think about the absence of a value instead of blindly using null.

Why Use Optional?


Makes your APIs clearer by explicitly indicating a value can be missing.
Encourages functional-style programming with safe chaining.
Helps reduce null checks and boilerplate code.
Prevents common runtime errors caused by null references.
fl
ff
fl
fl
fl
fl
How to Use Optional?
1. Creating Optional
Optional<String> opt1 = [Link]("Hello"); // non-null value
Optional<String> opt2 = [Link](); // empty Optional
Optional<String> opt3 = [Link](null); // nullable value

2. Accessing Value
Check if value is present
if ([Link]()) {
[Link]([Link]());
}

Or use ifPresent()
[Link](val -> [Link](val));

3. Provide Default Value


String result = [Link]("Default Value");
Or lazily with a supplier:
String result = [Link](() -> "Lazy Default");

4. Transform Value with map()


Optional<Integer> lengthOpt = [Link](String::length);
[Link](len -> [Link]("Length: " + len));

5. Avoid Nested Optionals with flatMap()


Optional<Optional<String>> nested = [Link]([Link]("Nested"));
Optional<String> flat = [Link](x -> x);

Example Usage:
public Optional<String> findNameById(int id) {
if (id == 1) {
return [Link]("Alice");
} else {
return [Link]();
}
}

Optional<String> nameOpt = findNameById(2);


String name = [Link]("Unknown");
[Link](name); // Output: Unknown

Summary
Feature Description

Represents nullable value Explicitly models absence of value

Encourages safe access Prevents NullPointerException

Supports functional methods map(), atMap(), lter()

Avoids boilerplate null checks Cleaner code


fl
fi
11. Spring Framework
What is @Component, @Service, @Repository?

All three are stereotype annotations and specializations of @Component. They mark classes as
Spring-managed beans, and Spring will auto-detect and register them with the ApplicationContext.

Annotation Typical Use Additional Behavior

@Component Generic bean Base annotation

@Service Business logic layer Indicates business intent

@Repository DAO / Persistence layer Enables exception translation for JPA/SQL

Example:
@Component
public class GenericBean {}

@Service
public class PaymentService {}

@Repository
public class PaymentRepository {}

What is Dependency Injection?

Dependency Injection (DI) is a design pattern used in Spring (and other frameworks) to manage
object dependencies.
Instead of a class creating its own dependencies (tight coupling), they are "injected" from the
outside — typically by the Spring container.

Analogy:
Imagine you're hiring a driver:
• Without DI: You build the car inside the driver class.
• With DI: You give the driver a car — the driver just uses it.

Why Use DI?


• Loose coupling
• Easier to test
• More modular code
• Promotes single responsibility

In Code (Constructor Injection – Recommended)

@Component
public class Car {
private final Engine engine;

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

public void start() {


[Link]();
}
}
Here, Spring injects the Engine dependency into Car. Car doesn't know how the engine is created.

Types of Dependency Injection in Spring:

Type How it's Done

Constructor Recommended, immutable, easy to test

Setter Spring calls a setter to inject the dependency

Field (Not preferred) Uses re ection to inject directly into elds

Testability Advantage:
Car car = new Car(mockEngine); // Easy to test with mocks!
You can easily mock the Engine and pass it to Car — no need for Spring context in a unit test.

Explain Spring Boot annotations?

1. @SpringBootApplication
Combines 3 key annotations:
• @Configuration – marks the class as a source of bean definitions

• @EnableAutoConfiguration – tells Spring Boot to auto-configure based on classpath

• @ComponentScan – scans the package for @Component, @Service, @Controller, etc.


Example:
@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
[Link]([Link], args);
}
}
“It’s the main entry point for a Spring Boot app. It bootstraps everything.”

@RestController

• Combines @Controller and @ResponseBody


• Used to expose REST APIs
• Returns data (like JSON), not views
Example:
@RestController
public class HelloController {
@GetMapping("/hello")
public String sayHello() {
return "Hello World";
fl
fi
}
}
“It’s for building RESTful APIs — the methods return JSON, not HTML.”

@RequestMapping and Shortcuts


• Maps HTTP requests to methods

Annotation HTTP Method

@RequestMapping Any

@GetMapping GET

@PostMapping POST

@PutMapping PUT

@DeleteMapping DELETE

Example:
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) { ... }
“It defines which URL and HTTP method should call which method in the controller.”

@Autowired
• Tells Spring to automatically inject a bean

@Autowired
private UserService userService;
“Spring looks for a bean of type UserService and injects it here.”

@Component, @Service, @Repository, @Controller

Annotation Purpose
@Component Generic Spring bean
@Service Business logic layer
@Repository Data access layer; adds exception translation
@Controller Web MVC controller (returns views)

@Configuration and @Bean


• Used to create beans manually in Java config

@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyService();
}
}
@Value and @ConfigurationProperties

• Inject values from [Link] or [Link]

@Value("${[Link]}")
private String appTitle;
Or bind whole config objects:

@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String title;
private int version;
}

@Profile
• Enables beans or configs only in specific environments (dev, prod, test)

@Profile("dev")
@Bean
public DataSource devDataSource() { ... }

How to Answer in Interview


“Spring Boot simplifies configuration using annotations. @SpringBootApplication starts
everything. @RestController and @RequestMapping are key for building REST APIs. I use @Autowired
for DI, and @Service, @Repository for structuring layers. Profiles help me separate dev and prod
configs.”

What is Spring Context, Pro les?

Enable different configurations for dev, test, prod, etc.

Example:
@Configuration
@Profile("dev")
public class DevDataSourceConfig {
@Bean
public DataSource dataSource() {
// dev DB config
}
}
Activate a profile:
yaml
CopyEdit
spring:
profiles:
active: dev
fi
What is the context in Spring?
The Spring ApplicationContext is a container that manages beans lifecycle and dependency
injection.
It acts as a central interface for configuration and manages the creation, wiring, and lifecycle
of Spring-managed objects.
Provides services like:
• Bean factory
• Event propagation
• Resource loading
• Internationalization support
12. Miscellaneous
What is serialization? How does it work

Serialization is a fundamental concept in Java for converting objects into a format that can be
easily stored or transmitted. Here’s a breakdown:

What is Serialization?
Serialization is the process of converting an object into a byte stream.
This byte stream can be saved to a file, sent over a network, or stored in a database.
The opposite process, deserialization, reconstructs the object from the byte stream.

Why Use Serialization?


To persist object state (e.g., saving user sessions).
To transfer objects between JVMs over networks (e.g., in RMI or distributed apps).
For caching, deep cloning, or messaging.

How Does Serialization Work in Java?


Implement Serializable Interface
A class must implement the marker interface [Link] to indicate that its objects
can be serialized. This interface has no methods.
Use ObjectOutputStream to Serialize
Write the object to a stream.
Use ObjectInputStream to Deserialize
Read the object back from the stream.

Simple Example:

import [Link].*;
class Person implements Serializable {
private static final long serialVersionUID = 1L; // version control

String name;
int age;

Person(String name, int age) {


[Link] = name;
[Link] = age;
}
}

public class SerializeDemo {


public static void main(String[] args) {
Person person = new Person("Alice", 30);
// Serialize
try (ObjectOutputStream oos = new ObjectOutputStream(new
FileOutputStream("[Link]"))) {
[Link](person);
} catch (IOException e) {
[Link]();
}

// Deserialize
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("[Link]"))) {
Person deserializedPerson = (Person) [Link]();
[Link]([Link] + ", " + [Link]);
} catch (IOException | ClassNotFoundException e) {
[Link]();
}
}
}
Important Points:
serialVersionUID: Used for versioning serialized classes. If changed between serialization and
deserialization, you get InvalidClassException.
Transient fields: Marked with transient keyword and not serialized.
If a non-serializable object is referenced inside a serializable class, serialization will fail
unless it’s marked transient.

How is memory managed in Java (GC, heap, stack)?

Java Memory Model: Key Areas

1. Heap Memory
Where all objects and instance variables live.
Shared among all threads.
Divided into:
Young Generation (Eden + Survivor spaces) — newly created objects.
Old (Tenured) Generation — long-lived objects promoted here.
Permanent Generation (or Metaspace in Java 8+) — stores class metadata, interned
strings.

2. Stack Memory
Each thread has its own stack.
Stores primitive local variables, method call frames, and references to objects on the heap.
Supports method invocation and return.
Stack memory is LIFO (Last In, First Out).

Garbage Collection (GC)


Java uses GC to automatically reclaim memory by removing objects no longer referenced.
Different GC algorithms exist (Serial, Parallel, CMS, G1, ZGC, Shenandoah) optimized for
different workloads.

GC process:
Mark: Identify reachable objects.
Sweep: Remove unreachable objects.
Compact: Optional step to reduce fragmentation.

How It Works Together


When you create a new object (new keyword), memory is allocated on the heap.
Local variables and method call data live in the stack.
When a method completes, its stack frame is popped.
If no references to an object remain, GC will eventually reclaim its heap memory.

Summary Table
Memory Area Purpose Lifetime Shared?
Heap Objects and instance Until GC removes Shared
variables
Stack Local primitives, method calls Until method ends Thread-local

Metaspace Class metadata, static info JVM lifetime Shared

Additional Notes
Finalizers (using finalize() method) are discouraged; use try-with-resources or explicit cleanup
instead.
You can suggest GC via [Link](), but it’s only a hint. Memory leaks can happen if references
unintentionally persist (e.g., static collections).

Explain class loading process.

The class loading process in Java is how the JVM loads class files into memory so that your Java
program can use them at runtime. Here’s a clear breakdown:

What is Class Loading?


Loading .class files (bytecode) into JVM memory.
Happens on demand — when a class is first referenced.

Main Steps of Class Loading

1. Loading
JVM locates the .class file (from disk, network, or other sources).
Reads the bytecode and creates an in-memory Class object.
Uses ClassLoader to do this.

2. Linking
Three sub-steps here:
Verification: Checks bytecode correctness to ensure it doesn't violate JVM rules.
Preparation: Allocates memory for static variables and sets them to default values.
Resolution: Replaces symbolic references with direct references (resolves other classes,
methods).

3. Initialization
Executes static initializers and static blocks in the class.
Initializes static variables with their explicit values.

Class Loader Hierarchy


Class Loader Loads

Bootstrap ClassLoader Core Java API classes ([Link]), part of JVM itself

Extension ClassLoader Classes from jre/lib/ext or Java extensions

System (Application) ClassLoader Classes from application classpath (-cp or CLASSPATH)

Custom Class Loaders


You can create your own ClassLoader by extending ClassLoader to load classes in special ways
(e.g., from encrypted files, network).
Useful in app servers, plugin systems.

When Does Loading Happen?


When a class is first accessed: creating instance, accessing static members, or invoking static
methods.
Lazy loading helps reduce startup time and memory footprint.

Summary

Phase What Happens

Loading Finds and reads class bytecode

Linking Veri es, prepares, and resolves references

Initialization Runs static initializers and assigns static vars

What is re ection?

Reflection is a feature in Java that allows a program to inspect and manipulate its own
structure (classes, methods, fields) at runtime.
It lets you dynamically examine classes, interfaces, fields, and methods, even if you don't know
their names at compile time.
Also enables you to invoke methods, access fields, and create objects dynamically.

Why Use Reflection?


Frameworks like Spring, Hibernate, JUnit use reflection to:
Instantiate classes dynamically.
Invoke methods (e.g., test methods).
Access private fields for configuration or testing.
Useful for building generic libraries and tools.
Enables dynamic behavior and introspection.
How Does Reflection Work?
Java provides the [Link] package with key classes:
Class<?> — Represents classes and interfaces.
Method — Represents methods.
Field — Represents fields.
Constructor — Represents constructors.

Simple Example:
Class<?> clazz = [Link]("[Link]"); // Load class dynamically
Method sizeMethod = [Link]("size"); // Get method info
Object listInstance = [Link]().newInstance(); // Create instance
int size = (int) [Link](listInstance); // Invoke method dynamically
[Link]("Size: " + size); // Output: Size: 0

Important Notes
Reflection bypasses normal access control checks, so you can access private members (with
setAccessible(true)).
It has performance overhead compared to direct calls.
Can lead to security risks if misused.
Use reflection sparingly and only when necessary.

Practical Use Cases of Reflection

Frameworks & Libraries


fi
fl
Dependency Injection: Frameworks like Spring use reflection to instantiate and wire classes
dynamically.
Testing: JUnit uses reflection to discover and invoke test methods.
Serialization/Deserialization: Libraries like Jackson inspect fields and constructors to convert
objects to/from JSON.
ORMs (Hibernate, JPA): Access entity metadata and fields at runtime.
Dynamic Proxies and AOP: Create proxy objects that add behavior (e.g., logging, transactions) at
runtime without changing source code.
Tools and Utilities: IDEs, debuggers, and profilers use reflection to inspect running
applications.
Plugin Architectures: Load and interact with plugins or modules dynamically without
recompilation.

Security Concerns:
Accessing Private Members: Reflection can access and modify private fields/methods, potentially
breaking encapsulation.
Bypassing Security Managers: Without proper security policies, reflection can be exploited to
access sensitive data.
Injection Attacks: Malicious input can cause unexpected reflection calls if not properly
validated.
Performance Overhead: Excessive reflection use can degrade performance.

Best Practices:
Restrict reflection use to trusted code.
Avoid reflection when alternatives exist.
Use SecurityManager or Java modules system to limit reflective access.

Reflection & Annotations:


Annotations provide metadata at runtime.
Reflection is used to read annotation information on classes, methods, fields, etc.

Example
@Retention([Link])
@interface MyAnnotation {
String value();
}

@MyAnnotation("ExampleClass")
class Example {}
public class Test {
public static void main(String[] args) {
Class<Example> clazz = [Link];
if ([Link]([Link])) {
MyAnnotation annotation = [Link]([Link]);
[Link]([Link]()); // Output: ExampleClass
}
}
}
What are enums and how are they useful?

What Are Enums?


Enums (short for enumerations) are a special Java class type that represents a fixed set of
constant values.
They were introduced in Java 5 (with the enum keyword).
Instead of using plain constants (static final fields), enums provide type safety and better
readability.

How to Define an Enum?


enum Day {

SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY

Why Are Enums Useful?


Type Safety
Only valid enum constants can be assigned, reducing bugs.
Example: You can’t assign any arbitrary integer or string accidentally.
Readability and Maintainability
Code is clearer because the possible values are explicitly defined in one place.
Built-in Methods
values(): Returns all enum constants as an array.
valueOf(String): Converts a string to the corresponding enum constant.
ordinal(): Returns the position/index of the enum constant.
Can Have Fields, Methods, and Constructors
Enums are more powerful than simple constants. You can associate data and behavior with
enum constants.

Example With Fields and Methods


enum Day {
SUNDAY(false), MONDAY(true), TUESDAY(true), WEDNESDAY(true),
THURSDAY(true), FRIDAY(true), SATURDAY(false);
private final boolean isWorkday;
Day(boolean isWorkday) {
[Link] = isWorkday;
}
public boolean isWorkday() {
return isWorkday;
}
}

Usage:
Day today = [Link];
if ([Link]()) {
[Link]("Time to work!");
} else {
[Link]("Relax!");
}

When to Use Enums?


When you have a fixed set of related constants (days, states, directions, types, etc.).
When you want to avoid magic numbers or strings.
When you want a clean, maintainable, and type-safe approach.
What Is CAS?

Compare-And-Swap (CAS) is an atomic operation that updates a variable only if it has


not been changed by another thread. It is a way to safely update a shared value
without locking.

How Does CAS Work?


CAS takes three parameters:
1. Memory location (V) – the value to check and possibly update.
2. Expected value (A) – the value you think is currently in V.
3. New value (B) – the value to set if V == A.

It works like this:


if (V == A) {
V = B;
return true;
} else {
return false;
}
This operation is atomic — meaning it’s completed in a single step without
interference.

Where Is CAS Used?


• Java's Atomic classes (AtomicInteger, AtomicLong, etc.) in [Link].
• Inside ConcurrentHashMap.
• In custom lock-free data structures.

Benefits
• No blocking/locks → Better performance and avoids deadlocks.
• High throughput in multi-threaded environments.

Limitations
1. Spin loops: CAS may retry many times if there's contention.
2. ABA Problem: If value changes from A → B → A again, CAS thinks nothing changed
(solved using stamps or version numbers).
3. Complex logic: CAS is best for simple updates — complex changes may require
other synchronization.
Java Example:
import [Link];
public class Example {
public static void main(String[] args) {
AtomicInteger counter = new AtomicInteger(5);
// Try to change 5 to 10
boolean updated = [Link](5, 10);

[Link]("Updated: " + updated); // true


[Link]("New Value: " + [Link]()); // 10
}
}
When will you use AtomicReference?

AtomicReference<T> is a class in [Link] that allows atomic


(thread-safe) updates to an object reference of type T. It is like AtomicInteger or
AtomicBoolean, but for any object type.

Why Use AtomicReference?


You’d use AtomicReference when:
1. You need to safely share and update an object between threads without using
locks (synchronized).
2. You want to implement lock-free algorithms or data structures.
3. You are working with immutable objects, and replacing them atomically (e.g.,
functional or concurrent programming patterns).

Example Use Case


import [Link];
class Person {
final String name;
Person(String name) { [Link] = name; }
}

public class Example {


public static void main(String[] args) {
AtomicReference<Person> ref = new AtomicReference<>(new Person("Alice"));

Person oldPerson = [Link]();


Person newPerson = new Person("Bob");

boolean updated = [Link](oldPerson, newPerson);

[Link]("Update success? " + updated);


[Link]("Current person: " + [Link]().name);
}
}

When is AtomicReference especially useful?


• Non-blocking stacks/queues (e.g., ConcurrentLinkedQueue).
• State machines where an object's state is changed atomically.
• Avoiding synchronized blocks while still ensuring data integrity.

Note
AtomicReference only helps with atomic reference changes — not with modifying fields
inside the object. If the object is mutable, further synchronization might still be
needed.
What’s an Observable?

• Observable is a core concept from Reactive Programming (popularized by libraries like


RxJava).
• It represents a stream of data or events that you can subscribe to and react to
asynchronously.
• Think of it as a data producer that emits items over time, and observers (or subscribers)
consume those items.

Key Points:
• It emits 0 or more items asynchronously.
• Observers can receive:
◦ Next items (data)
◦ Error notifications
◦ Completion notification (stream ended)
• Supports operators to transform, filter, combine data streams.

Simple analogy:
Imagine a news feed that pushes articles to subscribers as they come in — the news feed is the
Observable, and you are the subscriber receiving updates.

Basic RxJava Example:


Observable<String> observable = [Link]("Hello", "World");

[Link](
item -> [Link]("Received: " + item),
error -> [Link]("Error: " + error),
() -> [Link]("Done!")
);
Output:
makefile
CopyEdit
Received: Hello
Received: World
Done!

How is it different from a regular method?


• Instead of pulling data once, Observable pushes data over time.
• Enables reactive, event-driven programming with easy composition and concurrency.

Interview-friendly summary:
“An Observable is a stream of data or events that can emit multiple values asynchronously to
subscribers. It’s a key concept in reactive programming, allowing us to write non-blocking,
event-driven applications that can easily handle streams of data like user inputs, server
responses, or sensor data.”

What is the di erence between a mock and spy bean?

• Mock:
◦ A fake object that simulates the behavior of real objects.
ff
◦ Does not execute real methods unless explicitly specified.
◦ Used to isolate tests from dependencies.
• Spy:
◦ Wraps a real object.
◦ Real methods are called unless stubbed.
◦ Useful when you want to test part of the behavior while still using the real
object.

Aspect Mock Spy

De niti A mock is a test double that simulates a complete object. You A spy wraps a real object and partially mocks it — real methods are
on de ne its behavior explicitly. called unless explicitly stubbed.

Behavio By default, all methods return default values (null, 0,


By default, calls real methods unless stubbed otherwise.
r false) unless stubbed.

Use When you want to completely replace a dependency in your When you want to verify interactions but still use real method logic,
case test. or stub some methods only.

Created using
Creatio Created using [Link](realObject) or @Spy
n
[Link]([Link]) or @Mock
annotation.
annotation.

Exampl Mock a service to return prede ned responses without Spy on a list to verify method calls but still allow real add()
e running real code. behavior.

Simple Code Examples


List<String> mockList = [Link]([Link]);
[Link]([Link]()).thenReturn(5);
[Link]([Link]()); // prints 5
[Link]([Link](0)); // prints null because method not stubbed

Spy
List<String> realList = new ArrayList<>();
List<String> spyList = [Link](realList);
[Link]("Hello");
[Link](spyList).add("Hello");
[Link]([Link]()); // prints 1 because real method called

When to Use Which?


• Use mock when you want to simulate the whole behavior without running real code.
• Use spy when you want to wrap a real object and track or override some behaviors.

In Spring Boot Tests (@MockBean vs @SpyBean)

• @MockBean: Replaces a Spring bean with a mock.

• @SpyBean: Wraps a Spring bean with a spy, allowing partial mocking.

Interview-friendly summary:
“A mock is a fully simulated object where you define all behavior, useful for isolating tests. A
spy wraps a real object and lets you call real methods unless you stub them, allowing partial
mocking. In Spring Boot tests, you use @MockBean to replace beans and @SpyBean to partially mock
existing beans.”
fi
fi
fi
What is a S3?

Amazon Simple Storage Service (S3) is a highly scalable, durable, and secure object storage
service provided by AWS.
Key Points:
• Stores objects (files, images, videos, backups, etc.) in buckets.
• Objects can be any size, from kilobytes to terabytes.
• Designed for 99.999999999% (11 nines) durability — meaning your data is very safe.
• Offers high availability and scalability — you don’t need to worry about storage limits.
• Supports fine-grained access control, encryption, and lifecycle policies.
• Commonly used for:
◦ Static website hosting
◦ Backup and restore
◦ Data archiving
◦ Big data analytics input/output
◦ Media storage

Simple analogy:
Think of S3 as a cloud hard drive where you store files, accessible from anywhere via the
internet.

Basic structure:

Concept Description

Bucket Container for objects (like folders)

Object The le you store (with metadata)

Key The unique name of an object in a bucket


Interview-friendly summary:
“Amazon S3 is a cloud-based object storage service used to store and retrieve any amount of data
at any time. It’s highly durable, scalable, and secure, making it ideal for storing files,
backups, and static content. Data is organized into buckets, and each file is called an object
with a unique key.”

How to access S3?

Accessing Amazon S3 (Simple Storage Service) typically involves using AWS SDKs to interact with
buckets and objects (files). Here's a clear guide to help you explain or implement S3 access in
Java — perfect for your interview prep.
1. Set Up AWS SDK for Java
Add AWS SDK dependency (Maven example):
<dependency>
<groupId>[Link]</groupId>
<artifactId>s3</artifactId>
<version>2.x.x</version>
</dependency>

2. Configure AWS Credentials


• Credentials can be set via:

Environment variables (AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY)
fi

AWS credentials file (~/.aws/credentials)
◦ IAM roles if running on AWS infrastructure (EC2, Lambda, etc.)
• Using default credential provider chain is recommended.

3. Create S3 Client
import [Link].s3.S3Client;
import [Link];

S3Client s3 = [Link]()
.region(Region.US_EAST_1)
.build();

4. Basic Operations
a) List Buckets
[Link]().buckets().forEach(bucket -> {
[Link]([Link]());
});
b) Upload a File
import [Link];
import [Link];

PutObjectRequest putReq = [Link]()


.bucket("my-bucket")
.key("folder/[Link]")
.build();

[Link](putReq, [Link]([Link]("[Link]")));
c) Download a File
import [Link];
import [Link];

GetObjectRequest getReq = [Link]()


.bucket("my-bucket")
.key("folder/[Link]")
.build();

[Link](getReq, [Link]("[Link]"));

5. Best Practices
• Use IAM roles for AWS resources to avoid embedding credentials.
• Handle exceptions like S3Exception.
• For large files, use multipart uploads.
• Use async client for non-blocking operations if needed.

Interview Summary Answer


“To access S3 in Java, I use the AWS SDK. I create an S3Client configured with the right region
and credentials, then I can perform operations like listing buckets, uploading, and downloading
files using SDK methods such as putObject and getObject. Credentials are typically managed
securely via environment variables or IAM roles.”
13. Kafka
1. What is Apache Kafka?

• distributed streaming platform used for building real-time data pipelines and streaming
applications.
• designed to be fault-tolerant.
• high-throughput.
• horizontally scalable.
• based on a publish-subscribe messaging system
• stores streams of records in categories called topics.

2. What are the main components of Kafka?

• Producer: Sends data to Kafka topics.


• Consumer: Subscribes to topics and processes records.
• Broker: Kafka server that stores and serves data.
• Topic: Logical channel to which records are sent.
• Partition: Unit of parallelism in a topic; each topic can have multiple partitions.
• Zookeeper: Manages metadata, leader election (Note: Kafka is moving toward removing
Zookeeper).

3. What is a Kafka Topic and Partition?

• A Topic is a stream of messages of a particular category.


• A Partition is a subset of a topic that allows Kafka to parallelize data. Each partition is an
ordered, immutable sequence of records.

4. How does Kafka ensure fault tolerance?

Kafka replicates partitions across multiple brokers. Each partition has a leader and one or
more followers. If the leader fails, a follower takes over, ensuring no data is lost.

5. What is the role of Zookeeper in Kafka?

Zookeeper manages:
• Metadata about brokers and topics
• Leader election for partitions
• Cluster configuration and membership
Note: Kafka 2.8+ supports KRaft mode, which allows Kafka to run without Zookeeper.

6. What is a Kafka Consumer Group?

A consumer group is a set of consumers that work together to consume data from a topic. Each
partition is consumed by only one consumer in the group. This enables load
balancing and parallel processing.
7. What is Kafka’s delivery guarantee?

Kafka supports three delivery semantics:


• At most once: Messages may be lost but never redelivered.
• At least once: Messages are never lost but may be redelivered.
• Exactly once: Each message is processed exactly one time (requires idempotent producers and
Kafka Streams or transactions).

8. How does Kafka handle message retention?

Kafka retains messages for a configurable period (default 7 days) or until a specified size
limit is reached, even if they are consumed. This makes Kafka suitable for replaying data.

9. How does Kafka ensure high throughput?

Kafka achieves high throughput using:


• Disk-based storage with sequential writes
• Batching of messages
• Zero-copy transfer using sendfile()
• Partitioned parallel processing

10. What are Kafka producers and how do they work?

Kafka producers push data to topics. They can choose which partition to write to using:
• A key-based partitioner (same key goes to the same partition)
• A round-robin approach (default when no key is specified)

11. Explain Kafka’s ISR (In-Sync Replicas).

The ISR is the set of replicas that are fully caught up with the leader’s data. Kafka only
considers messages “committed” when they are written to all ISRs, ensuring durability.

12. What is Kafka Stream API vs Kafka Consumer API?

• Consumer API: Lets you manually consume and process Kafka messages.
• Streams API: High-level abstraction for building stream processing applications with features
like windowing, joins, and stateful operations.

13. What is Kafka Connect?

Kafka Connect is a framework for connecting Kafka with external systems like databases or file
systems using prebuilt connectors (e.g., JDBC, HDFS, Elasticsearch).
14. What happens when a Kafka broker fails?

• Leader election occurs for partitions hosted on that broker.


• Followers from the ISR list take over.
• Consumers automatically reconnect to new leaders via metadata refresh.

15. What is log compaction in Kafka?

Log compaction allows Kafka to retain only the latest value for each key. This is useful for
restoring state after a failure and for use cases like changelogs.

16. How would you scale Kafka for a high-throughput system?

• Increase the number of partitions for better parallelism


• Use multiple brokers to distribute load
• Use asynchronous producers with batching
• Tune producer and broker configurations (e.g., [Link], [Link], [Link])
• Monitor and scale consumer groups accordingly

17. Kafka is showing consumer lag. What do you do?

• Check consumer group health and instance count


• Check processing time or consumer errors
• Increase partition count and rebalance consumers
• Tune batch size or processing logicp
• Use Kafka monitoring tools (e.g., Kafka Manager, Confluent Control Center)

18. How do you ensure message ordering in Kafka?

Message ordering is guaranteed within a partition. To ensure ordering for related messages,
always produce them with the same key so they land in the same partition.

19. How do Kafka transactions work?

Kafka transactions allow atomic writes across multiple partitions and topics. A producer can
send messages as part of a transaction using the initTransactions() and commitTransaction()
APIs, ensuring exactly-once semantics.

20. What are some real-world use cases of Kafka?

• Event sourcing and audit logs


• Real-time analytics
• Log aggregation
• Stream processing (fraud detection, recommendations)
• Data pipeline ingestion into data lakes/warehouses

You might also like