2010-06-28 16 views
80

Ho appena visto il codice simile a questo:Strano Integer boxe in Java

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 correva, questo blocco di codice stamperà fuori:

false 
true 

capisco perché il primo è false: perché i due oggetti sono oggetti separati, quindi lo == confronta i riferimenti. Ma non riesco a capire, perché la seconda affermazione restituisce true? C'è qualche strana regola di autoboxing che interviene quando il valore di un Integer è in un certo intervallo? Cosa sta succedendo qui?

+1

Sembra una vittima di http://stackoverflow.com/questions/1514910/when-comparing-two-integers-in- java-does-auto-unboxing-occorrono –

+2

@ RC - Non è un vero capriccio, ma una situazione simile è discussa. Grazie per il riferimento però. – Joel

+2

questo è orribile. questo è il motivo per cui non ho mai capito il punto di tutto questo primitivo, ma oggetto, ma entrambi, ma auto-inscatolato, ma dipende, ma aaaaaaaaargh. – njzk2

risposta

79

La linea true è attualmente garantita dalle specifiche del linguaggio. Da section 5.1.7:

Se il valore p essere inscatolato è vero, falso, un byte, un char nell'intervallo \ u0000 a \ u007F, o un int o corto numero compreso tra -128 e 127, quindi let r1 e r2 siano i risultati di due conversioni di boxing pari a di p. È 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 è (si veda l'ultimo paragrafo citato qui di seguito):

Idealmente, boxe un dato valore di base p, darebbe sempre un riferimento identico a . In pratica, questo potrebbe non essere fattibile utilizzando le tecniche di implementazione esistenti . Le regole di cui sopra sono un compromesso pragmatico. La clausola finale di cui sopra richiede che determinati valori comuni siano sempre raggruppati in in oggetti indistinguibili. L'implementazione può memorizzarli in cache, con la pigrizia o con impazienza.

Per gli altri valori, questa formulazione non consente alcuna ipotesi circa il identità dei valori incasellati da parte del programmatore. Ciò consentirebbe (ma non richiedere) la condivisione di alcuni o tutti questi riferimenti.

Ciò garantisce che, in più comuni casi, il comportamento sarà quello desiderata, senza imporre una sanzione prestazioni indebite, specialmente su piccoli dispositivi. Ad esempio, meno implementazioni della memoria potrebbero, ad esempio, memorizzare nella cache tutti i caratteri e i cortocircuiti, come come interi e lunghi nell'intervallo di -32K - + 32K.

+16

Potrebbe anche valere la pena notare che l'autoboxing è in realtà solo zucchero sintattico per chiamare il metodo 'valueOf' della classe box (come [' Integer.valueOf (int) '] (http://java.sun.com/javase/ 6/docs/api/java/lang/Integer.html # valueOf% 28int% 29)). Interessante che il JLS definisca esattamente il desugaring di unboxing - usando 'intValue()' et al - ma non il desingaring di boxe. – gustafc

6

Gli oggetti interi in un intervallo (penso che sia compreso tra -128 e 127) vengono memorizzati nella cache e riutilizzati. I numeri interi al di fuori di tale intervallo ottengono ogni volta un nuovo oggetto.

+1

Questo intervallo può essere esteso utilizzando la proprietà 'java.lang.Integer.IntegerCache.high'. Interessante che Long non abbia questa opzione. –

3

La mia ipotesi è che Java conservi una cache di piccoli numeri interi che sono già "in scatola" perché sono così comuni e risparmia un sacco di tempo per riutilizzare un oggetto esistente piuttosto che crearne uno nuovo .

3

Sì, c'è una strana regola di autoboxing che interviene quando i valori si trovano in un determinato intervallo. Quando assegni una costante a una variabile Oggetto, nulla nella definizione della lingua dice che un nuovo oggetto deve essere creato. Può riutilizzare un oggetto esistente dalla cache.

In effetti, la JVM di solito memorizza una cache di piccoli numeri interi per questo scopo, oltre a valori come Boolean.TRUE e Boolean.FALSE.

3

In Java la boxe funziona nell'intervallo tra -128 e 127 per un numero intero. Quando si utilizzano numeri in questo intervallo, è possibile confrontarlo con l'operatore ==. Per gli oggetti interi al di fuori dell'intervallo devi usare gli uguali.

3

Questo è un punto interessante. Nel libro Effective Java si consiglia di sovrascrivere gli uguali per le proprie classi. Inoltre, per controllare l'uguaglianza per due istanze di oggetti di una classe java usa 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)); 
    } 
} 

ritorni:

true 
true 
+0

@Joel ha richiesto molto altro argomento, non l'uguaglianza di interi ma il comportamento di runtime degli oggetti. –

16
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); 
    } 
} 

uscita:

false 
true 

Yep la prima uscita è prodotta per confrontare riferimento; 'a' e 'b' - questi sono due riferimenti diversi. 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é le JVM tenta di risparmiare memoria, quando il Integer cade in un intervallo (da -128 a 127). Al punto 2 non viene creato alcun nuovo riferimento di tipo Integer per "d". Invece di creare un nuovo oggetto per la variabile di riferimento di tipo Integer 'd', viene assegnato solo all'oggetto precedentemente creato referenziato da 'c'. Tutti questi sono fatti da JVM.

Queste regole di salvataggio della memoria non sono solo per Integer. a scopo di risparmio di memoria, due istanze dei seguenti oggetti wrapper (mentre creato attraverso la boxe), saranno sempre == dove i loro valori primitivi sono gli stessi -

  • booleano
  • Byte
  • carattere da \ u0000 a \u007f (7f è 127 in decimale)
  • brevi e numero intero da -128 a
0

In Java 5, è stata introdotta una nuova funzionalità per salvare la memoria e migliorare le prestazioni per i manipoli degli oggetti di tipo Integer. Gli oggetti interi sono memorizzati nella cache internamente e riutilizzati tramite gli stessi oggetti di riferimento.

  1. Questo è valido per i valori interi nella gamma tra -127 e +127 (valore massimo intero).

  2. Questa cache integer funziona solo su autoboxing. Gli oggetti interi saranno non memorizzati nella cache quando vengono creati utilizzando il costruttore.

Per ulteriori dettagli pls passare attraverso sotto il collegamento:

Integer Cache in Detail

0

Se controlliamo il codice sorgente di Integer obeject, troveremo la fonte di valueOf metodo 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é gli oggetti Integer, nell'intervallo da -128 (Integer.low) a 127 (Integer.high), sono gli stessi oggetti di riferimento durante l'autoboxing. E possiamo vedere che esiste una classe IntegerCache che si prende cura dell'array di cache Integer, che è una classe interna statica privata della classe Integer.

V'è un altro esempio interessante può aiutarci a capire questa strana situazione:

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  

}