Immutable Class in Java – Complete Guide with Examples

An Immutable Class in Java is a class whose instances (objects) cannot be modified after creation. Once an object of an immutable class is created, its state remains constant throughout the lifetime of the object. Immutable objects are widely used in multi-threaded environments because they are inherently thread-safe and don’t require synchronization.

✅ Why Use Immutable Class?

  • Thread-safety: Immutable objects can be safely shared between multiple threads without additional synchronization.
  • Caching and Performance: Immutable objects can be cached and reused, reducing the need to create new objects frequently.
  • Security: Since the object’s state can’t be changed, it prevents accidental or malicious modifications.

✅ Key Characteristics of Immutable Class

  1. Final Class: The class should be declared final to prevent subclassing which might override methods and break immutability.
  2. Private Final Fields: All fields should be declared private and final to prevent direct access and modification.
  3. No Setter Methods: Do not provide setters. Only provide getters to expose field values.
  4. Deep Copy in Constructor: When the class has mutable fields (like arrays or collections), perform deep copying in the constructor and in the getter methods to prevent external modification.

✅ Example of Immutable Class

import java.util.Date;

public final class Employee {

    private final int id;
    private final String name;
    private final Date joiningDate;

    // Constructor performs deep copy of mutable objects
    public Employee(int id, String name, Date joiningDate) {
        this.id = id;
        this.name = name;
        this.joiningDate = new Date(joiningDate.getTime()); // Defensive copy
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public Date getJoiningDate() {
        return new Date(joiningDate.getTime()); // Return copy to maintain immutability
    }

    @Override
    public String toString() {
        return "Employee { " +
                "id=" + id +
                ", name='" + name + '\'' +
                ", joiningDate=" + joiningDate +
                " }";
    }
}

✅ Usage Example

import java.util.Date;

public class Main {
    public static void main(String[] args) {
        Date date = new Date();
        Employee emp = new Employee(101, "John Doe", date);

        System.out.println(emp);

        // Attempt to modify original date object
        date.setTime(0);

        // The internal state of Employee remains unchanged
        System.out.println(emp);

        // Attempt to modify date retrieved via getter
        emp.getJoiningDate().setTime(0);

        // Employee’s joiningDate remains immutable
        System.out.println(emp);
    }
}

✅ Output Explanation

  • Even though we modify the original Date object and the one obtained from getJoiningDate(), the internal state of the Employee object remains unchanged.
  • This demonstrates how defensive copying preserves immutability.

✅ Best Practices for Immutable Class

  • Use primitive types and immutable objects (like String, Integer) for fields whenever possible.
  • For fields that are mutable objects (like Date, arrays, or collections), always use deep copy in constructors and getters.
  • Mark the class as final to prevent subclassing.
  • Avoid exposing mutable fields directly.

✅ When Should You Use Immutable Classes?

  • When thread-safety is crucial.
  • When object state should not change after creation.
  • For value objects such as coordinates, currency, or configurations.
  • To avoid defensive copying outside your control.

✅ Advantages and Disadvantages

AdvantagesDisadvantages
Thread-safe by designHigher memory footprint if many copies needed
Simple to reason about object statePerformance overhead due to object copying
Can be safely shared across threadsCannot be partially updated, needs recreation

✅ Conclusion

Immutable classes are a cornerstone of robust and thread-safe application design in Java. They offer simplicity, safety, and reliability in concurrent environments. However, they should be used judiciously where immutability is beneficial, and care must be taken with mutable fields.

Java developer with 9+ years of IT experience, sharing tutorials and tips to help learners master Java programming.

Leave a Reply

Your email address will not be published. Required fields are marked *