Wie erstelle ich unveränderliche Objekte in Java?
Welche Objekte sollten als unveränderlich bezeichnet werden?
Wenn ich eine Klasse mit allen statischen Mitgliedern habe, ist sie unveränderlich?
Wie erstelle ich unveränderliche Objekte in Java?
Welche Objekte sollten als unveränderlich bezeichnet werden?
Wenn ich eine Klasse mit allen statischen Mitgliedern habe, ist sie unveränderlich?
Antworten:
Nachfolgend sind die harten Anforderungen an ein unveränderliches Objekt aufgeführt.
final
, das Objekt jedoch weiterhin veränderbar sein kann private final Date imStillMutable
). Sie sollten defensive copies
in diesen Fällen machen.Die Gründe für die Erstellung der Klasse final
sind sehr subtil und werden oft übersehen. Wenn es nicht endgültig ist, können Personen Ihre Klasse frei erweitern, überschreiben public
oder protected
verhalten, veränderbare Eigenschaften hinzufügen und dann ihre Unterklasse als Ersatz angeben. Indem Sie die Klasse deklarieren final
, können Sie sicherstellen, dass dies nicht geschieht.
Um das Problem in Aktion zu sehen, betrachten Sie das folgende Beispiel:
public class MyApp{
/**
* @param args
*/
public static void main(String[] args){
System.out.println("Hello World!");
OhNoMutable mutable = new OhNoMutable(1, 2);
ImSoImmutable immutable = mutable;
/*
* Ahhhh Prints out 3 just like I always wanted
* and I can rely on this super immutable class
* never changing. So its thread safe and perfect
*/
System.out.println(immutable.add());
/* Some sneak programmer changes a mutable field on the subclass */
mutable.field3=4;
/*
* Ahhh let me just print my immutable
* reference again because I can trust it
* so much.
*
*/
System.out.println(immutable.add());
/* Why is this buggy piece of crap printing 7 and not 3
It couldn't have changed its IMMUTABLE!!!!
*/
}
}
/* This class adheres to all the principles of
* good immutable classes. All the members are private final
* the add() method doesn't modify any state. This class is
* just a thing of beauty. Its only missing one thing
* I didn't declare the class final. Let the chaos ensue
*/
public class ImSoImmutable{
private final int field1;
private final int field2;
public ImSoImmutable(int field1, int field2){
this.field1 = field1;
this.field2 = field2;
}
public int add(){
return field1+field2;
}
}
/*
This class is the problem. The problem is the
overridden method add(). Because it uses a mutable
member it means that I can't guarantee that all instances
of ImSoImmutable are actually immutable.
*/
public class OhNoMutable extends ImSoImmutable{
public int field3 = 0;
public OhNoMutable(int field1, int field2){
super(field1, field2);
}
public int add(){
return super.add()+field3;
}
}
In der Praxis tritt das obige Problem häufig in Umgebungen mit Abhängigkeitsinjektion auf. Sie instanziieren Dinge nicht explizit und die Superklassenreferenz, die Sie erhalten, kann tatsächlich eine Unterklasse sein.
Um harte Garantien für die Unveränderlichkeit zu geben, muss die Klasse als markiert werden final
. Dies wird in Joshua Blochs Effective Java ausführlich behandelt und in der Spezifikation für das Java-Speichermodell explizit erwähnt .
Fügen Sie der Klasse nur keine öffentlichen Mutator-Methoden (Setter-Methoden) hinzu.
private
oder die Klasse sollte sein final
. Nur um Vererbung zu vermeiden. Weil die Vererbung die Kapselung verletzt.
Klassen sind nicht unveränderlich, Objekte sind.
Unveränderlich bedeutet: Mein öffentlich sichtbarer Zustand kann sich nach der Initialisierung nicht ändern.
Felder müssen nicht als endgültig deklariert werden, obwohl dies enorm zur Gewährleistung der Gewindesicherheit beitragen kann
Wenn Ihre Klasse nur statische Elemente hat, sind Objekte dieser Klasse unveränderlich, da Sie den Status dieses Objekts nicht ändern können (wahrscheinlich können Sie es auch nicht erstellen :))
Um eine Klasse in Java unveränderlich zu machen, können Sie die folgenden Punkte beachten:
1. Stellen Sie keine Setter-Methoden bereit, um die Werte einer der Instanzvariablen der Klasse zu ändern.
2. Deklarieren Sie die Klasse als 'final' . Dies würde verhindern, dass eine andere Klasse sie erweitert und somit eine Methode überschreibt, die Instanzvariablenwerte ändern könnte.
3. Deklarieren Sie die Instanzvariablen als privat und endgültig .
4. Sie können den Konstruktor der Klasse auch als privat deklarieren und bei Bedarf eine Factory-Methode hinzufügen, um eine Instanz der Klasse zu erstellen.
Diese Punkte sollten helfen !!
private
?
Von Oracle - Website, wie unveränderliche Objekte in Java erstellen.
- Stellen Sie keine "Setter" -Methoden bereit - Methoden, die Felder oder Objekte ändern, auf die Felder verweisen.
- Machen Sie alle Felder endgültig und privat.
- Unterklassen dürfen Methoden nicht überschreiben. Der einfachste Weg, dies zu tun, besteht darin, die Klasse als endgültig zu deklarieren. Ein komplexerer Ansatz besteht darin, den Konstruktor privat zu machen und Instanzen in Factory-Methoden zu konstruieren.
- Wenn die Instanzfelder Verweise auf veränderbare Objekte enthalten, dürfen diese Objekte nicht geändert werden:
I. Geben Sie keine Methoden an, mit denen die veränderlichen Objekte geändert werden.
II. Teilen Sie keine Verweise auf die veränderlichen Objekte. Speichern Sie niemals Verweise auf externe, veränderbare Objekte, die an den Konstruktor übergeben werden. Erstellen Sie gegebenenfalls Kopien und speichern Sie Verweise auf die Kopien. Erstellen Sie bei Bedarf Kopien Ihrer internen veränderlichen Objekte, um zu vermeiden, dass die Originale in Ihren Methoden zurückgegeben werden.
Ein unveränderliches Objekt ist ein Objekt, das seinen internen Status nach der Erstellung nicht ändert. Sie sind in Multithread-Anwendungen sehr nützlich, da sie ohne Synchronisierung von Threads gemeinsam genutzt werden können.
1. Fügen Sie keine Setter-Methode hinzu
Wenn Sie ein unveränderliches Objekt erstellen, ändert sich sein interner Status niemals. Die Aufgabe einer Setter-Methode besteht darin, den internen Wert eines Felds so zu ändern, dass Sie ihn nicht hinzufügen können.
2. Deklarieren Sie alle Felder als endgültig und privat
Ein privates Feld ist von außerhalb der Klasse nicht sichtbar, sodass keine manuellen Änderungen darauf angewendet werden können.
Wenn Sie ein Feld final deklarieren, wird garantiert, dass sich der Wert niemals ändert, wenn er auf einen primitiven Wert verweist, wenn er auf ein Objekt verweist. Die Referenz kann nicht geändert werden. Dies reicht nicht aus, um sicherzustellen, dass ein Objekt mit nur privaten Endfeldern nicht veränderbar ist.
3. Wenn ein Feld ein veränderbares Objekt ist, erstellen Sie defensive Kopien davon für Getter-Methoden
Wir haben zuvor gesehen, dass die Definition eines Feldes final und private nicht ausreicht, da es möglich ist, seinen internen Status zu ändern. Um dieses Problem zu lösen, müssen wir eine defensive Kopie dieses Feldes erstellen und dieses Feld jedes Mal zurückgeben, wenn es angefordert wird.
4. Wenn ein an den Konstruktor übergebenes veränderbares Objekt einem Feld zugewiesen werden muss, erstellen Sie eine defensive Kopie davon
Das gleiche Problem tritt auf, wenn Sie eine an den Konstruktor übergebene Referenz enthalten, da diese geändert werden kann. Wenn Sie also einen Verweis auf ein an den Konstruktor übergebenes Objekt halten, können veränderbare Objekte erstellt werden. Um dieses Problem zu lösen, muss eine defensive Kopie des Parameters erstellt werden, wenn es sich um veränderbare Objekte handelt.
Beachten Sie, dass es nicht ausreicht, das Feld als endgültig und privat zu definieren , wenn ein Feld eine Referenz auf ein unveränderliches Objekt ist, um defensive Konstruktionen davon im Konstruktor und in den Getter-Methoden zu erstellen.
5. Unterklassen dürfen Methoden nicht überschreiben
Wenn eine Unterklasse eine Methode überschreibt, kann sie den ursprünglichen Wert eines veränderlichen Feldes anstelle einer defensiven Kopie davon zurückgeben.
Wenn Sie diese einfachen Regeln befolgen, können Sie Ihre unveränderlichen Objekte frei zwischen Threads teilen, da diese threadsicher sind!
Es gibt kein Richtig oder Falsch, es kommt nur darauf an, was Sie bevorzugen. Es hängt nur von Ihren Vorlieben ab und davon, was Sie erreichen möchten (und beide Ansätze problemlos anwenden zu können, ohne eingefleischte Fans der einen oder anderen Seite zu entfremden, ist ein heiliger Gral, nach dem einige Sprachen suchen).
Zunächst wissen Sie, warum Sie ein unveränderliches Objekt erstellen müssen und welche Vorteile ein unveränderliches Objekt bietet.
Vorteile eines unveränderlichen Objekts
Parallelität und Multithreading Es ist automatisch threadsicher, so dass Synchronisationsprobleme auftreten ... usw.
Sie brauchen nicht zu Konstruktor kopieren nicht brauchen , um die Umsetzung von Klon. Klasse kann nicht überschrieben werden Machen Sie das Feld als privaten und endgültigen Aufrufer, ein Objekt vollständig in einem einzigen Schritt zu erstellen , anstatt einen Konstruktor ohne Argumente zu verwenden
Unveränderliche Objekte sind einfach Objekte, deren Status bedeutet, dass sich die Daten des Objekts nicht ändern können, nachdem das unveränderliche Objekt erstellt wurde.
Bitte beachten Sie den folgenden Code.
public final class ImmutableReminder{
private final Date remindingDate;
public ImmutableReminder (Date remindingDate) {
if(remindingDate.getTime() < System.currentTimeMillis()){
throw new IllegalArgumentException("Can not set reminder" +
" for past time: " + remindingDate);
}
this.remindingDate = new Date(remindingDate.getTime());
}
public Date getRemindingDate() {
return (Date) remindingDate.clone();
}
}
Mutabilität minimieren
Eine unveränderliche Klasse ist einfach eine Klasse, deren Instanzen nicht geändert werden können. Alle in jeder Instanz enthaltenen Informationen werden beim Erstellen bereitgestellt und sind für die Lebensdauer des Objekts festgelegt.
Unveränderliche JDK-Klassen: String, die Boxed-Primitive-Klassen (Wrapper-Klassen), BigInteger und BigDecimal usw.
Wie macht man eine Klasse unveränderlich?
Machen Sie defensive Kopien. Stellen Sie den exklusiven Zugriff auf veränderbare Komponenten sicher.
öffentliche Liste getList () {return Collections.unmodizableList (Liste); <=== defensive Kopie des veränderlichen Feldes, bevor es an den Aufrufer zurückgegeben wird}
Wenn Ihre Klasse Felder enthält, die auf veränderbare Objekte verweisen, stellen Sie sicher, dass Clients der Klasse keine Verweise auf diese Objekte erhalten können. Initialisieren Sie ein solches Feld niemals mit einer vom Client bereitgestellten Objektreferenz oder geben Sie die Objektreferenz von einem Accessor zurück.
import java.util.Date;
public final class ImmutableClass {
public ImmutableClass(int id, String name, Date doj) {
this.id = id;
this.name = name;
this.doj = doj;
}
private final int id;
private final String name;
private final Date doj;
public int getId() {
return id;
}
public String getName() {
return name;
}
/**
* Date class is mutable so we need a little care here.
* We should not return the reference of original instance variable.
* Instead a new Date object, with content copied to it, should be returned.
* */
public Date getDoj() {
return new Date(doj.getTime()); // For mutable fields
}
}
import java.util.Date;
public class TestImmutable {
public static void main(String[] args) {
String name = "raj";
int id = 1;
Date doj = new Date();
ImmutableClass class1 = new ImmutableClass(id, name, doj);
ImmutableClass class2 = new ImmutableClass(id, name, doj);
// every time will get a new reference for same object. Modification in reference will not affect the immutability because it is temporary reference.
Date date = class1.getDoj();
date.setTime(date.getTime()+122435);
System.out.println(class1.getDoj()==class2.getDoj());
}
}
Weitere Informationen finden Sie in meinem Blog:
http://javaexplorer03.blogspot.in/2015/07/minimize-mutability.html
Ein Objekt wird als unveränderlich bezeichnet, wenn sein Status nach dem Erstellen nicht mehr geändert werden kann. Eine der einfachsten Möglichkeiten, unveränderliche Klassen in Java zu erstellen, besteht darin, alle Felder als endgültig festzulegen. Wenn Sie unveränderliche Klassen schreiben müssen, die veränderbare Klassen wie "java.util.Date" enthalten. Um die Unveränderlichkeit in solchen Fällen zu bewahren, wird empfohlen, eine Kopie des Originalobjekts zurückzugeben.
Unveränderliche Objekte sind Objekte, deren Status nach ihrer Erstellung nicht mehr geändert werden kann. Beispielsweise ist die String-Klasse eine unveränderliche Klasse. Unveränderliche Objekte können nicht geändert werden, sodass sie bei gleichzeitiger Ausführung auch threadsicher sind.
Merkmale unveränderlicher Klassen:
Schlüssel zum Schreiben einer unveränderlichen Klasse:
Die folgenden Schritte müssen berücksichtigt werden, wenn Sie eine Klasse als unveränderliche Klasse möchten.
Werfen wir einen Blick auf das, was wir oben eingegeben haben:
//ImmutableClass
package younus.attari;
public final class ImmutableExample {
private final String name;
private final String address;
public ImmutableExample(String name,String address){
this.name=name;
this.address=address;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
}
//MainClass from where an ImmutableClass will be called
package younus.attari;
public class MainClass {
public static void main(String[] args) {
ImmutableExample example=new ImmutableExample("Muhammed", "Hyderabad");
System.out.println(example.getName());
}
}
Hinzufügen von mehr als auf die Antwort , die von @ nsfyn55 die folgenden Aspekte müssen auch für Objekt Unveränderlichkeit in Betracht gezogen werden, die von sind prime Bedeutung
Betrachten Sie die folgenden Klassen:
public final class ImmutableClass {
private final MutableClass mc;
public ImmutableClass(MutableClass mc) {
this.mc = mc;
}
public MutableClass getMutClass() {
return this.mc;
}
}
public class MutableClass {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
public class MutabilityCheck {
public static void main(String[] args) {
MutableClass mc = new MutableClass();
mc.setName("Foo");
ImmutableClass iMC = new ImmutableClass(mc);
System.out.println(iMC.getMutClass().getName());
mc.setName("Bar");
System.out.println(iMC.getMutClass().getName());
}
}
Es folgt die Ausgabe von MutabilityCheck:
Foo
Bar
Es ist wichtig sich das zu merken,
Konstruieren veränderlicher Objekte auf einem unveränderlichen Objekt (über den Konstruktor), entweder durch 'Kopieren' oder 'Cloing' in Instanzvariablen der unveränderlichen Objekte, die durch die folgenden Änderungen beschrieben werden:
public final class ImmutableClass {
private final MutableClass mc;
public ImmutableClass(MutableClass mc) {
this.mc = new MutableClass(mc);
}
public MutableClass getMutClass() {
return this.mc;
}
}
public class MutableClass {
private String name;
public MutableClass() {
}
//copy constructor
public MutableClass(MutableClass mc) {
this.name = mc.getName();
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
gewährleistet immer noch keine vollständige Unveränderlichkeit, da Folgendes aus der Klasse MutabilityCheck weiterhin gültig ist:
iMC.getMutClass().setName("Blaa");
Das Ausführen von MutabilityCheck mit den in 1. vorgenommenen Änderungen führt jedoch zu folgender Ausgabe:
Foo
Foo
Um eine vollständige Unveränderlichkeit für ein Objekt zu erreichen, müssen auch alle seine abhängigen Objekte unveränderlich sein
Ab JDK 14+ mit JEP 359 können wir " records
" verwenden. Es ist die einfachste und stressfreieste Art, unveränderliche Klassen zu erstellen.
Eine Datensatzklasse ist ein flach unveränderlicher , transparenter Träger für einen festen Satz von Feldern, der als Datensatz bezeichnet components
wird und eine state
Beschreibung für den Datensatz enthält. Jedes component
führt zu einem final
Feld, das den angegebenen Wert enthält, und einer accessor
Methode zum Abrufen des Werts. Der Feldname und der Zugriffsname stimmen mit dem Namen der Komponente überein.
Betrachten wir das Beispiel zum Erstellen eines unveränderlichen Rechtecks
record Rectangle(double length, double width) {}
Keine Notwendigkeit, einen Konstruktor zu deklarieren, keine Notwendigkeit, equals
& hashCode
Methoden zu implementieren . Nur alle Datensätze benötigen einen Namen und eine Statusbeschreibung.
var rectangle = new Rectangle(7.1, 8.9);
System.out.print(rectangle.length()); // prints 7.1
Wenn Sie den Wert während der Objekterstellung überprüfen möchten, müssen wir den Konstruktor explizit deklarieren.
public Rectangle {
if (length <= 0.0) {
throw new IllegalArgumentException();
}
}
Der Datensatzkörper kann statische Methoden, statische Felder, statische Initialisierer, Konstruktoren, Instanzmethoden und verschachtelte Typen deklarieren.
Instanzmethoden
record Rectangle(double length, double width) {
public double area() {
return this.length * this.width;
}
}
statische Felder, Methoden
Da state Teil der Komponenten sein sollte, können wir Datensätzen keine Instanzfelder hinzufügen. Wir können jedoch statische Felder und Methoden hinzufügen:
record Rectangle(double length, double width) {
static double aStaticField;
static void aStaticMethod() {
System.out.println("Hello Static");
}
}