Автор оригинала: 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 HashMaptestMap; 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 для создания неизменяемых классов .