Ich habe gelernt, dass beim Ändern einer Variablen in Java die Variable, auf der sie basiert, nicht geändert wird
int a = new Integer(5);
int b = a;
b = b + b;
System.out.println(a); // 5 as expected
System.out.println(b); // 10 as expected
Ähnliches habe ich für Objekte angenommen. Betrachten Sie diese Klasse.
public class SomeObject {
public String text;
public SomeObject(String text) {
this.setText(text);
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
Nachdem ich diesen Code ausprobiert hatte, war ich verwirrt.
SomeObject s1 = new SomeObject("first");
SomeObject s2 = s1;
s2.setText("second");
System.out.println(s1.getText()); // second as UNexpected
System.out.println(s2.getText()); // second as expected
Bitte erklären Sie mir, warum sich das Ändern eines der Objekte auf das andere auswirkt. Ich verstehe, dass der Wert von variablem Text für beide Objekte an derselben Stelle im Speicher gespeichert ist.
Warum sind die Werte für Variablen unabhängig, aber für Objekte korreliert?
Wie kann man SomeObject duplizieren, wenn eine einfache Zuweisung nicht ausreicht?
Integer
ist einObject
und kein primitiver Typ. Wenn Sie schreibenInteger b = a;
,b
bezieht sich auf dieselbe Instanz wiea
. Beim Schreibenb = b+b;
bezieht sich b jedoch auf eine neue Instanz, die vom+
Operator erstellt wird.Antworten:
Jede Variable in Java ist eine Referenz . Also wenn du es tust
Sie zeigen nur
s2
auf dasselbe Objekt wies1
auf. Sie weisenSomeClass
s2 tatsächlich den Wert der Referenz s1 zu (die auf eine Instanz von verweist ). Wenn Sie Änderungen vornehmens1
,s2
werden diese ebenfalls geändert (da sie auf dasselbe Objekt verweisen).Es gibt eine Ausnahme, primitive Typen :
int, double, float, boolean, char, byte, short, long
. Sie werden nach Wert gespeichert. Wenn Sie also verwenden=
, weisen Sie nur den Wert zu, aber sie können nicht auf dasselbe Objekt verweisen (da es sich nicht um Referenzen handelt). Das bedeutet, dassint b = a;
setzt nur den Wert von
b
auf den Wert vona
. Wenn Sie sich änderna
,b
wird sich nicht ändern.Letztendlich ist alles eine Zuordnung nach Wert, es ist nur der Wert der Referenz und nicht der Wert des Objekts (mit Ausnahme der oben erwähnten primitiven Typen).
Wenn Sie in Ihrem Fall eine Kopie davon erstellen möchten
s1
, können Sie dies folgendermaßen tun:SomeClass s1 = new SomeClass("first"); SomeClass s2 = new SomeClass(s1.getText());
Alternativ können Sie einen Kopierkonstruktor hinzufügen
SomeClass
, der eine Instanz als Argument verwendet und in eine eigene Instanz kopiert.class SomeClass { private String text; // all your fields and methods go here public SomeClass(SomeClass copyInstance) { this.text = new String(copyInstance.text); } }
Mit diesem können Sie ein Objekt ziemlich einfach kopieren:
SomeClass s2 = new SomeClass(s1);
quelle
s1.clone()
ist manchmal eine Option.Die Antwort von @ brimborium ist sehr gut (+1 für ihn), aber ich möchte nur anhand einiger Zahlen näher darauf eingehen. Nehmen wir zuerst die primitive Aufgabe:
int a = new Integer(5); int b = a; b = b + b; System.out.println(a); // 5 as expected System.out.println(b); // 10 as expected
int a = new Integer(5);
1- Die erste Anweisung erstellt ein Integer-Objekt mit dem Wert 5.
a
Wenn Sie es dann der Variablen zuweisen, wird das Integer-Objekt entpackt und als Grundelement gespeicherta
.Nach dem Erstellen eines Integer-Objekts und vor der Zuweisung:
Nach der Zuordnung:
int b = a;
2- Dies liest nur den Wert von
a
und speichert ihn dann inb
.(Das Integer-Objekt ist jetzt für die Speicherbereinigung berechtigt, aber zu diesem Zeitpunkt noch nicht unbedingt für die Speicherbereinigung.)
3- Dies liest den Wert von
b
zweimal, addiert sie und platziert den neuen Wert inb
.Auf der anderen Seite:
SomeObject s1 = new SomeObject("first"); SomeObject s2 = s1; s2.setText("second"); System.out.println(s1.getText()); // second as UNexpected System.out.println(s2.getText()); // second as expected
SomeObject s1 = new SomeObject("first");
1- Erstellt eine neue Instanz der
SomeObject
Klasse und weist sie der Referenz zus1
.2- Dadurch werden die Referenzpunkte
s2
auf das Objekt gesetzt,s1
auf das gezeigt wird.s2.setText("second");
3- Wenn Sie Setter für eine Referenz verwenden, wird das Objekt geändert, auf das die Referenz zeigt.
4- Beide sollten drucken
second
, da die beiden Referenzens1
unds2
beziehen sich auf das gleiche Objekt (wie in der vorherigen Abbildung gezeigt).quelle
SomeObject
), möglicherweise auch mit Bildern.Wenn du das machst
SomeObject s1 = new SomeObject("first"); SomeObject s2 = s1;
Sie haben 2 Verweise auf dasselbe Objekt. Dies bedeutet, dass unabhängig davon, welches Referenzobjekt Sie verwenden, die von Ihnen vorgenommenen Änderungen bei Verwendung der zweiten Referenz sichtbar sind.
Stellen Sie sich das so vor: Sie haben einen Fernseher im Raum, aber zwei Fernbedienungen: Es spielt keine Rolle, welche Fernbedienung Sie verwenden, Sie werden immer noch Änderungen an demselben zugrunde liegenden Objekt (dem Fernseher) vornehmen.
quelle
Wenn Sie schreiben:
SomeObject s1 = new SomeObject("first");
s1
ist nicht einSomeObject
. Es ist ein Verweis auf dasSomeObject
Objekt.Wenn Sie also s2 s1 zuweisen, weisen Sie einfach Referenzen / Handles zu, und das zugrunde liegende Objekt ist dasselbe. Dies ist in Java wichtig. Alles ist pass-by-value, aber Sie geben niemals Objekte weiter - nur Verweise auf Objekte.
Wenn Sie also
s1 = s2
eine Methode zuweisen und dann aufrufens2
, mit der ein Objekt geändert wird, wird das zugrunde liegende Objekt geändert. Dies wird angezeigt, wenn Sie auf das Objekt über verweisens1
.Dies ist ein Argument für die Unveränderlichkeit in Objekten. Indem Sie Objekte unveränderlich machen, ändern sie sich nicht unter Ihnen und verhalten sich daher vorhersehbarer. Wenn Sie ein Objekt kopieren möchten, besteht die einfachste / praktischste Methode darin, eine
copy()
Methode zu schreiben , die einfach eine neue Version erstellt und über die Felder kopiert. Sie können clevere Kopien mit Serialisierung / Reflexion usw. erstellen, aber das ist natürlich komplexer.quelle
Von den zehn häufigsten Fehlern, die Java-Programmierer machen :
Da
int
es sich um einen primitiven Typ handelt,int b = a;
handelt es sich um eine Kopie nach Wert, was bedeutet, dassa
undb
zwei verschiedene Objekte sind, jedoch mit demselben Wert.SomeObject s2 = s1;
makes1
unds2
zwei Referenzen desselben Objekts. Wenn Sie also eines ändern, wird auch das andere geändert.Eine gute Lösung besteht darin, einen anderen Konstruktor wie folgt zu implementieren:
public class SomeObject{ public SomeObject(SomeObject someObject) { setText(someObject.getText()); } // your code }
Dann benutze es so:
SomeObject s2 = new SomeObject(s1);
quelle
In Ihrem Code
s1
unds2
sind das gleiche Objekt (Sie haben nur ein Objekt mit erstelltnew
) und Sie lassens2
in der nächsten Zeile auf das gleiche Objekt zeigen. Wenn Sie dies änderntext
, ändert sich daher beides, wenn Sie den Wert durchs1
und referenzierens2
.Der
+
Operator für Ganzzahlen erstellt ein neues Objekt und ändert das vorhandene Objekt nicht (das Hinzufügen von 5 + 5 ergibt also nicht 5 den neuen Wert 10 ...).quelle
Das liegt daran, dass die JVM einen Zeiger auf speichert
s1
. Wenn Sie anrufens2 = s1
, sagen Sie im Grunde, dass ders2
Zeiger (dh die Speicheradresse) den gleichen Wert hat wie der fürs1
. Da beide auf dieselbe Stelle im Gedächtnis verweisen, repräsentieren sie genau dasselbe.Der
=
Operator weist Zeigerwerte zu. Das Objekt wird nicht kopiert.Das Klonen von Objekten ist an sich schon eine komplizierte Angelegenheit. Jedes Objekt verfügt über eine clone () -Methode, die Sie möglicherweise verwenden möchten, die jedoch eine flache Kopie erstellt (was im Grunde bedeutet, dass eine Kopie des Objekts der obersten Ebene erstellt wird, aber alle darin enthaltenen Objekte nicht geklont werden). Wenn Sie mit Objektkopien herumspielen möchten, lesen Sie unbedingt Joshua Blochs Effective Java .
quelle
Beginnen wir mit Ihrem zweiten Beispiel:
Diese erste Anweisung weist ein neues Objekt zu
s1
SomeObject s1 = new SomeObject("first");
Wenn Sie die Zuweisung in der zweiten Anweisung (
SomeObject s2 = s1
) vornehmens2
, sagen Sie, dass Sie auf dasselbe Objekts1
zeigen sollen, auf das Sie gerade zeigen, sodass Sie zwei Verweise auf dasselbe Objekt haben.Beachten Sie, dass Sie das nicht dupliziert haben
SomeObject
, sondern dass zwei Variablen nur auf dasselbe Objekt zeigen. Wenn Sie also etwas änderns1
oder änderns2
, ändern Sie tatsächlich dasselbe Objekt (beachten Sie, wenn Sie so etwas getan habens2 = new SomeObject("second")
, würden sie jetzt auf verschiedene Objekte zeigen).In Ihrem ersten Beispiel
a
undb
sind primitive Werte, so modifiziert man die andere nicht beeinflussen.Unter der Haube von Java arbeiten alle Objekte mit Pass-by-Wert. Bei Objekten übergeben Sie den übergebenen "Wert" als Speicherort des Objekts im Speicher (es scheint also einen ähnlichen Effekt der Referenzübergabe zu haben). Grundelemente verhalten sich anders und übergeben nur eine Kopie des Werts.
quelle
Die obigen Antworten erklären das Verhalten, das Sie sehen.
Als Antwort auf "Wie kann man SomeObject duplizieren, wenn eine einfache Zuweisung den Job nicht erledigt?" - Versuchen Sie, nach
cloneable
(es ist eine Java-Oberfläche, die eine Möglichkeit zum Kopieren von Objekten bietet) und 'copy constructors
' (ein alternativer und wohl besserer Ansatz) zu suchen.quelle
Die Objektzuweisung zu einer Referenz klont Ihr Objekt nicht. Referenzen sind wie Zeiger. Sie zeigen auf ein Objekt, und wenn Operationen aufgerufen werden, erfolgt dies für das Objekt, auf das der Zeiger zeigt. In Ihrem Beispiel zeigen s1 und s2 auf dasselbe Objekt, und Setter ändern den Status desselben Objekts, und die Änderungen sind in allen Referenzen sichtbar.
quelle
Ändern Sie Ihre Klasse, um eine neue Referenz zu erstellen, anstatt dieselbe zu verwenden:
public class SomeObject{ public String text; public SomeObject(String text){ this.setText(text); } public String getText(){ return text; } public void setText(String text){ this.text = new String(text); } }
Sie können so etwas verwenden (ich gebe nicht vor, eine ideale Lösung zu haben):
public class SomeObject{ private String text; public SomeObject(String text){ this.text = text; } public SomeObject(SomeObject object) { this.text = new String(object.getText()); } public String getText(){ return text; } public void setText(String text){ this.text = text; } }
Verwendung:
SomeObject s1 = new SomeObject("first"); SomeObject s2 = new SomeObject(s1); s2.setText("second"); System.out.println(s1.getText()); // first System.out.println(s2.getText()); // second
quelle
int a = new Integer(5)
Im obigen Fall wird eine neue Ganzzahl erstellt. Hier ist Integer ein nicht primitiver Typ, und der darin enthaltene Wert wird konvertiert (in ein int) und einem int 'a' zugewiesen.
SomeObject s1 = new SomeObject("first"); SomeObject s2 = s1;
In diesem Fall sind sowohl s1 als auch s2 Referenztypen. Sie sind nicht so erstellt, dass sie einen Wert wie primitive Typen enthalten, sondern enthalten die Referenz eines Objekts. Zum besseren Verständnis können wir Referenz als einen Link betrachten, der angibt, wo ich das Objekt finde, auf das verwiesen wird.
Hier sagt uns die Referenz s1, wo wir den Wert von "first" finden können (der in einer Instanz von SomeObject tatsächlich im Speicher des Computers gespeichert ist). Mit anderen Worten, s1 ist die Adresse des Objekts der Klasse "SomeObject". Durch die folgende Zuordnung -
Wir kopieren nur den in s1 gespeicherten Wert nach s2 und wissen jetzt, dass s1 die Adresse der Zeichenfolge "first" enthält. Nach dieser Zuordnung erzeugen beide println () dieselbe Ausgabe, da sowohl s1 als auch s2 dasselbe Objekt auffrischen.
Zusammen mit dem Kopierkonstruktor können Sie Objekte mit der Methode clone () kopieren, wenn Sie ein Java-Benutzer sind. Der Klon kann auf folgende Weise verwendet werden:
Weitere Informationen zu clone () finden Sie unter http://en.wikipedia.org/wiki/Clone_%28Java_method%29
quelle
Das liegt daran
s1
unds2
funktioniert nur als Referenz auf Ihre Objekte. Bei der Zuweisungs2 = s1
weisen Sie nur die Referenz zu, was bedeutet, dass beide auf dasselbe Objekt im Speicher verweisen (das Objekt mit dem aktuellen Text "first").Wenn Sie jetzt einen Setter ausführen, entweder auf s1 oder s2, ändern beide dasselbe Objekt.
quelle
Wenn Sie einer Variablen ein Objekt zuweisen und ein Objekt zuweisen, weisen Sie diesem Objekt tatsächlich den Verweis zu. Also verweisen beide Variablen
s1
unds2
auf dasselbe Objekt.quelle
Da wurden Objekte durch eine Referenz referenziert. Wenn Sie also s2 = s1 schreiben, wird nur die Referenz kopiert. Es ist wie in C, wo Sie nur mit Speicheradressen umgehen. Wenn Sie die Adresse eines Speichers kopieren und den Wert hinter dieser Adresse ändern, ändern beide Zeiger (Referenzen) den einen Wert an dieser Stelle im Speicher.
quelle
Die zweite Zeile (
SomeObject s2 = s1;
) weist der ersten einfach die zweite Variable zu. Dies führt dazu, dass die zweite Variable auf dieselbe Objektinstanz wie die erste zeigt.quelle