Warum ist 128 == 128 falsch, aber 127 == 127 ist wahr, wenn Integer-Wrapper in Java verglichen werden?

172
class D {
    public static void main(String args[]) {
        Integer b2=128;
        Integer b3=128;
        System.out.println(b2==b3);
    }
}

Ausgabe:

false

class D {
    public static void main(String args[]) {
        Integer b2=127;
        Integer b3=127;
        System.out.println(b2==b3);
    }
}

Ausgabe:

true

Hinweis: Zahlen zwischen -128 und 127 sind wahr.

vipin k.
quelle
10
Möglicherweise finden Sie bexhuff.com/2006/11/java-1-5-autoboxing-wackyness informativ.
Dominic Rodger
1
Wie sind Sie zu dem Punkt gekommen, diese Frage zu stellen? es macht wirklich Spaß, aber so etwas "in der realen Welt" kommt man nie vor ... oder?
Mare Infinitus

Antworten:

217

Wenn Sie ein Zahlenliteral in Java kompilieren und es einer Ganzzahl (Großbuchstabe I) zuweisen, gibt der Compiler Folgendes aus:

Integer b2 =Integer.valueOf(127)

Diese Codezeile wird auch generiert, wenn Sie Autoboxing verwenden.

valueOf wird so implementiert, dass bestimmte Zahlen "gepoolt" werden, und es wird dieselbe Instanz für Werte kleiner als 128 zurückgegeben.

Aus dem Java 1.6-Quellcode, Zeile 621:

public static Integer valueOf(int i) {
    if(i >= -128 && i <= IntegerCache.high)
        return IntegerCache.cache[i + 128];
    else
        return new Integer(i);
}

Der Wert von highkann mit der Systemeigenschaft auf einen anderen Wert konfiguriert werden.

-Djava.lang.Integer.IntegerCache.high = 999

Wenn Sie Ihr Programm mit dieser Systemeigenschaft ausführen, wird true ausgegeben!

Die offensichtliche Schlussfolgerung: Verlassen Sie sich niemals darauf, dass zwei Referenzen identisch sind, sondern vergleichen Sie sie immer mit der .equals()Methode.

Also b2.equals(b3)wird true für alle logisch gleichen Werte von b2, b3 gedruckt.

Beachten Sie, dass der IntegerCache aus Leistungsgründen nicht vorhanden ist, sondern der JLS entspricht, Abschnitt 5.1.7 ; Die Objektidentität muss für die Werte -128 bis einschließlich 127 angegeben werden.

Die Ganzzahl # valueOf (int) dokumentiert auch dieses Verhalten:

Diese Methode führt wahrscheinlich zu einer deutlich besseren räumlichen und zeitlichen Leistung, indem häufig angeforderte Werte zwischengespeichert werden. Diese Methode speichert immer Werte im Bereich von -128 bis einschließlich 127 zwischen und kann andere Werte außerhalb dieses Bereichs zwischenspeichern.

Andreas Petersson
quelle
1
Beachten Sie, dass Werte kleiner als 127 von Java ignoriert werden und Werte größer als Integer.MAX_VALUE-128 begrenzt werden.
Andreas Petersson
Ganzzahlen werden in Java 5 und höher für Bytewerte zwischengespeichert, wodurch eine neue Ganzzahl (1) == eine neue Ganzzahl (1) entsteht. Dies ist jedoch in Java 1.4 oder niedriger nicht der Fall. Seien Sie also vorsichtig, wenn Sie eventuell ein Downgrade auf diese Umgebung durchführen müssen.
MetroidFan2002
11
Nein, das ist falsch. new Integer (1) == new Integer (1) ist unabhängig von der JVM falsch. AFAIK kein Compiler betrügt das "neue" Schlüsselwort. Es MUSS immer ein neues Objekt instanziieren.
Andreas Petersson
1
@ Holger interessanter Punkt. Aber es ist technisch möglich, die Integer-Klasse aus dem JDK durch ein benutzerdefiniertes Impl zu ersetzen ... (fragen Sie nicht, warum jemand so verrückt wäre) - dann könnte es Nebenwirkungen haben, die nicht wegoptimiert werden dürfen
Andreas Petersson
1
@ AndreasPetersson sicher. "Compiler" bezeichnet den JIT-Compiler, der die tatsächliche Implementierungsklasse genau kennt und möglicherweise nur dann optimiert, wenn der Konstruktor keine Nebenwirkungen hat. Oder optimieren Sie den Ausdruck, um nur die Nebenwirkungen zu reproduzieren, gefolgt von der Verwendung false. Tatsächlich kann dies bereits heute als Nebeneffekt der Anwendung von Escape Analysis und Scalar Replacement auftreten.
Holger
24

Autoboxing-Caches -128 bis 127. Dies ist im JLS ( 5.1.7 ) angegeben.

Wenn der Wert p , der eingerahmt wird, wahr, falsch, ein Byte, ein Zeichen im Bereich von \ u0000 bis \ u007f oder eine int oder kurze Zahl zwischen -128 und 127 ist, dann seien r1 und r2 das Ergebnis von zwei beliebigen Boxumwandlungen von p. Es ist immer so, dass r1 == r2.

Eine einfache Regel, die Sie beim Umgang mit Objekten beachten sollten, lautet: Verwenden .equalsSie diese Option, wenn Sie überprüfen möchten, ob die beiden Objekte "gleich" sind. Verwenden ==Sie diese Option, wenn Sie feststellen möchten, ob sie auf dieselbe Instanz verweisen.

Michael Lloyd Lee mlk
quelle
1
Hinweis: Das JLS wurde in Java 9 geändert. Dies ist jetzt nur für Konstantenausdrücke zur Kompilierungszeit garantiert . Siehe Aktualisierung der akzeptierten Antwort.
Stephen C
9

Die Verwendung primitiver Datentypen, Ints, würde in beiden Fällen die erwartete Ausgabe erzeugen.

Da Sie jedoch Integer-Objekte verwenden, hat der Operator == eine andere Bedeutung.

== prüft im Kontext von Objekten, ob sich die Variablen auf dieselbe Objektreferenz beziehen.

Um den Wert der Objekte zu vergleichen, sollten Sie die Methode equals () verwenden, z

 b2.equals(b1)

Dies zeigt an, ob b2 kleiner als b1, größer als oder gleich ist (überprüfen Sie die API auf Details).

Chrisbunney
quelle
7

Es ist Speicheroptimierung in Java bezogen.

Um Speicherplatz zu sparen, verwendet Java alle Wrapper-Objekte erneut, deren Werte in die folgenden Bereiche fallen:

Alle Booleschen Werte (wahr und falsch)

Alle Bytewerte

Alle Zeichenwerte von \ u0000 bis \ u007f (dh 0 bis 127 in Dezimalzahl)

Alle Short- und Integer-Werte von -128 bis 127.

Entwickler Marius Žilėnas
quelle
3

Werfen Sie einen Blick auf die Integer.java, wenn der Wert zwischen -128 und 127 ist, wird die im Cache - Pool verwenden, so (Integer) 1 == (Integer) 1während(Integer) 222 != (Integer) 222

 /**
 * Returns an {@code Integer} instance representing the specified
 * {@code int} value.  If a new {@code Integer} instance is not
 * required, this method should generally be used in preference to
 * the constructor {@link #Integer(int)}, as this method is likely
 * to yield significantly better space and time performance by
 * caching frequently requested values.
 *
 * This method will always cache values in the range -128 to 127,
 * inclusive, and may cache other values outside of this range.
 *
 * @param  i an {@code int} value.
 * @return an {@code Integer} instance representing {@code i}.
 * @since  1.5
 */
public static Integer valueOf(int i) {
    assert IntegerCache.high >= 127;
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}       
Yanghaogn
quelle
0

Andere Antworten beschreiben, warum die beobachteten Effekte beobachtet werden können, aber das ist für Programmierer wirklich nebensächlich (interessant, aber etwas, das Sie beim Schreiben von tatsächlichem Code vergessen sollten).

Verwenden Sie die equalsMethode, um Integer-Objekte auf Gleichheit zu vergleichen .

Versuchen Sie nicht, Integer-Objekte mit dem Identitätsoperator auf Gleichheit zu vergleichen ==.

Es kann vorkommen, dass einige gleiche Werte identische Objekte sind, aber darauf sollte man sich im Allgemeinen nicht verlassen.

user13463803
quelle
-4

Ich habe Folgendes geschrieben, da dieses Problem nicht nur für Integer spezifisch ist. Mein Fazit ist, dass Sie häufig falsches Verhalten feststellen, wenn Sie die API falsch verwenden. Verwenden Sie es richtig und Sie sollten das richtige Verhalten sehen:

public static void main (String[] args) {
    Byte b1=127;
    Byte b2=127;

    Short s1=127; //incorrect should use Byte
    Short s2=127; //incorrect should use Byte
    Short s3=128;
    Short s4=128;

    Integer i1=127; //incorrect should use Byte
    Integer i2=127; //incorrect should use Byte
    Integer i3=128;
    Integer i4=128;

    Integer i5=32767; //incorrect should use Short
    Integer i6=32767; //incorrect should use Short

    Long l1=127L;           //incorrect should use Byte
    Long l2=127L;           //incorrect should use Byte
    Long l3=13267L;         //incorrect should use Short
    Long l4=32767L;         //incorrect should use Short
    Long l5=2147483647L;    //incorrect should use Integer 
    Long l6=2147483647L;    //incorrect should use Integer
    Long l7=2147483648L;
    Long l8=2147483648L;

    System.out.print(b1==b2); //true  (incorrect) Used API correctly
    System.out.print(s1==s2); //true  (incorrect) Used API incorrectly
    System.out.print(i1==i2); //true  (incorrect) Used API incorrectly
    System.out.print(l1==l2); //true  (incorrect) Used API incorrectly

    System.out.print(s3==s4); //false (correct) Used API correctly
    System.out.print(i3==i4); //false (correct) Used API correctly
    System.out.print(i5==i6); //false (correct) Used API correctly
    System.out.print(l3==l4); //false (correct) Used API correctly
    System.out.print(l7==l8); //false (correct) Used API correctly
    System.out.print(l5==l6); //false (correct) Used API incorrectly

}
der Barkeeper
quelle