Skip to content

Java Immutable & Wrapper Classes

A class is considered immutable when its state cannot change after creation.

To achieve immutability, the class must follow these rules:

  1. Declare the class as final
    β†’ Prevents subclassing (otherwise a child class could modify behavior).
  2. Make all fields private and final
    β†’ Prevents external modification & ensures values never rebind.
  3. Do not provide setters
    β†’ State cannot be reassigned after object creation.
  4. Initialize fields only in constructor
    β†’ The object’s state is set once.
  5. Perform deep copy of mutable objects
    β†’ Prevents clients from modifying internal state by reference.
  6. Return deep copies (not original references) in getters
    β†’ Ensures internal fields remain protected.

2. Why must mutable fields be deeply copied in getters and constructors?

Section titled β€œ2. Why must mutable fields be deeply copied in getters and constructors?”

If you store or return mutable objects (e.g., List, Date, Map) by reference, callers can modify them, which breaks immutability.

this.list = new ArrayList<>(inputList); // Defensive copy in constructor
return new ArrayList<>(this.list); // Defensive copy in getter

Deep copying preserves the internal state, ensuring the object truly remains immutable.


3. Is final keyword enough to make a class immutable? Explain.

Section titled β€œ3. Is final keyword enough to make a class immutable? Explain.”

❌ No.
final only prevents reassignment of reference, not modification of the object itself.

Example:

final List<String> list = new ArrayList<>();
list.add("X"); // Allowed β†’ internal state changes

So, immutability requires both:

  • final reference fields and
  • No modification to underlying objects (via deep copy).

4. Why is immutability useful in multi-threading and caching?

Section titled β€œ4. Why is immutability useful in multi-threading and caching?”
BenefitExplanation
Thread-safetyImmutable objects cannot change, so no need for synchronization. Multiple threads can safely share the same instance.
Safe cachingCached objects cannot be tampered with β†’ predictable reads.
Memory efficiencyOne shared instance instead of making multiple copies.

This is why String, Integer, LocalDate, and BigDecimal are immutable.


5. Give an example where immutable objects improve system safety.

Section titled β€œ5. Give an example where immutable objects improve system safety.”

Example: Using String for database connection URLs, authentication tokens, etc.

If String were mutable:

  • Someone could modify password tokens in memory
  • HashMap keys would become inconsistent
  • Logging systems could leak confidential updates

Immutability prevents these critical risks.


1. What are wrapper classes in Java and why do we need them?

Section titled β€œ1. What are wrapper classes in Java and why do we need them?”

Wrapper classes are object representations of primitive types, e.g.:

PrimitiveWrapper
intInteger
doubleDouble
booleanBoolean

We need wrappers because:

  • Collections (e.g., List, Map) cannot store primitives
  • Frameworks (Spring, Hibernate, JSON) rely on objects, not primitives
  • Wrappers provide useful utility methods and constants

2. Explain autoboxing and unboxing. What performance issues can arise?

Section titled β€œ2. Explain autoboxing and unboxing. What performance issues can arise?”
  • Autoboxing: Converting primitive β†’ wrapper automatically
    Example: Integer x = 10;
  • Unboxing: Converting wrapper β†’ primitive automatically
    Example: int y = x;

Performance concerns:

  • Autoboxing creates new wrapper objects, increasing heap usage.
  • Excessive boxing/unboxing in loops can cause GC overhead and performance drops.

3. How does Integer caching work and what values are cached?

Section titled β€œ3. How does Integer caching work and what values are cached?”

Java maintains a cache for Integer values from -128 to +127.

Integer a = 100;
Integer b = 100;
a == b // true (same cached object)

Values outside this range are new objects:

Integer x = 200;
Integer y = 200;
x == y // false (different objects)

This improves memory usage and speeds up frequently-used small integers.


4. What is the result of comparing two Integers with == vs equals()? Explain memory impact.

Section titled β€œ4. What is the result of comparing two Integers with == vs equals()? Explain memory impact.”
ComparisonBehaviorExampleResult
==Compares object referencesInteger a = 128; Integer b = 128; a == bfalse (different objects)
.equals()Compares valuesa.equals(b)true

Key point:

  • For values between -128 to +127, == may be true because cached object is reused.
  • For values outside this range, == is false because new objects are created.