Strano Integer boxe in Java
-
01-10-2019 - |
Domanda
Ho appena codice sega simile a questo:
public class Scratch
{
public static void main(String[] args)
{
Integer a = 1000, b = 1000;
System.out.println(a == b);
Integer c = 100, d = 100;
System.out.println(c == d);
}
}
Quando Ran, questo blocco di codice stamperà:
false
true
capisco perché il primo è false
: perché i due oggetti sono oggetti separati, in modo che il ==
confronta i riferimenti. Ma io non riesco a capire, perché è la seconda istruzione tornando true
? C'è una strana regola autoboxing che calci a quando il valore di un intero è in un certo intervallo? Che cosa sta succedendo qui?
Soluzione
La linea true
è effettivamente garantita dalla specifica del linguaggio. Da href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.7" 5.1.7 :
Se il valore p essere inscatolato è vero, falso, un byte, un char nella gamma \ U0000 a \ u007F, o un int o corto numero compreso tra -128 e 127, poi lasciare r1 e r2 essere i risultati di due qualsiasi conversioni boxe di p. E 'sempre il caso che r1 == r2.
La discussione va avanti, il che suggerisce che anche se la seconda linea di uscita è garantita, la prima non è (vedi l'ultimo paragrafo citato qui di seguito):
Idealmente, la boxe un dato primitivo valore p, sarebbe sempre produrre un riferimento identici. In pratica, questo potrebbe non essere possibile utilizzando esistenti tecniche di implementazione. Le regole sopra sono un compromesso pragmatico. Il clausola finale precedente richiede che certi valori comuni sempre essere inscatolati in oggetti indistinguibili. Il implementazione può memorizzare nella cache questi, pigramente o entusiasmo.
Per altri valori, questa formulazione disabilita qualsiasi ipotesi circa il l'identità dei valori in scatola sul parte del programmatore. Ciò consentirebbe (Ma non richiede) la condivisione di alcuni o di tutti questi riferimenti.
In questo modo, nella maggior parte dei comuni casi, il comportamento sarà il desiderato uno, senza imporre un indebito penalizzazione delle prestazioni, in particolare su piccoli dispositivi. Meno memoria limitata implementazioni potrebbe, per esempio, memorizzare nella cache tutti i personaggi e cortometraggi, come così come numeri interi e long nel gamma di -32K -. + 32K
Altri suggerimenti
public class Scratch
{
public static void main(String[] args)
{
Integer a = 1000, b = 1000; //1
System.out.println(a == b);
Integer c = 100, d = 100; //2
System.out.println(c == d);
}
}
Output:
false
true
Sì la prima uscita è prodotta per confrontare riferimento; 'A' e 'b' - questi sono due diversi di riferimento. Al punto 1, in realtà due riferimenti sono creati, che è simile a -
Integer a = new Integer(1000);
Integer b = new Integer(1000);
La seconda uscita è prodotta perché l'JVM
tenta di risparmiare memoria, quando il Integer
cade in un intervallo (da -128 a 127). Al punto 2 non viene creato nessun nuovo riferimento di tipo Integer per 'd'. Invece di creare un nuovo oggetto per la variabile di riferimento tipo integer 'd', è assegnato soltanto con oggetto creato in precedenza fa riferimento 'c'. Tutti questi sono fatto da JVM
.
Queste regole risparmio di memoria non sono solo per intero. a scopo di risparmio di memoria, due istanze dei seguenti oggetti wrapper (mentre creato attraverso la boxe), sarà sempre == dove i loro valori primitivi sono gli stessi -
- booleana
- Byte
- Carattere da \ u0000 per
\u007f
(7f è 127 in decimale) - Breve e Integer da -128 a 127
Integer oggetti in un certo range (penso che forse -128 a 127) vanno nella cache e riutilizzato. Interi al di fuori di tale intervallo ottenere un nuovo oggetto ogni volta.
Sì, c'è una regola autoboxing strano che calci a quando i valori sono in un certo intervallo. Quando si assegna una costante ad una variabile oggetto, niente nella definizione del linguaggio, dice un nuovo oggetto deve essere creato. Si può riutilizzare un oggetto esistente dalla cache.
In realtà, la JVM solito memorizzare una cache di piccoli numeri interi per questo scopo, come pure valori come Boolean.TRUE e Boolean.FALSE.
Questo è un punto interessante. Nel libro Effective Java suggerisce sempre di sostituzione è uguale per le proprie classi. Inoltre che, per controllare l'uguaglianza per due istanze di oggetti di una classe Java utilizzare sempre il metodo equals.
public class Scratch
{
public static void main(String[] args)
{
Integer a = 1000, b = 1000;
System.out.println(a.equals(b));
Integer c = 100, d = 100;
System.out.println(c.equals(d));
}
}
restituisce:
true
true
La mia ipotesi è che Java mantiene una cache di piccoli numeri interi che sono gia 'in scatola', perché sono così molto comuni e si risparmia un diavolo di un sacco di tempo per il riutilizzo di un oggetto esistente piuttosto che crearne uno nuovo.
In Java i lavori boxe nell'intervallo compreso tra -128 e 127 per i numeri interi. Quando si utilizza i numeri in questo intervallo si può confrontare con l'operatore ==. Per Integer oggetti di fuori del campo si deve utilizzare è uguale.
l'assegnazione diretta di un int letterale ad un riferimento Integer è un esempio di auto-boxing, dove il valore letterale di codice di conversione oggetto viene gestito dal compilatore.
Quindi, durante fase di compilazione del compilatore converte Integer a = 1000, b = 1000;
a Integer a = Integer.valueOf(1000), b = Integer.valueOf(1000);
.
Quindi è il metodo Integer.valueOf()
che in realtà ci dà gli oggetti interi, e se guardiamo il codice sorgente del metodo Integer.valueOf()
possiamo vedere chiaramente le cache metodo interi oggetti nella gamma da -128 a 127 (compreso).
/**
*
* 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) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
Così, invece di creare e restituire nuovi oggetti interi, Integer.valueOf()
il metodo restituisce interi oggetti dal IntegerCache
interno qualora l'int letterale passato è maggiore di -128 e inferiore a 127.
Java memorizza nella cache questi oggetti interi, perché questa serie di numeri interi si abitua molto in giorno per giorno di programmazione che consente di risparmiare indirettamente po 'di memoria.
La cache viene inizializzata al primo utilizzo quando la classe viene caricato in memoria a causa del blocco statico. La portata massima della cache può essere controllato dall'opzione -XX:AutoBoxCacheMax
JVM.
Questo comportamento della cache non è applicabile per oggetti solo numero intero, simile a Integer.IntegerCache abbiamo anche ByteCache, ShortCache, LongCache, CharacterCache
per Byte, Short, Long, Character
rispettivamente.
Si può leggere di più sul mio articolo Java Integer Cache - Perché Integer.valueOf (127) == Integer.valueOf (127) È vero .
In Java 5, una nuova funzionalità è stata introdotta per salvare la memoria e migliorare le prestazioni per gli oggetti di tipo Integer manipolazioni. oggetti interi sono memorizzati nella cache internamente e riutilizzati attraverso gli stessi oggetti di riferimento.
-
Questo è valido per i valori interi nella gamma tra -127 e +127 (Valore massimo intero).
-
Questa caching Integer funziona solo su autoboxing. Integer Objects Non essere memorizzate nella cache quando vengono costruiti utilizzando il costruttore.
Per maggiori dettagli pls passano sotto il collegamento:
Se controlliamo il codice sorgente di Integer
obeject, troveremo la fonte del metodo di valueOf
proprio come questo:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
che può spiegare perché Integer
oggetti, che nell'intervallo da -128 (Integer.low
) a 127 (Integer.high
), sono gli stessi oggetti di riferimento durante l'autoboxing. E possiamo vedere c'è una IntegerCache
di classe si prende cura della matrice di cache Integer
, che è una classe interna statica privata di classe Integer
.
C'è un altro esempio interessante può aiutarci a capire questa situazione strana:
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Class cache = Integer.class.getDeclaredClasses()[0];
Field myCache = cache.getDeclaredField("cache");
myCache.setAccessible(true);
Integer[] newCache = (Integer[]) myCache.get(cache);
newCache[132] = newCache[133];
Integer a = 2;
Integer b = a + a;
System.out.printf("%d + %d = %d", a, a, b); //The output is: 2 + 2 = 5
}