Skip to main content

Java & Spring Interview Questions [Most Asked]

This compilation covers critical interview topics across Core Java, Collections, Hibernate, and the Spring Framework with senior-level depth.

1. ArrayList vs. LinkedList

FeatureArrayListLinkedList
Internal Data StructureResizable Array (contiguous memory)Doubly Linked List (nodes on heap)
Random AccessO(1) โ€” direct indexO(n) โ€” traversal from head/tail
Add/Remove at endO(1) amortizedO(1)
Add/Remove in middleO(n) โ€” System.arraycopy() shiftO(n) traversal + O(1) pointer update
Memory per element~4-8 bytes~40 bytes (Node overhead)
Cache FriendlinessExcellent (contiguous)Poor (scattered on heap)
Best Use CaseDefault choice for most scenariosQueue/Deque operations, frequent iterator removal

Key insight for interviews: Prefer ArrayDeque over LinkedList for stack/queue operations โ€” it's faster due to contiguous memory layout and has no Node allocation overhead.

2. Lazy Loading in Hibernate

Lazy loading postpones the initialization of associated entities until they are explicitly accessed, reducing unnecessary database queries.

How it works internally

Hibernate creates a proxy object (using CGLIB or ByteBuddy bytecode generation) that extends your entity class. When you access a lazily-loaded field, the proxy intercepts the call and fires the SQL query at that point.

@Entity
public class Employee {
@OneToMany(fetch = FetchType.LAZY) // Default for collections
private List<Address> addresses; // Proxy, not loaded yet
}

Employee emp = session.get(Employee.class, 1L); // SQL: SELECT * FROM employee WHERE id=1
// emp.addresses is a PersistentBag proxy โ€” NO SQL yet

emp.getAddresses().size(); // NOW fires SQL: SELECT * FROM address WHERE employee_id=1

The N+1 Problem

List<Employee> emps = session.createQuery("FROM Employee", Employee.class).list();
// 1 query: SELECT * FROM employee (returns 100 employees)

for (Employee emp : emps) {
emp.getAddresses().size(); // 100 MORE queries! One per employee
}
// Total: 1 + 100 = 101 queries (the "N+1 problem")

Solutions:

  1. JOIN FETCH (JPQL): "FROM Employee e JOIN FETCH e.addresses" โ€” single query with a JOIN
  2. @EntityGraph (JPA 2.1): Declarative fetch plan
  3. @BatchSize(size=20): Loads 20 address collections per query โ†’ 1 + 5 = 6 queries for 100 employees

LazyInitializationException

If you access a lazy field after the Hibernate Session is closed, you get LazyInitializationException. Common in web apps where the Session closes after the Service layer but the Controller/View tries to access lazy data.

Solutions: Open Session in View (anti-pattern), DTO projection, or fetch eagerly for known access patterns.

3. Hibernate Caching: First Level vs. Second Level

FeatureFirst Level CacheSecond Level Cache
ScopeSingle Session (EntityManager)SessionFactory (application-wide)
EnabledAlways (cannot disable)Must be explicitly configured
StorageIdentity Map: Map<EntityKey, Entity>Dehydrated state (serialized form, not entity objects)
EvictionWhen Session is closed/clearedTTL-based, size-based, or manual
Object IdentitySame Session โ†’ same object reference (==)Different Sessions โ†’ different objects (equals() true)
ProviderBuilt into HibernateExternal: EhCache, Hazelcast, Infinispan, Redis

Query Cache (often overlooked)

The entity cache only works for session.get(id) / session.find(id). For HQL/JPQL queries, you need the Query Cache โ€” it stores the list of matching primary keys, not the entities themselves. The entity cache is then used to resolve each key to an entity.

// Enable Query Cache for specific queries
List<Product> products = session.createQuery("FROM Product WHERE active = true")
.setCacheable(true)
.list();

4. JVM Garbage Collection Generations

JVM Heap memory is divided into generations based on object lifetime, enabling optimized GC strategies:

Memory SpaceAllocation ProportionSub-Areas & Internal LayoutPurpose & GC Behavior
Young Generation~1/3 of HeapEden Space (new object allocations)
Survivor S0 / S1 (from/to spaces)
Minor GC executes frequently (10โ€“50ms); live objects alternate between S0/S1 until tenure age threshold (default 15).
Old (Tenured) Generation~2/3 of HeapLong-lived objects promoted from Young Gen, direct large arraysMajor / Concurrent Mark Sweep / G1 mixed collections. Slower compaction.
Metaspace (Native RAM)OS Virtual Memory (Off-Heap)Loaded class metadata, method bytecode, constant pool, annotationsReplaced PermGen in Java 8; auto-expands until host RAM limits unless bounded by -XX:MaxMetaspaceSize.

GC Types

GC EventWhat happensImpact
Minor GCCollects Young Gen onlyFast (10-50ms), frequent
Major GCCollects Old GenSlower (100ms-1s), less frequent
Full GCCollects entire heap + MetaspaceStop-the-world, very slow

Object Lifecycle

  1. New object โ†’ Eden space
  2. Survives Minor GC โ†’ moves to Survivor S0 (age = 1)
  3. Survives another Minor GC โ†’ moves to Survivor S1 (age = 2)
  4. Alternates between S0/S1 for several GC cycles
  5. Age reaches threshold (default 15) โ†’ promoted to Old Gen
  6. Large objects may go directly to Old Gen (avoids copying overhead)

GC Algorithms (Java 17+)

AlgorithmPause TargetBest For
G1 GC (default since Java 9)~200msGeneral purpose, balanced throughput/latency
ZGC< 1msUltra-low latency (trading databases, real-time)
Shenandoah< 10msLow latency (similar to ZGC, available in OpenJDK)
Parallel GCMaximize throughputBatch processing, offline computation

5. What is Serialization?

Serialization is the process of converting an object's state into a byte stream, so it can be saved to a file, sent over a network, or stored in a database. Deserialization is the reverse process.

// Serialize
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("data.ser"))) {
oos.writeObject(employee);
}

// Deserialize
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("data.ser"))) {
Employee emp = (Employee) ois.readObject();
}

Key details

  • Class must implement java.io.Serializable (marker interface)
  • serialVersionUID โ€” version control for serialized classes. If you change the class structure without updating this ID, deserialization throws InvalidClassException
  • transient keyword โ€” marks fields that should NOT be serialized (passwords, connections, caches)
  • static fields are NOT serialized (they belong to the class, not the instance)

Modern alternatives

Java serialization has known security vulnerabilities (deserialization attacks) and performance issues. Modern alternatives:

  • JSON: Jackson, Gson (human-readable, widely supported)
  • Protocol Buffers: Google's binary format (compact, fast, schema-driven)
  • Avro: Apache's format (schema evolution, used in Kafka)

6. Spring IoC and Dependency Injection (DI)

Inversion of Control (IoC)

A principle where the control of object creation and lifecycle is transferred from the developer to the Spring Container (ApplicationContext). Instead of new Service(), the container creates, configures, and manages the object.

Dependency Injection (DI)

The mechanism to implement IoC. Instead of an object creating its dependencies, the container injects them:

// WITHOUT DI โ€” tight coupling
class OrderService {
private PaymentGateway gateway = new StripeGateway(); // Hard-coded dependency
}

// WITH DI โ€” loose coupling
@Service
class OrderService {
private final PaymentGateway gateway; // Interface, not implementation

@Autowired // Constructor injection (recommended)
OrderService(PaymentGateway gateway) {
this.gateway = gateway;
}
}

Injection Types (ranked by recommendation)

TypeProsCons
Constructor (recommended)Immutable (final fields), testable, fails fastVerbose for many dependencies
SetterOptional dependencies, reconfigurableMutable, easy to forget
Field (@Autowired on field)ConciseUntestable without reflection, hides dependencies

Bean Lifecycle

Constructor โ†’ @PostConstruct โ†’ afterPropertiesSet() โ†’ Custom init โ†’
Ready โ†’
@PreDestroy โ†’ destroy() โ†’ Custom destroy

7. What is WeakHashMap?

A WeakHashMap stores keys as WeakReferences. When a key has no more strong references anywhere in the program, the GC can reclaim it and the entry is automatically removed from the map.

Use case: Metadata/annotation caches where the cache entry should live only as long as the key object is in use. Example: caching computed properties of objects without preventing those objects from being garbage collected.

WeakHashMap<ClassLoader, Map<String, Class<?>>> classCache = new WeakHashMap<>();
// When a ClassLoader is unloaded, its cached classes are automatically cleaned up

Caveat: String literal keys are never garbage collected (they live in the String Pool). Use new String("key") for testing, but be aware this defeats the purpose in production.

8. Functional Interfaces (Java 8)

A Functional Interface has exactly one abstract method (SAM โ€” Single Abstract Method). It can have any number of default or static methods.

Built-in Functional Interfaces

InterfaceSignatureExample
Predicate<T>T โ†’ booleanfilter(), validation
Function<T,R>T โ†’ Rmap(), transformation
Consumer<T>T โ†’ voidforEach(), logging
Supplier<T>() โ†’ TFactory methods, lazy init
UnaryOperator<T>T โ†’ TreplaceAll()
BinaryOperator<T>(T, T) โ†’ Treduce()
BiFunction<T,U,R>(T, U) โ†’ Rmerge(), replaceAll()

Lambda Implementation (Under the Hood)

Lambdas are not compiled to anonymous inner classes. They use invokedynamic bytecode + LambdaMetafactory:

  1. First call: JVM generates a class at runtime (no .class file on disk)
  2. Subsequent calls: reuse the generated class (cached)
  3. Benefit: No .class file explosion, potential for JIT optimization, less memory overhead

9. ConcurrentHashMap vs. SynchronizedMap

FeatureSynchronizedMapConcurrentHashMap
LockingSingle mutex on entire mapCAS + per-node synchronized (Java 8+)
Read concurrencyBlocked (readers wait for writers)Lock-free (volatile reads)
Write concurrencyOne writer at a timeMultiple writers to different buckets
Null keys/valuesAllowedNot allowed
Atomic operationsputIfAbsent, computeIfAbsent NOT atomicputIfAbsent, computeIfAbsent ARE atomic
IteratorFail-fastWeakly consistent
ScalabilityPoor (serializes all access)Excellent

When to use SynchronizedMap: Only when you need null keys/values in a thread-safe map (very rare). For everything else, use ConcurrentHashMap.


๐Ÿ“–
Track Page Progress0 / 635 Read
Knowledge Base Completion0%