Ist Integer unveränderlich

100

Ich weiß, dass dies wahrscheinlich sehr dumm ist, aber viele Orte behaupten, dass die Integer-Klasse in Java unveränderlich ist, aber der folgende Code:

Integer a=3;
Integer b=3;
a+=b;
System.out.println(a);

Kann problemlos ausgeführt werden, um das (erwartete) Ergebnis 6 zu erhalten. Der Wert von a hat sich also effektiv geändert. Bedeutet das nicht, dass Integer veränderlich ist? Sekundäre Frage und ein wenig offtopic: "Unveränderliche Klassen brauchen keine Kopierkonstruktoren". Möchte jemand erklären warum?

K.Steff
quelle
12
Die Klasse ist unveränderlich, aber Autoboxing macht funky Dinge möglich: stackoverflow.com/questions/3085332/…
wkl
Danke, Boxen war das Schlüsselwort, das ich brauchte, um zu googeln :)
K.Steff
7
Sie verwechseln unveränderlich mit einem endgültigen oder konstanten Wert.
Code Enthusiastic

Antworten:

94

Unveränderlich bedeutet nicht, dass aniemals ein anderer Wert erreicht werden kann. Ist zum Beispiel auch Stringunveränderlich, aber ich kann das trotzdem tun:

String str = "hello";
// str equals "hello"
str = str + "world";
// now str equals "helloworld"

strwurde nicht geändert, sondern strist jetzt ein völlig neu instanziiertes Objekt, so wie es Integerist. Der Wert von amutierte also nicht, sondern wurde durch ein völlig neues Objekt ersetzt, d new Integer(6). H.

Travis Webb
quelle
14
"Dies liegt daran, dass str jetzt ein vollständig neu instanziiertes Objekt ist". Oder vielmehr, str (a Varibale) Punkte auf ein neues Objekt. Das Objekt selbst ist nicht veränderbar, aber da die Variable nicht endgültig ist, kann sie auf ein anderes Objekt verweisen.
Sandman
Ja, es zeigt auf ein anderes Objekt, das als Ergebnis der +=Operation instanziiert wurde .
Travis Webb
11
Streng genommen ist es muss nicht sein , neues Objekt. Boxing verwendet Integer.valueOf(int)und diese Methode verwaltet einen Cache von IntegerObjekten. Das Ergebnis +=einer IntegerVariablen könnte also ein Objekt sein, das zuvor existiert hat (oder es könnte sogar dasselbe Objekt sein ... im Fall von a += 0).
Stephen C
1
Warum sagt JavaDoc for String explizit, dass es unveränderlich ist, JavaDoc for Integer jedoch nicht? Dieser Unterschied ist, warum ich diese Frage
lese
50

aist ein "Verweis" auf eine Ganzzahl (3), Ihre Kurzschrift a+=bbedeutet wirklich, dies zu tun:

a = new Integer(3 + 3)

Also nein, Ganzzahlen sind nicht veränderbar, aber die Variablen, die auf sie zeigen, sind *.

* Es ist möglich, unveränderliche Variablen zu haben. Diese werden mit dem Schlüsselwort gekennzeichnet final, was bedeutet, dass sich die Referenz möglicherweise nicht ändert.

final Integer a = 3;
final Integer b = 3;
a += b; // compile error, the variable `a` is immutable, too.
Mark Elliot
quelle
18

Sie können feststellen, dass sich das Objekt mithilfe von geändert hat System.identityHashCode()(Ein besserer Weg ist die Verwendung von plain, es ist ==jedoch nicht so offensichtlich, dass sich die Referenz und nicht der Wert geändert hat).

Integer a = 3;
System.out.println("before a +=3; a="+a+" id="+Integer.toHexString(System.identityHashCode(a)));
a += 3;
System.out.println("after a +=3; a="+a+" id="+Integer.toHexString(System.identityHashCode(a)));

druckt

before a +=3; a=3 id=70f9f9d8
after a +=3; a=6 id=2b820dda

Sie können sehen, dass sich die zugrunde liegende "ID" des Objekts, aauf das verwiesen wird, geändert hat.

Peter Lawrey
quelle
System.identityHashCode () ist ein sehr guter Tipp. Danke dafür.
Ad Infinitum
10

Auf die erste Frage gestellt,

Integer a=3;
Integer b=3;
a+=b;
System.out.println(a);

Die Ganzzahl ist unveränderlich. Was oben passiert ist, ist, dass 'a' in eine neue Referenz von Wert 6 geändert wurde. Der Anfangswert 3 hat keine Referenz im Speicher (er wurde nicht geändert), sodass er mit Müll gesammelt werden kann.

Wenn dies bei einem String passiert, bleibt er länger im Pool (im PermGen-Bereich) als die Ganzzahlen, da er Referenzen erwartet.

Harsha
quelle
8

Ja Integer ist unveränderlich.

A ist eine Referenz, die auf ein Objekt zeigt. Wenn Sie ein + = 3 ausführen, wird A neu zugewiesen, um auf ein neues Integer-Objekt mit einem anderen Wert zu verweisen.

Sie haben das ursprüngliche Objekt nie geändert, sondern den Verweis auf ein anderes Objekt gerichtet.

Lesen Sie mehr über den Unterschied zwischen Objekten und Referenzen hier .

Andy
quelle
Einfach und leicht in der Sprache eines Laien gesagt, mit allen anderen komplexen Erklärungen da draußen :)
Roshan Fernando
5

Unveränderlich bedeutet nicht, dass Sie den Wert für eine Variable nicht ändern können. Es bedeutet nur, dass jede neue Zuweisung ein neues Objekt erstellt (ihm einen neuen Speicherort zuweist) und ihm dann der Wert zugewiesen wird.

Um dies selbst zu verstehen, führen Sie eine Ganzzahlzuweisung in einer Schleife durch (wobei die Ganzzahl außerhalb der Schleife deklariert ist) und sehen Sie sich die Live-Objekte im Speicher an.

Der Grund, warum der Kopierkonstruktor für unveränderliche Objekte nicht benötigt wird, ist der einfache gesunde Menschenverstand. Da bei jeder Zuweisung ein neues Objekt erstellt wird, erstellt die Sprache technisch bereits eine Kopie, sodass Sie keine weitere Kopie erstellen müssen.

uncaught_exceptions
quelle
2

"Unveränderliche Klassen benötigen keine Kopierkonstruktoren". Möchte jemand erklären warum?

Der Grund ist, dass es selten notwendig ist, eine Instanz einer unveränderlichen Klasse zu kopieren (oder sogar zu kopieren). Die Kopie des Objekts sollte "dieselbe" wie das Original sein, und wenn es dieselbe ist, sollte es nicht erforderlich sein, es zu erstellen.

Es gibt jedoch einige zugrunde liegende Annahmen:

  • Es wird davon ausgegangen, dass Ihre Anwendung der Objektidentität von Instanzen der Klasse keine Bedeutung beimisst.

  • Es wird davon ausgegangen, dass die Klasse überladen ist equalsund hashCodedass eine Kopie einer Instanz nach diesen Methoden "dieselbe" wie das Original ist.

Eine oder beide dieser Annahmen könnten falsch sein, und dies könnte die Hinzufügung eines Kopierkonstruktors rechtfertigen.

Stephen C.
quelle
1

So verstehe ich unveränderlich

int a=3;    
int b=a;
b=b+5;
System.out.println(a); //this returns 3
System.out.println(b); //this returns 8

Wenn int mutieren könnte, würde "a" 8 ausgeben, aber nicht, weil es unveränderlich ist, deshalb ist es 3. Ihr Beispiel ist nur eine neue Zuweisung.

Ndheti
quelle
0

Ich kann klarstellen, dass Integer (und andere seiner Glaubensbekenntnisse wie Float, Short usw.) durch einfachen Beispielcode unveränderlich sind:

Beispielcode

public class Test{
    public static void main(String... args){
        Integer i = 100;
        StringBuilder sb = new StringBuilder("Hi");
        Test c = new Test();
        c.doInteger(i);
        c.doStringBuilder(sb);
        System.out.println(sb.append(i)); //Expected result if Integer is mutable is Hi there 1000
    }

    private void doInteger(Integer i){
        i=1000;
    }

    private void doStringBuilder(StringBuilder sb){
        sb.append(" there");
    }

}

Tatsächliche Ergebnis

Das Ergebnis ist Hi There 100 anstelle des erwarteten Ergebnisses (wenn sb und i veränderbare Objekte sind) Hi There 1000

Dies zeigt, dass das von i in main erstellte Objekt nicht geändert wird, während sb geändert wird.

StringBuilder zeigte also veränderliches Verhalten, aber keine Ganzzahl.

Integer ist also unveränderlich. Daher bewiesen

Ein weiterer Code ohne Integer:

public class Test{
    public static void main(String... args){
        Integer i = 100;
        Test c = new Test();
        c.doInteger(i);
        System.out.println(i); //Expected result is 1000 in case Integer is mutable
    }

    private void doInteger(Integer i){
        i=1000;
    }


}
Ashutosh Nigam
quelle
Sie machen zwei verschiedene Dinge - versuchen, die Ganzzahl neu zuzuweisen und eine Methode für den Stringbuilder aufzurufen. Wenn Sie das tun , private void doStringBuilder(StringBuilder sb){ sb = new StringBuilder(); }dann sbist unverändert.
MT0
Ich habe StringBuilder (der veränderbar ist) hinzugefügt, um Integer einem anderen veränderbaren Objekt gegenüberzustellen. Wenn Sie möchten, können Sie den gesamten StringBuilder-Code löschen und einfach i ausdrucken, um 100 zu sehen.
Ashutosh Nigam
Dies beweist keine Unveränderlichkeit. Alles, was Sie tun, ist ein erneutes Hashing dieses Beispiels, das zeigt, dass Java Pass-by-Value verwendet (und dass die für Objekte übergebenen Werte Zeiger sind).
MT0
Versuchen Sieprivate void doInteger(Integer i){ System.out.println( i == 100 ); i=1000; System.out.println( i == 100 ); }
MT0
@ MT0 Wenn Sie den Wert übergeben, zeigt StringBuilder immer noch auf dasselbe Objekt, aber Integer übergibt eine neue Kopie, die nicht auf dasselbe Objekt verweist. Wenn Sie in doInteger ausdrucken, zeigen Sie die Kopie der Funktion an, nicht die Hauptfunktion. Wir wollen sehen, ob das Objekt, auf das i in main zeigt, dasselbe ist oder nicht. Hoffe, es klärt die Konzepte :) Auch StringBuilder unveränderliche Version ist String. Lassen Sie mich wissen, ob ich eine Probe dafür teilen soll.
Ashutosh Nigam
-1
public static void main(String[] args) {
    // TODO Auto-generated method stub

    String s1="Hi";
    String s2=s1;

    s1="Bye";

    System.out.println(s2); //Hi  (if String was mutable output would be: Bye)
    System.out.println(s1); //Bye

    Integer i=1000;
    Integer i2=i;

    i=5000;

    System.out.println(i2); // 1000
    System.out.println(i); // 5000

    int j=1000;
    int j2=j;

    j=5000;

    System.out.println(j2); // 1000
    System.out.println(j); //  5000


    char c='a';
    char b=c;

    c='d';

    System.out.println(c); // d
    System.out.println(b); // a
}

Ausgabe ist:

Hallo Tschüss 1000 5000 1000 5000 d a

Char ist also veränderlich, String Integer und int sind unveränderlich.

özgür arslan
quelle
1
Diese Antwort enthält keine Informationen von den anderen.
Giulio Caccin