Last Updated : 30 May, 2025
In Java, immutability means that once an object is created, its internal state cannot be changed. Immutable classes in Java provide many advantages like thread safety, easy debugging and all. In Java, all the wrapper classes (like Integer, Boolean, Byte, Short) and the String class is immutable. We can create our own immutable class as well.
In this article, we are going to learn:
An immutable class is a class whose objects cannot be changed once created. If we do any modification, it results in a new object. This method is used in concurrent applications.
Rules for Creating an Immutable ClassNote: There should be no setters or in simpler terms, there should be no option to change the value of the instance variable.
Example: Immutable class implementation
Student.java
Java
// Java Program to Create An Immutable Class
import java.util.HashMap;
import java.util.Map;
// declare the class as final
final class Student {
// make fields private and final
private final String name;
private final int regNo;
private final Map<String, String> metadata;
// initialize all fields via constructor
public Student(String name, int regNo, Map<String, String> metadata) {
this.name = name;
this.regNo = regNo;
// deep copy of mutable object (Map)
Map<String, String> tempMap = new HashMap<>();
for (Map.Entry<String, String> entry : metadata.entrySet()) {
tempMap.put(entry.getKey(), entry.getValue());
}
this.metadata = tempMap;
}
// only provide getters (no setters)
public String getName() {
return name;
}
public int getRegNo() {
return regNo;
}
// return deep copy to avoid exposing internal state
public Map<String, String> getMetadata() {
Map<String, String> tempMap = new HashMap<>();
for (Map.Entry<String, String> entry : this.metadata.entrySet()) {
tempMap.put(entry.getKey(), entry.getValue());
}
return tempMap;
}
}
In this example, we have created a final class named Student. It has three final data members, a parameterized constructor, and getter methods. Please note that there is no setter method here. Also, note that we don't need to perform deep copy or cloning of data members of wrapper types as they are already immutable.
Geeks.java:
Java
import java.util.HashMap;
import java.util.Map;
public class Geeks {
public static void main(String[] args) {
// create a map and adding data
Map<String, String> map = new HashMap<>();
map.put("1", "first");
map.put("2", "second");
// create an immutable Student object
Student s = new Student("GFG", 101, map);
// accessing data
System.out.println(s.getName());
System.out.println(s.getRegNo());
System.out.println(s.getMetadata());
// try to modify the original map
map.put("3", "third");
System.out.println(s.getMetadata());
// try to modify the map returned by getMetadata()
s.getMetadata().put("4", "fourth");
System.out.println(s.getMetadata());
}
}
Even after modifying the original or returned Map, the internal state of the Student object remains unchanged. This confirms the immutability concept.
Output:
GFG
101
{1=first, 2=second}
{1=first, 2=second}
{1=first, 2=second}
Java 14 introduced record. This is a clear and concise way to define immutable like classes:
record Student(String name, int regNo, Map<String, String> metadata) {}
But this only offers shallow immutability. If the Map is modified externally, the internal state of the record changes:
Map<String, String> map = new HashMap<>();
map.put("1", "first");
Student s = new Student("ABC", 101, map);
// Changes internal state — NOT safe
map.put("2", "second");
s.metadata().put("3", "third");
Note: Use record only if all fields are immutable types like String, int, or other records.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4