Blog
Immutable Class in Java
Immutable Class in Java
Immutable classes in Java are classes whose objects cannot be modified after they are created. Once an immutable object is initialized, its state remains unchanged for the entire lifetime of that object.
Immutability is an important concept in Java programming because it helps create thread-safe, predictable, and easier-to-maintain applications. The most common example of an immutable class in Java is the String class.
What is an Immutable Class in Java?
An immutable class is a Java class in which the state of an object cannot be changed after the object has been created.
For example, consider the following code:
String name = "Java";
name = name.concat(" Programming");
System.out.println(name);
The original "Java" object is not modified. Instead, concat() creates another String object and the variable name references the new object.
Characteristics of an Immutable Class
A well-designed immutable class generally follows these important rules:
- Declare the class as
finalso it cannot be subclassed. - Declare all instance variables as
private. - Declare fields as
finalso they can be assigned only once. - Initialize all fields through the constructor.
- Do not provide setter methods that modify the object’s state.
- Return defensive copies of mutable objects when necessary.
- Do not expose internal mutable objects directly.
How to Create an Immutable Class in Java
Let’s create a simple immutable Employee class.
public final class Employee {
private final int id;
private final String name;
private final String department;
public Employee(int id, String name, String department) {
this.id = id;
this.name = name;
this.department = department;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getDepartment() {
return department;
}
}
In this example, the class is final, all fields are private final, and there are no setter methods. Therefore, an Employee object cannot be modified after creation.
Example of Using an Immutable Object
public class Main {
public static void main(String[] args) {
Employee employee =
new Employee(101, "John", "Testing");
System.out.println(employee.getId());
System.out.println(employee.getName());
System.out.println(employee.getDepartment());
}
}
After the Employee object is created, its id, name, and department cannot be changed.
Why is String Immutable in Java?
The String class is one of the most important examples of immutability in Java.
String str = "Hello";
str = str.toUpperCase();
System.out.println(str);
The operation does not modify the original String object. A new String object is created instead.
String immutability provides several benefits, including:
- Security: Strings are widely used for usernames, passwords, file paths, URLs, class names, and other sensitive values.
- Thread Safety: The same String object can safely be shared between multiple threads.
- String Pooling: Immutable Strings can be safely shared in the String pool.
- Hashing: A String’s value does not change after it is used as a key in a hash-based collection.
Immutable Class with a Mutable Field
Special care is required when an immutable class contains mutable objects such as Date, List, Map, arrays, or custom mutable classes.
Consider this example:
public final class Student {
private final String name;
private final java.util.Date dateOfBirth;
public Student(String name, java.util.Date dateOfBirth) {
this.name = name;
this.dateOfBirth = dateOfBirth;
}
public String getName() {
return name;
}
public java.util.Date getDateOfBirth() {
return dateOfBirth;
}
}
This class may appear immutable, but it is not fully immutable because Date is mutable.
For example:
java.util.Date date = new java.util.Date();
Student student = new Student("Alice", date);
date.setTime(0);
The internal state of the Student object can potentially be affected because the original mutable Date object is shared.
Using Defensive Copies
To maintain immutability, create a defensive copy when accepting or returning mutable objects.
public final class Student {
private final String name;
private final java.util.Date dateOfBirth;
public Student(String name, java.util.Date dateOfBirth) {
this.name = name;
this.dateOfBirth = new java.util.Date(dateOfBirth.getTime());
}
public String getName() {
return name;
}
public java.util.Date getDateOfBirth() {
return new java.util.Date(dateOfBirth.getTime());
}
}
Here, the constructor stores a copy of the supplied Date, and the getter returns another copy. Therefore, external code cannot directly modify the internal state.
Immutable Class with a List
Collections require similar care because a List can normally be modified.
import java.util.List;
public final class Course {
private final String courseName;
private final List<String> topics;
public Course(String courseName, List<String> topics) {
this.courseName = courseName;
this.topics = List.copyOf(topics);
}
public String getCourseName() {
return courseName;
}
public List<String> getTopics() {
return topics;
}
}
List.copyOf() creates an unmodifiable copy of the supplied list, preventing callers from modifying the stored collection through the returned reference.
Immutable Class vs Mutable Class
| Feature | Immutable Class | Mutable Class |
|---|---|---|
| Object state | Cannot change after creation | Can change after creation |
| Setter methods | Generally not provided | Commonly provided |
| Thread safety | Easier to achieve | May require synchronization |
| Design | State is fixed | State can be updated |
| Examples | String, wrapper classes |
StringBuilder, ArrayList |
Advantages of Immutable Classes in Java
1. Thread Safety
Immutable objects cannot be modified, so multiple threads can safely share them without worrying about one thread changing the object’s state while another thread is using it.
2. Simpler Code
Because the state never changes, immutable objects are easier to reason about, test, debug, and maintain.
3. Improved Security
Immutable objects are useful when values must not be changed unexpectedly. This is especially important for configuration and security-related data.
4. Safe Hash-Based Collections
Immutable objects make excellent keys in HashMap and elements in HashSet because their state does not change after insertion.
Map<String, String> map = new HashMap<>();
map.put("language", "Java");
If the key’s state changes after insertion, hash-based collections can behave incorrectly. Immutable objects help avoid this problem.
5. Easier Caching
Since an immutable object’s value cannot change, it can safely be cached and reused without worrying about external modifications.
Disadvantages of Immutable Classes
Immutability also has some trade-offs.
- Creating a new object is required whenever a different state is needed.
- Large numbers of temporary objects can sometimes increase memory usage.
- Designing immutable classes containing mutable objects requires additional care.
- Repeated updates may be less efficient compared with purpose-built mutable classes.
Immutable Class and Thread Safety
One of the major reasons developers use immutable objects is to simplify concurrent programming.
public final class Configuration {
private final String environment;
private final int port;
public Configuration(String environment, int port) {
this.environment = environment;
this.port = port;
}
public String getEnvironment() {
return environment;
}
public int getPort() {
return port;
}
}
The same Configuration object can be safely shared between threads because its fields cannot be changed after construction.
Can an Immutable Class Have Methods?
Yes. An immutable class can have methods as long as those methods do not modify the object’s internal state.
public final class Rectangle {
private final int width;
private final int height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
public int getArea() {
return width * height;
}
}
The getArea() method calculates and returns a value without changing the object’s state.
Immutable Objects in Java
Several commonly used Java types are immutable, including:
StringIntegerLongDoubleBooleanCharacter- Many classes from the modern
java.timeAPI, such asLocalDateandLocalDateTime
Best Practices for Creating Immutable Classes
Follow these best practices when designing an immutable Java class:
- Use a
finalclass whenever subclassing should not be allowed. - Keep fields
private final. - Initialize all required state in the constructor.
- Avoid setters.
- Use immutable types whenever possible.
- Use defensive copies for mutable inputs and outputs.
- For collections, consider
List.copyOf(),Set.copyOf(), orMap.copyOf(). - Ensure referenced custom objects are also immutable when complete immutability is required.
Immutable Class vs Final Class
A final class and an immutable class are not the same thing.
The final keyword prevents a class from being extended, but it does not automatically make the object’s state immutable.
public final class Person {
private String name;
public Person(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
}
Although Person is declared final, its state can still be changed through setName(). Therefore, it is not immutable.
Key Takeaways
Immutable classes in Java provide objects whose state remains unchanged after construction. The basic design principles are private final fields, constructor initialization, no state-changing methods, and defensive handling of mutable objects.
Immutability is especially valuable in multithreaded Java applications, caching, collections, security-sensitive code, functional-style programming, and large enterprise applications. Understanding immutable classes is an essential part of becoming a strong Java developer.
Frequently Asked Questions About Immutable Classes in Java
What is an immutable class in Java?
An immutable class is a class whose object state cannot be changed after the object is created.
Is String immutable in Java?
Yes. String is an immutable class in Java. String operations that appear to modify a value actually produce a new String object.
Why should fields in an immutable class be final?
The final modifier ensures that a field can be assigned only once, helping prevent the object’s state from being changed after construction.
Can an immutable class contain a mutable object?
Yes, but the class must prevent external code from modifying that mutable object. Defensive copies or immutable replacements should be used where necessary.
Are immutable objects thread-safe?
Immutable objects are inherently safe to share once safely constructed because their observable state does not change.
Conclusion
Immutable classes in Java are a fundamental object-oriented programming concept that leads to safer and more predictable software. By making object state unchangeable, Java developers can reduce side effects, simplify concurrent programming, improve reliability, and make applications easier to maintain.
For Java developers and software engineers, mastering immutable objects, defensive copying, final fields, and thread-safe design is an important step toward writing robust and scalable Java applications.