How to Use Immutable Class in Java: Syntax and Examples
To use an
immutable class in Java, declare the class as final, make all fields private final, initialize them via constructor only, and provide no setters. This ensures the object's state cannot change after creation, making it thread-safe and predictable.Syntax
An immutable class in Java typically has these parts:
final classto prevent subclassing.private finalfields so values cannot change.- A constructor to set all fields once.
- No setter methods to modify fields.
- Only getter methods to read field values.
java
public final class ImmutableExample { private final String name; private final int age; public ImmutableExample(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } }
Example
This example shows how to create and use an immutable class. Once created, the object's data cannot be changed.
java
public final class Person { private final String firstName; private final String lastName; public Person(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public static void main(String[] args) { Person person = new Person("Alice", "Smith"); System.out.println("First Name: " + person.getFirstName()); System.out.println("Last Name: " + person.getLastName()); // No setters available, so person data cannot be changed } }
Output
First Name: Alice
Last Name: Smith
Common Pitfalls
Common mistakes when creating immutable classes include:
- Not declaring fields as
final, allowing changes after construction. - Providing setter methods that modify fields.
- Allowing mutable objects (like arrays or lists) to be exposed directly through getters.
- Not making the class
final, so subclasses can alter behavior.
Always make defensive copies of mutable objects in constructor and getters.
java
public final class WrongImmutable { private StringBuilder data; // mutable field public WrongImmutable(StringBuilder data) { this.data = data; // no defensive copy } public StringBuilder getData() { return data; // exposes internal mutable object } } // Correct way: public final class CorrectImmutable { private final StringBuilder data; public CorrectImmutable(StringBuilder data) { this.data = new StringBuilder(data.toString()); // defensive copy } public StringBuilder getData() { return new StringBuilder(data.toString()); // return copy } }
Quick Reference
- Declare class as
final. - Make all fields
private final. - Initialize fields only in constructor.
- Do not provide setters.
- Return defensive copies for mutable fields.
Key Takeaways
Make the class and fields final to prevent changes after creation.
Initialize all fields in the constructor and avoid setters.
Use defensive copies for mutable objects to keep immutability.
Immutable classes are thread-safe and easier to reason about.
Avoid exposing internal mutable state through getters.