Я уже знаю определение неизменяемых классов, но мне нужно несколько примеров.
Примеры непреложных классов
Ответ 1
Некоторые известные неизменные классы в стандартном API:
- java.lang.String(уже упоминалось)
- Классы-оболочки для примитивных типов: java.lang.Integer, java.lang.Byte, java.lang.Character, java.lang.Short, java.lang.Boolean, java.lang.Long, java.lang.Double, java.lang.Float
- java.lang.StackTraceElement(используется при построении стеков исключений)
-
Большинство классов перечисления неизменяемы, но это фактически зависит от конкретного случая. (Не реализуйте изменчивые перечисления, это немного повредит вам.) Я думаю, что по крайней мере все классы enum в стандартном API фактически неизменяемы.
-
java.math.BigInteger и java.math.BigDecimal(по крайней мере, объекты самих этих классов, подклассы могут вводить изменчивость, хотя это не очень хорошая идея)
-
java.io.File. Обратите внимание, что это представляет объект, внешний по отношению к виртуальной машине (файл в локальной системе), который может или не может существовать, и имеет некоторые методы, изменяющие и запрашивающие состояние этого внешнего объекта. Но сам объект File остается неизменным. (Все остальные классы в java.io изменяемы.)
-
java.awt.Font - представляет шрифт для рисования текста на экране (могут быть некоторые изменяемые подклассы, но это, конечно, не будет полезно)
- java.awt.BasicStroke - вспомогательный объект для рисования линий в графических контекстах
- java.awt.Color - (по крайней мере, объекты этого класса, некоторые подклассы могут быть изменчивыми или в зависимости от некоторых внешних факторов (например, системных цветов)) и большинства других реализаций java.awt.Paint
- java.awt.GradientPaint,
- java.awt.LinearGradientPaint
- java.awt.RadialGradientPaint,
- (Я не уверен в java.awt.TexturePaint)
-
java.awt.Cursor - представление растрового изображения для курсора мыши (здесь тоже некоторые подклассы могут изменяться или в зависимости от внешних факторов)
-
java.util.Locale - представляющий конкретный географический, политический или культурный регион.
- java.util.UUID - максимально возможный глобальный уникальный идентификатор
-
в то время как большинство коллекций изменяемы, в классе java.util.Collections есть некоторые методы-обертки, которые возвращают немодифицируемый вид коллекции. Если вы передадите им коллекцию, не известную нигде, это на самом деле неизменные коллекции. Кроме того,
Collections.singletonMap()
,.singletonList
,.singleton
возвращают неизменяемые одноэлементные коллекции, а также неизменяемые пустые. -
java.net.URL и java.net.URI - представление ресурса (в Интернете или где-то еще)
- java.net.Inet4Address и java.net.Inet6Address, java.net.InetSocketAddress
- большинство подклассов java.security.Permission(представляющие разрешения, необходимые для некоторого действия или данные для некоторого кода), но не java.security.PermissionCollection и подклассы.
Можно сказать, что примитивные типы тоже неизменяемы - вы не можете изменить значение 42, не так ли?
- класс AccessControlContext неизменяемый класс
AccessControlContext не имеет каких-либо мутирующих методов. И его состояние состоит из списка ProtectionDomains (который является неизменным классом) и DomainCombiner. DomainCombiner - это интерфейс, поэтому в принципе реализация может делать что-то другое при каждом вызове.
На самом деле, а также поведение ProtectionDomain может зависеть от текущей политики в силе -. Спорно ли называть такой объект неизменяемой
и AccessController?
Нет объектов типа AccessController, так как это конечный класс без доступного конструктора. Все методы статичны. Можно сказать, что AccessController не является ни изменяемым, ни неизменным, или и тем, и другим.
То же самое относится ко всем другим классам, которые не могут иметь объекты (экземпляры), наиболее известные:
- java.lang.Void
- java.lang.System(но это имеет некоторое изменяемое статическое состояние -
in
,out
,err
) - java.lang.Math(это тоже - генератор случайных чисел)
- java.lang.reflect.Array
- java.util.Collections
- java.util.Arrays
Ответ 2
Неизменяемые классы не могут быть изменены после строительства. Так, например, Java String
неизменен.
Чтобы сделать класс неизменным, вы должны сделать его final
и все поля private
и final
. Например, следующий класс является неизменным:
public final class Person {
private final String name;
private final int age;
private final Collection<String> friends;
public Person(String name, int age, Collection<String> friends) {
this.name = name;
this.age = age;
this.friends = new ArrayList(friends);
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
public Collection<String> getFriends() {
return Collections.unmodifiableCollection(this.friends);
}
}
Я добавил в методе в примере кода, показывающем, как обрабатывать коллекции, важный момент.
По возможности вы должны сделать классы неизменными, потому что тогда вам не нужно беспокоиться о таких вещах, как безопасность потоков.
Ответ 3
Важно помнить, что объявление класса как окончательного не означает, что оно является "неизменным", это в основном означает, что этот класс не может быть расширен (или специализирован).
Неизменяемые классы должны иметь частные и окончательные поля (без сеттеров), поэтому после его построения он не может изменять значения поля.
Ответ 4
Чтобы создать неизменяемый класс, вам необходимо выполнить следующие шаги:
- Объявить класс окончательным, чтобы он не расширялся.
- Сделать все поля закрытыми, чтобы прямой доступ не разрешался.
- Не предоставлять методы настройки для переменных
- Сделать все изменяемые поля окончательными, чтобы можно было присвоить его значение только один раз.
- Инициализировать все поля через конструктор, выполняющий глубокую копию.
- Выполнение клонирования объектов в методах getter для возврата копии а не возвращать фактическую ссылку на объект.
Ниже приведен пример .
Мы также можем использовать Builder Pattern для легкого создания неизменяемых классов, пример можно найти здесь.
Ответ 5
LocalDate, LocalTime и LocalDateTime (начиная с версии 1.8) также неизменяемы. На самом деле, этот вопрос находится на экзамене OCAJSE8 (1Z0-808), и именно поэтому я решил рассматривать его как не просто комментарий.
Все примитивные классы-оболочки (например, Логические, Символ, Байт, Короткие, Целое > , Длинные, Float и Двойной) являются неизменяемыми.
Деньги и Валюта API (для Java9) также должны быть неизменными.
Кстати, список с поддержкой массива (созданный Arrays.asList(myArray)
) являются структурно неизменными.
Кроме того, существуют некоторые граничные случаи, такие как java.util.Optional (показано на экзамене OCP, 1Z0-809), что является неизменным, если содержащийся элемент сам по себе является неизменным.
Ответ 6
String
- хороший пример "реального мира" неизменяемого класса. И вы можете сравнить его с изменяемым классом StringBuilder
.
Большинство классов Java, используемых для отражения, неизменяемы. И некоторые из них "почти неизменяемы": например, классы, реализующие Accessible
, имеют только метод setAccessible
, который изменяет состояние экземпляра Accessible
.
Я уверен, что в стандартных библиотеках классов есть еще много.
Ответ 7
Документация Sun (Oracle) имеет отличный контрольный список о том, как создать неизменяемый объект.
- Не предоставляйте методы setter - методы, которые изменяют поля или объекты, на которые ссылаются поля.
- Сделать все поля окончательными и частными.
- Не позволяйте подклассам переопределять методы. Самый простой способ сделать это - объявить класс окончательным. Более сложный подход состоит в том, чтобы сделать конструктор частным и построить экземпляры в factory методах.
- Если поля экземпляра включают ссылки на изменяемые объекты, не разрешайте изменять эти объекты:
- Не предоставляйте методы, изменяющие изменяемые объекты.
- Не используйте ссылки на изменяемые объекты. Никогда не храните ссылки на внешние, изменяемые объекты, переданные конструктору; при необходимости, создавать копии и хранить ссылки на копии. Аналогичным образом создайте копии своих внутренних изменяемых объектов, если это необходимо, чтобы избежать возврата оригиналов в ваши методы.
От: http://download.oracle.com/javase/tutorial/essential/concurrency/imstrat.html
На сайте также приведены примеры его использования в контексте concurrency, но неизменность также полезна при написании библиотек. Он гарантирует, что абоненты библиотеки могут изменять только то, что мы им разрешаем.
Ответ 8
Неизменяемый класс - это класс, который когда-то был создан, его содержимое не может быть изменено. Неизменяемыми объектами являются объекты, состояние которых не может быть изменено после его создания. Пример. Строка и все классы оболочки java.
Переменные объекты - это объекты, состояние которых может быть изменено после построения .example- StringBuffer После изменения значения измененной ячейки памяти изменено. Пример ниже -
public static void immutableOperation(){
String str=new String("String is immutable class in Java object value cann't alter once created...");
System.out.println(str);
str.replaceAll("String", "StringBuffer");
System.out.println(str);
str.concat("Concating value ");
System.out.println(str + "HashCode Value " + str.hashCode());
str=str.concat("Concating value ");
System.out.println(str + "HashCode Val " + str.hashCode());
}
public static void mutableOperation(){
StringBuffer str=new StringBuffer("StringBuffer is mutable class in Java object value can alter once created...");
System.out.println(str + "HashCode Val - " + str.hashCode());
str.replace(0, 12, "String");
System.out.println(str + "HashCode Val - " + str.hashCode());
}
Ответ 9
Мне нравится использовать примеры, обладающие свойством mutable. Это помогает понять, как действительно функционируют неизменные классы.
Mutable class
class MutableBook {
private String title;
public String getTitle(){
return this.title;
}
public void setTitle(String title){
this.title = title;
}
}
И неизменяемая реализация с использованием изменяемого экземпляра книги.
public class ImmutableReader {
private final MutableBook readersBook;
private final int page;
public ImmutableReader(MutableBook book) {
this(book, 0);
}
private ImmutableReader(MutableBook book, int page){
this.page = page;
// Make copy to ensure this books state won't change.
MutableBook bookCopy = new MutableBook();
bookCopy.setTitle(book.getTitle());
this.readersBook = bookCopy;
}
public MutableBook getBook() {
// Do not return the book, but a new copy. Do not want the readers
// book to change it state if developer changes book after this call.
MutableBook bookCopy = new MutableBook();
bookCopy.setTitle(this.readersBook.getTitle());
return bookCopy;
}
public int getPage() {
// primitives are already immutable.
return page;
}
/**
* Must return reader instance since it state has changed.
**/
public ImmutableReader turnPage() {
return new ImmutableReader(this.readersBook, page + 1);
}
}
Для того, чтобы ваш класс был действительно неизменным, он должен соответствовать следующей сиремии:
- Все члены класса объявлены окончательными.
- Все переменные, используемые в классе на уровне класса, должны быть созданы при построении класса.
- Никакая переменная класса не может иметь метод setter.
- Это подразумевается из первого утверждения, но хочу дать понять, что вы не можете изменить состояние класса.
- Все дочерние объекты также должны быть неизменными или их состояние никогда не изменяется в неизменяемом классе.
- Если у вас есть класс с изменяемыми свойствами, вы должны заблокировать его. Объявите его приватным и убедитесь, что вы его никогда не изменяете.
Чтобы узнать немного больше, взгляните на мой пост в блоге: http://keaplogik.blogspot.com/2015/07/java-immutable-classes-simplified.html
Ответ 10
Простой неизменный класс:
public final class ImmutableStudent {
private final int id;
private final String name;
public ImmutableStudent(int id, String name) {
this.name = name;
this.id = id;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
}
Неизменяемый класс с изменяемыми полями:
package com.programmer.gate.beans;
public final class ImmutableStudent {
private final int id;
private final String name;
private final Age age;
public ImmutableStudent(int id, String name, Age age) {
this.name = name;
this.id = id;
Age cloneAge = new Age();
cloneAge.setDay(age.getDay());
cloneAge.setMonth(age.getMonth());
cloneAge.setYear(age.getYear());
this.age = cloneAge;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public Age getAge() {
Age cloneAge = new Age();
cloneAge.setDay(this.age.getDay());
cloneAge.setMonth(this.age.getMonth());
cloneAge.setYear(this.age.getYear());
return cloneAge;
}
}