Einführung
Dieser Artikel gibt einen Überblick darüber, wie man in der Java-Programmierung eine unveränderliche Klasse erstellt.
Ein Objekt ist unveränderlich, wenn sich sein Zustand nach der Initialisierung nicht mehr ändert. Zum Beispiel ist die String
-Klasse unveränderlich, und einmal instanziiert, ändert sich der Wert eines String
-Objekts nie. Erfahren Sie mehr darüber, warum die String
-Klasse in Java unveränderlich ist.
Weil ein unveränderliches Objekt nicht aktualisiert werden kann, müssen Programme für jede Zustandsänderung ein neues Objekt erstellen. Unveränderliche Objekte haben jedoch auch folgende Vorteile:
- Eine unveränderliche Klasse eignet sich gut für Caching-Zwecke, da Sie sich keine Gedanken über Wertänderungen machen müssen.
- Eine unveränderliche Klasse ist von Natur aus threadsicher, sodass Sie sich in Mehrfadenumgebungen keine Gedanken über Threadsicherheit machen müssen.
Erfahren Sie mehr über Multithreading in Java und durchsuchen Sie die Java-Multithreading-Interviewfragen.
Erstellen einer unveränderlichen Klasse in Java
Um eine unveränderliche Klasse in Java zu erstellen, müssen Sie diesen allgemeinen Grundsätzen folgen:
- Deklarieren Sie die Klasse als
final
, damit sie nicht erweitert werden kann. - Machen Sie alle Felder
private
, damit ein direkter Zugriff nicht erlaubt ist. - Stellen Sie keine Settermethoden für Variablen bereit.
- Machen Sie alle veränderlichen Felder
final
, damit der Wert eines Feldes nur einmal zugewiesen werden kann. - Initialisieren Sie alle Felder mithilfe einer Konstruktor-Methode, die eine Deep-Copy durchführt.
- Führen Sie eine Klonung der Objekte in den Getter-Methoden durch, um eine Kopie zurückzugeben, anstatt die tatsächliche Objektreferenz zurückzugeben.
Die folgende Klasse ist ein Beispiel, das die Grundlagen der Unveränderlichkeit veranschaulicht. Die Klasse FinalClassExample
definiert die Felder und stellt die Konstruktor-Methode bereit, die Deep-Copy verwendet, um das Objekt zu initialisieren. Der Code in der main
-Methode der Datei FinalClassExample.java
testet die Unveränderlichkeit des Objekts.
Erstellen Sie eine neue Datei mit dem Namen FinalClassExample.java
und kopieren Sie den folgenden Code hinein:
import java.util.HashMap;
import java.util.Iterator;
public final class FinalClassExample {
// Felder der Klasse FinalClassExample
private final int id;
private final String name;
private final HashMap<String,String> testMap;
public int getId() {
return id;
}
public String getName() {
return name;
}
// Getter-Funktion für veränderbare Objekte
public HashMap<String, String> getTestMap() {
return (HashMap<String, String>) testMap.clone();
}
// Konstruktor-Methode führt eine Deep Copy durch
public FinalClassExample(int i, String n, HashMap<String,String> hm){
System.out.println("Performing Deep Copy for Object initialization");
// "this" Schlüsselwort bezieht sich auf das aktuelle Objekt
this.id=i;
this.name=n;
HashMap<String,String> tempMap=new HashMap<String,String>();
String key;
Iterator<String> it = hm.keySet().iterator();
while(it.hasNext()){
key=it.next();
tempMap.put(key, hm.get(key));
}
this.testMap=tempMap;
}
// Testen der unveränderlichen Klasse
public static void main(String[] args) {
HashMap<String, String> h1 = new HashMap<String,String>();
h1.put("1", "first");
h1.put("2", "second");
String s = "original";
int i=10;
FinalClassExample ce = new FinalClassExample(i,s,h1);
// Drucken der Werte von ce
System.out.println("ce id: "+ce.getId());
System.out.println("ce name: "+ce.getName());
System.out.println("ce testMap: "+ce.getTestMap());
// Ändern der Werte der lokalen Variablen
i=20;
s="modified";
h1.put("3", "third");
// erneutes Drucken der Werte
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<String, String> hmTest = ce.getTestMap();
hmTest.put("4", "new");
System.out.println("ce testMap after changing variable from getter methods: "+ce.getTestMap());
}
}
Das Programm kompilieren und ausführen:
- javac FinalClassExample.java
- java FinalClassExample
Hinweis: Beim Kompilieren der Datei erhalten Sie möglicherweise folgende Meldung: Note: FinalClassExample.java verwendet nicht überprüfbare oder unsichere Operationen
, da die Getter-Methode einen nicht überprüften Cast von HashMap<String,String>
zu Object
verwendet. Sie können die Compiler-Warnung für dieses Beispiel ignorieren.
Sie erhalten die folgende Ausgabe:
OutputPerforming Deep Copy for Object initialization
ce id: 10
ce name: original
ce testMap: {1=first, 2=second}
ce id after local variable change: 10
ce name after local variable change: original
ce testMap after local variable change: {1=first, 2=second}
ce testMap after changing variable from getter methods: {1=first, 2=second}
Die Ausgabe zeigt, dass sich die Werte der HashMap nicht geändert haben, da der Konstruktor eine Deep Copy verwendet und die Getter-Funktion einen Klon des Originalobjekts zurückgibt.
Was passiert, wenn Sie keine Tiefenkopie und Klonen verwenden?
Sie können Änderungen an der Datei FinalClassExample.java
vornehmen, um zu zeigen, was passiert, wenn Sie anstelle einer Tiefenkopie eine Flachkopie verwenden und das Objekt anstelle einer Kopie zurückgeben. Das Objekt ist nicht mehr unveränderlich und kann geändert werden. Führen Sie die folgenden Änderungen an der Beispieldatei durch (oder kopieren Sie sie aus dem Codebeispiel):
- Löschen Sie die Konstruktor-Methode, die eine Tiefenkopie bereitstellt, und fügen Sie die Konstruktor-Methode ein, die eine Flachkopie bereitstellt und die im folgenden Beispiel hervorgehoben ist.
- Im Getter-Funktion löschen Sie
return (HashMap<String, String>) testMap.clone();
und fügen Siereturn testMap;
hinzu.
Die Beispieldatei sollte jetzt so aussehen:
import java.util.HashMap;
import java.util.Iterator;
public final class FinalClassExample {
// Felder der Klasse FinalClassExample
private final int id;
private final String name;
private final HashMap<String,String> testMap;
public int getId() {
return id;
}
public String getName() {
return name;
}
// Getter-Funktion für veränderliche Objekte
public HashMap<String, String> getTestMap() {
return testMap;
}
// Konstruktor-Methode, die eine Flachkopie durchführt
public FinalClassExample(int i, String n, HashMap<String,String> hm){
System.out.println("Performing Shallow Copy for Object initialization");
this.id=i;
this.name=n;
this.testMap=hm;
}
// Testen Sie die unveränderliche Klasse
public static void main(String[] args) {
HashMap<String, String> h1 = new HashMap<String,String>();
h1.put("1", "first");
h1.put("2", "second");
String s = "original";
int i=10;
FinalClassExample ce = new FinalClassExample(i,s,h1);
// geben Sie die Werte von ce aus
System.out.println("ce id: "+ce.getId());
System.out.println("ce name: "+ce.getName());
System.out.println("ce testMap: "+ce.getTestMap());
// ändern Sie die Werte der lokalen Variable
i=20;
s="modified";
h1.put("3", "third");
// geben Sie die Werte erneut aus
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<String, String> hmTest = ce.getTestMap();
hmTest.put("4", "new");
System.out.println("ce testMap after changing variable from getter methods: "+ce.getTestMap());
}
}
Kompilieren und führen Sie das Programm aus:
- javac FinalClassExample.java
- java FinalClassExample
Du erhältst folgende Ausgabe:
OutputPerforming Shallow Copy for Object initialization
ce id: 10
ce name: original
ce testMap: {1=first, 2=second}
ce id after local variable change: 10
ce name after local variable change: original
ce testMap after local variable change: {1=first, 2=second, 3=third}
ce testMap after changing variable from getter methods: {1=first, 2=second, 3=third, 4=new}
Die Ausgabe zeigt, dass sich die HashMap-Werte geändert haben, weil der Konstruktor eine Flachkopie verwendet und es daher eine direkte Referenz zum Originalobjekt in der Getter-Funktion gibt.
Schlussfolgerung
Du hast einige allgemeine Grundsätze kennengelernt, die du beachten solltest, wenn du in Java unveränderliche Klassen erstellst, einschließlich der Bedeutung der Tiefenkopie. Setze dein Lernen mit weiteren Java-Tutorials fort.
Source:
https://www.digitalocean.com/community/tutorials/how-to-create-immutable-class-in-java