In Java, an Immutable Class is a class whose objects cannot be modified after they are created. Once an instance of an immutable class is created, it represents a fixed state that cannot be altered. Immutable objects provide several advantages, such as thread safety, simplicity in programming, and fewer errors due to unintended changes.
Characteristics of an Immutable Class:
No setter methods – No methods to modify fields.
Private and final fields – The data members (fields) should be private and marked final to prevent modification after construction.
Initialization via constructor only – All fields are initialized through the constructor at the time of object creation.
Deep copy of mutable fields – If the class contains fields that are references to mutable objects (like arrays or collections), a deep copy should be made to avoid external modification.
No external modification of mutable objects – Ensure that any returned references to mutable objects are copies, not the actual objects.
Example of an Immutable Class in Java:
final class ImmutableClass {
private final String name;
private final int age;
private final List<String> friends;
// Constructor to initialize the fields
public ImmutableClass(String name, int age, List<String> friends) {
this.name = name;
this.age = age;
// Creating a deep copy of the mutable list
this.friends = new ArrayList<>(friends);
}
// Getter methods, but no setters
public String getName() {
return name;
}
public int getAge() {
return age;
}
// Returning a copy of the list to prevent modification
public List<String> getFriends() {
return new ArrayList<>(friends);
}
}
In this example, the ImmutableClass is immutable because:
All fields are private and final.
No setters are present.
A deep copy of the mutable List is created to prevent modification of the original list.
What is an Immutable Class?
Follow-up: Why should we use immutable objects?
Answer: An immutable class is one whose state cannot be modified after creation. It provides simplicity, thread-safety, and prevents accidental modification.
How do you create an immutable class in Java?
Follow-up: What happens if one of the fields is a mutable object?
Answer: You ensure the fields are private, final, initialized in the constructor, and that no setters are provided. If the field is mutable (e.g., a collection or array), return a deep copy instead of the original reference.
Can you make a class immutable without declaring it as final?
Follow-up: Why do we often declare immutable classes as final?
Answer: Yes, but marking the class final ensures that it cannot be subclassed. This prevents any potential subclass from violating immutability by adding setter methods or mutable fields.
Is String class immutable? How is immutability achieved in String?
Follow-up: What are the benefits of String being immutable in Java?
Answer: Yes, String is immutable. Once created, the value of a String cannot be changed. Its immutability is achieved through private and final character array fields and the absence of methods that modify the value. The benefits include thread-safety, security (e.g., when dealing with sensitive data), and performance optimizations like String interning.
How does immutability help in multi-threaded environments?
Follow-up: Are there any drawbacks to using immutable objects in a multi-threaded application?
Answer: Immutable objects are inherently thread-safe because their state cannot be changed once created, eliminating synchronization issues. However, creating many immutable objects can lead to performance concerns due to frequent object creation.
Can immutability cause memory overhead?
Follow-up: How can we mitigate this memory overhead?
Answer: Yes, since every change requires the creation of a new object, there can be more memory overhead compared to mutable objects. This can be mitigated using techniques like object pooling (e.g., String interning) or by reducing unnecessary object creation.
What is the difference between a shallow copy and a deep copy in an immutable class?
Follow-up: Why do we prefer deep copy for mutable fields in immutable classes?
Answer: A shallow copy copies only the reference to an object, while a deep copy duplicates the entire object structure. In immutable classes, deep copies prevent the original mutable objects from being altered outside of the class, ensuring true immutability.
Why do we need to be careful with returning internal mutable objects in an immutable class?
Follow-up: How would you handle mutable fields in an immutable class?
Answer: If we return the reference to a mutable object, external code can modify it, breaking immutability. To handle this, we return a deep copy of the mutable field instead of the original reference.
Can you modify an immutable object using reflection?
Follow-up: Is it possible to protect an immutable class from modification using reflection?
Answer: Yes, reflection can technically modify even final fields, though this is not recommended as it breaks the contract of immutability. To mitigate this, developers can restrict access to reflection or disable it via security policies.
How would you test whether a class is truly immutable?
Follow-up: What kind of unit tests would you write to ensure immutability?
Answer: Tests should ensure that no setter methods exist, that fields are private and final, and that no changes to internal fields affect the object’s state. Unit tests would attempt to modify the class through various means (e.g., trying to change a returned reference to a mutable object) and verify that the state remains unchanged.
Design patterns: Interviewers might ask how immutability fits into certain design patterns (e.g., Builder pattern for constructing immutable objects).
Optimization techniques: Be prepared to discuss how to balance immutability with performance (e.g., avoiding frequent object creation through caching or flyweight patterns).
Security implications: Highlight cases where immutability improves security, such as in multi-threaded environments or cryptography.