poner - Weird Entero boxeo en Java
setbounds java (9)
Acabo de ver un código similar a esto:
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);
}
}
Cuando se ejecuta, este bloque de código se imprimirá:
false
true
Entiendo por qué el primero es false
: porque los dos objetos son objetos separados, por lo que ==
compara las referencias. Pero no puedo entender, ¿por qué la segunda afirmación es true
? ¿Hay alguna regla extraña de autoboxing que se activa cuando el valor de un entero está en un cierto rango? ¿Que está pasando aqui?
En Java 5, se introdujo una nueva característica para guardar la memoria y mejorar el rendimiento de las manipulaciones de objetos de tipo entero. Los objetos enteros se almacenan en caché internamente y se reutilizan a través de los mismos objetos referenciados.
Esto es aplicable para valores enteros en el rango entre -127 a +127 (valor entero máximo).
Este almacenamiento en caché entero solo funciona en autoboxing. Los objetos enteros no se almacenarán en caché cuando se generen utilizando el constructor.
Para obtener más detalles, por favor diríjase a Link:
En Java, el boxeo funciona en el rango entre -128 y 127 para un Entero. Cuando usa números en este rango, puede compararlos con el operador ==. Para objetos enteros fuera del rango, debe usar equals.
Ese es un punto interesante. En el libro Effective Java sugiere siempre anular iguales para sus propias clases. Además, para verificar la igualdad de dos instancias de objeto de una clase java, siempre use el método 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));
}
}
devoluciones:
true
true
La true
línea está garantizada por la especificación del idioma. De la sección 5.1.7 :
Si el valor p que está siendo encasillado es verdadero, falso, un byte, un carácter entre el rango / u0000 a / u007f, o un número entero o corto entre -128 y 127, entonces r1 y r2 serán los resultados de dos conversiones de boxeo de p. Siempre es el caso que r1 == r2.
La discusión continúa, sugiriendo que aunque su segunda línea de salida está garantizada, la primera no lo es (vea el último párrafo citado a continuación):
Idealmente, encajonar un valor primitivo p dado, siempre arrojaría una referencia idéntica. En la práctica, esto puede no ser factible utilizando las técnicas de implementación existentes. Las reglas anteriores son un compromiso pragmático. La cláusula final anterior requiere que ciertos valores comunes siempre estén enmarcados en objetos indistinguibles. La implementación puede almacenarlos en caché, de forma perezosa o ansiosa.
Para otros valores, esta formulación no permite ninguna suposición sobre la identidad de los valores encuadrados por parte del programador. Esto permitiría (pero no requeriría) compartir algunas o todas estas referencias.
Esto garantiza que, en la mayoría de los casos, el comportamiento será el deseado, sin imponer una penalización de rendimiento excesiva, especialmente en dispositivos pequeños. Las implementaciones menos limitadas de memoria podrían, por ejemplo, almacenar en caché todos los caracteres y cortos, así como enteros y largos en el rango de -32K - + 32K.
Los objetos enteros en algún rango (creo que tal vez -128 a 127) se almacenan en caché y se vuelven a usar. Los enteros fuera de ese rango obtienen un nuevo objeto cada vez.
Sí, hay una regla extraña de autoboxing que se activa cuando los valores están en un cierto rango. Cuando asigna una constante a una variable Object, nada en la definición del lenguaje dice que se debe crear un nuevo objeto. Puede reutilizar un objeto existente de la memoria caché.
De hecho, la JVM generalmente almacenará un caché de enteros pequeños para este propósito, así como también valores como Boolean.TRUE y Boolean.FALSE.
Si comprobamos el código fuente de Integer
obeject, encontraremos la fuente del método valueOf
así:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
lo que puede explicar por qué los objetos Integer
, que en el rango de -128 ( Integer.low
) a 127 ( Integer.high
), son los mismos objetos referenciados durante el autoboxing. Y podemos ver que hay una clase IntegerCache
se encarga de la matriz de caché Integer
, que es una clase interna estática privada de la clase Integer
.
Hay otro ejemplo interesante que puede ayudarnos a entender esta extraña situación:
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
}
Supongo que Java mantiene un caché de enteros pequeños que ya están ''encuadrados'' porque son muy comunes y ahorra mucho tiempo reutilizar un objeto existente que crear uno nuevo.
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);
}
}
Salida:
false
true
Sí, el primer resultado se produce para comparar la referencia; ''a'' y ''b'' - estas son dos referencias diferentes. En el punto 1, en realidad se crean dos referencias que son similares a:
Integer a = new Integer(1000);
Integer b = new Integer(1000);
El segundo resultado se produce porque la JVM
intenta guardar la memoria, cuando el Integer
cae en un rango (de -128 a 127). En el punto 2, no se crea una nueva referencia de tipo entero para ''d''. En lugar de crear un nuevo objeto para la variable de referencia tipo ''Integer'', solo se le asignó un objeto previamente creado al que se hace referencia con ''c''. Todos estos son hechos por JVM
.
Estas reglas de ahorro de memoria no son solo para Integer. para fines de ahorro de memoria, dos instancias de los siguientes objetos de contenedor (mientras se crean a través del boxeo), siempre serán == donde sus valores primitivos son los mismos:
- Booleano
- Byte
- Carácter desde / u0000 hasta
/u007f
(7f es 127 en decimal) - Corto y entero de -128 a 127