Introduction :
In the world of Java, Strings play a fundamental and important role. Whether we are writing a simple “Hello, World!” program or building complex enterprise applications or working with web services, Strings are everywhere.
Strings are immutable which means once we create a string object, we can not perform any changes in the existing object. If we are trying to perform any changes then with those changes a new object will be created. This non-changable behaviour is nothing but immutability of string.
Rules for Creating an Immutable Class in Java :
In order to create an immutable class in java, we must to follow the golden rules as given below :
1) Declare your class as final – So that no other classes can extend it.
(2) Declare all fields private and final – It ensures that values cannot be re-assigned.
(3) Initialize fields via constructor only.
(4) Do not provide setters methods.
(5) If our classes holds a mutable object : We must return the deep copies of mutable Object.
(6) Ensure immutability in getters methods – It is mandatory that never expose original mutable objects.
In this way, we can create the class as immutable. Through one example we can understand this in better way.
Example of an Immutable Class :
Approach 1 :
package com.javatechzone.model;
import java.math.BigDecimal;
import java.util.Date;
public final class Employee {
private final Integer id;
private final String name;
private final BigDecimal salary;
private final Date dob;
private final Address address;
public Employee(Integer id, String name, BigDecimal salary, Date dob, Address address) {
this.id = id;
this.name = name;
this.salary = salary;
this.dob = dob;
this.address = address;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public BigDecimal getSalary() {
return salary;
}
public Date getDob() {
return new Date(dob.getTime());
//return dob;
}
public Address getAddress() {
Address address = new Address();
address.setAddress1(address.getAddress1());
address.setAddress2(address.getAddress2());
address.setCity(address.getCity());
return address;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", salary=" + salary + ", dob=" + dob + ", address=" + address
+ "]";
}
}
package com.javatechzone.client;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.javatechzone.model.Address;
import com.javatechzone.model.Employee;
public class ClientTest {
public static void main(String[] args) {
Address address1 = new Address();
address1.setAddress1("ADDRESS1");
address1.setAddress2("ADDRESS2");
address1.setCity("Mumbai");
Address address2 = new Address();
address2.setAddress1("ADDRESS3");
address2.setAddress2("ADDRESS4");
address2.setCity("Chennai");
Employee e1 = new Employee(1001, "javatechzone", new BigDecimal("8000.00"), getDob("04/02/1986"), address1);
Employee e2 = new Employee(1002, "javatechzone1", new BigDecimal("6000.00"), getDob("25/02/1985"), address2);
System.out.println(e1);
System.out.println(e2);
System.out.println("-----------------------------------------------------");
e1.getAddress().setCity("Chennai");
System.out.println(e1);
}
private static Date getDob(String dob) {
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
try {
return dateFormat.parse(dob);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
package com.javatechzone.model;
public class Address {
private String address1;
private String address2;
private String city;
public String getAddress1() {
return address1;
}
public void setAddress1(String address1) {
this.address1 = address1;
}
public String getAddress2() {
return address2;
}
public void setAddress2(String address2) {
this.address2 = address2;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "Address [address1=" + address1 + ", address2=" + address2 + ", city=" + city + "]";
}
}
Approach 2 :
package com.javatechzone.model;
public final class Student {
private final int id;
private final String name;
private final StudentAddress address;
public Student(int id, String name, StudentAddress address) throws CloneNotSupportedException {
this.id = id;
this.name = name;
this.address = (StudentAddress) address.clone();
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public StudentAddress getAddress() throws CloneNotSupportedException {
return (StudentAddress) address.clone();
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", address=" + address + "]";
}
}
package com.javatechzone.model;
public class StudentAddress implements Cloneable{
private int id;
private String address;
public StudentAddress(int id, String address) {
this.id = id;
this.address = address;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "AddressNew [id=" + id + ", address=" + address + "]";
}
}
package com.javatechzone.client;
import com.javatechzone.model.Student;
import com.javatechzone.model.StudentAddress;
public class StudentTest {
public static void main(String[] args) throws CloneNotSupportedException {
final StudentAddress address = new StudentAddress(1, "Mumbai");
final Student student = new Student(101, "John", address);
//Before changes to object
System.out.println("Before changes to object ---- "+student.toString());
//Change the address Object
address.setAddress("Chennai");
System.out.println("After changes to object ---- "+student.toString());
}
}