Автор оригинала: Pankaj Kumar.
Сегодня мы узнаем о неизменяемом классе в Java. Что такое неизменяемые классы? Преимущества и важность глубокого копирования для неизменности.
Что такое неизменяемый класс в Java?
Неизменяемые объекты – это экземпляры, состояние которых не меняется после инициализации. Например, String является неизменяемым классом, и после создания экземпляра его значение никогда не меняется.
Прочитайте : Почему строка в неизменяемом в Java
Преимущества неизменяемого класса в Java
Неизменяемый класс хорош для целей кэширования, потому что вам не нужно беспокоиться об изменениях значений.
Еще одним преимуществом неизменяемого класса является то , что он по своей сути потокобезопасен, поэтому вам не нужно беспокоиться о безопасности потоков в случае многопоточной среды.
Прочитайте : Учебник по потокам Java и Вопросы для многопоточного интервью Java .
Здесь я предлагаю способ создания неизменяемого класса на примере для лучшего понимания.
Как создать неизменяемый класс в Java?
Чтобы создать неизменяемый класс в Java, вам необходимо выполнить следующие действия.
- Объявите класс окончательным, чтобы его нельзя было расширить.
- Сделайте все поля закрытыми, чтобы прямой доступ был запрещен.
- Не предоставляйте методы настройки для переменных.
- Сделайте все изменяемые поля окончательными , чтобы его значение можно было присвоить только один раз.
- Инициализируйте все поля с помощью конструктора , выполнив глубокое копирование.
- Выполните клонирование объектов в методах получения, чтобы вернуть копию, а не фактическую ссылку на объект.
Чтобы понять пункты 4 и 5, давайте запустим пример конечного класса, который хорошо работает, и значения не изменяются после создания экземпляра.
Чтобы понять пункты 4 и 5, давайте запустим пример конечного класса, который хорошо работает, и значения не изменяются после создания экземпляра.
package com.journaldev.java;
import java.util.HashMap;
import java.util.Iterator;
public final class FinalClassExample {
private final int id;
private final String name;
private final HashMap testMap;
public int getId() {
return id;
}
public String getName() {
return name;
}
/**
* Accessor function for mutable objects
*/
public HashMap getTestMap() {
//return testMap;
return (HashMap) testMap.clone();
}
/**
* Constructor performing Deep Copy
* @param i
* @param n
* @param hm
*/
public FinalClassExample(int i, String n, HashMap hm){
System.out.println("Performing Deep Copy for Object initialization");
this.id=i;
this.name=n;
HashMap tempMap=new HashMap();
String key;
Iterator it = hm.keySet().iterator();
while(it.hasNext()){
key=it.next();
tempMap.put(key, hm.get(key));
}
this.testMap=tempMap;
}
/**
* Constructor performing Shallow Copy
* @param i
* @param n
* @param hm
*/
/**
public FinalClassExample(int i, String n, HashMap hm){
System.out.println("Performing Shallow Copy for Object initialization");
this.id=i;
this.name=n;
this.testMap=hm;
}
*/
/**
* To test the consequences of Shallow Copy and how to avoid it with Deep Copy for creating immutable classes
* @param args
*/
public static void main(String[] args) {
HashMap h1 = new HashMap();
h1.put("1", "first");
h1.put("2", "second");
String s = "original";
int i=10;
FinalClassExample ce = new FinalClassExample(i,s,h1);
//Lets see whether its copy by field or reference
System.out.println(s==ce.getName());
System.out.println(h1 == ce.getTestMap());
//print the ce values
System.out.println("ce id:"+ce.getId());
System.out.println("ce name:"+ce.getName());
System.out.println("ce testMap:"+ce.getTestMap());
//change the local variable values
i=20;
s="modified";
h1.put("3", "third");
//print the values again
System.out.println("ce id after local variable change:"+ce.getId());
System.out.println("ce name after local variable change:"+ce.getName());
System.out.println("ce testMap after local variable change:"+ce.getTestMap());
HashMap hmTest = ce.getTestMap();
hmTest.put("4", "new");
System.out.println("ce testMap after changing variable from accessor methods:"+ce.getTestMap());
}
}
Результатом приведенного выше примера программы является:
Performing Deep Copy for Object initialization
true
false
ce id:10
ce name:original
ce testMap:{2=second, 1=first}
ce id after local variable change:10
ce name after local variable change:original
ce testMap after local variable change:{2=second, 1=first}
ce testMap after changing variable from accessor methods:{2=second, 1=first}
Почему глубокое копирование важно для неизменности?
Давайте прокомментируем конструктор, предоставляющий глубокую копию, и раскомментируем конструктор, предоставляющий неглубокую копию.
Кроме того, раскомментируйте оператор return в методе getTestMap () , который возвращает фактическую ссылку на объект.
Запустите программу после того, как все изменения будут сделаны. Это приведет к следующему результату.
Performing Shallow Copy for Object initialization
true
true
ce id:10
ce name:original
ce testMap:{2=second, 1=first}
ce id after local variable change:10
ce name after local variable change:original
ce testMap after local variable change:{3=third, 2=second, 1=first}
ce testMap after changing variable from accessor methods:{3=third, 2=second, 1=first, 4=new}
Как вы можете видеть из выходных данных, значения хэш-карты изменились из-за неглубокого копирования в конструкторе.
Это происходит из-за прямой ссылки на исходный объект в функции getter.
Это все для неизменяемого класса в Java. Мы также узнали о важности глубокого копирования для неизменяемых классов.
Дальнейшее чтение : Если неизменяемый класс имеет много атрибутов, и некоторые из них являются необязательными, мы можем использовать шаблон builder для создания неизменяемых классов .