2010-06-28 4 views
80

Я только что видел код, подобный этому:Weird Integer бокс в 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); 
    } 
} 

Когда бежал, этот блок кода будет распечатать:

false 
true 

Я понимаю, почему первый является false: потому что два объекта являются отдельными объектами, поэтому == сравнивает ссылки. Но я не могу понять, почему второе заявление возвращается true? Есть ли какое-то странное правило autoboxing, которое срабатывает, когда значение Integer находится в определенном диапазоне? Что тут происходит?

+1

выглядит как простофиля из http://stackoverflow.com/questions/1514910/when-comparing-two-integers-in- java-do-auto-unboxing- – 2010-06-28 05:49:20

+2

@RC - Не совсем обман, но обсуждается аналогичная ситуация. Спасибо за ссылку, хотя. – Joel

+2

это ужасно. вот почему я никогда не понимал смысла всего примитива, но объекта, но и того, и другого, но авто-бокса, но зависит, но aaaaaaaaargh. – njzk2

ответ

79

Линия true на самом деле гарантирована спецификацией языка. От section 5.1.7:

Если значение р будучи в штучной упаковке верно, ложных, байты, в полукоксе в диапазоне \ u0000 к \ u007f, или Int или короткому числа между -128 и 127, то пусть r1 и r2 - результаты любых двух преобразований бокса в на стр. Это всегда случай, когда r1 == r2.

речь идет о том, что говорит, что, хотя ваша вторая линия продукции гарантирована, то первый не (см последнего абзаца цитируемый ниже):

В идеале, бокс данного примитива значения p, всегда будет давать идентичную ссылку . На практике это может оказаться невозможным с использованием существующих методов реализации . Правила выше являются прагматичным компромиссом. Окончательное предложение требует, чтобы определенные общие значения всегда помещались в в неотличимые объекты. Реализация может кэшировать их, лениво или с нетерпением.

Для других значений, эта формулировка запрещает какие-либо предположения о идентичности штучных значений на части программиста. Это позволило бы (но не требовать) совместного использования некоторых или всех этих ссылок.

Это гарантирует, что в наиболее распространенных случаев поведение будет желательно один, без наложения чрезмерного штрафа производительности, особенно на небольших устройствах. Менее ограниченные памятью реализации могут, например, кэшировать все символы и шорты, как , а также целые числа и длины в диапазоне -32K - + 32K.

+16

Можно также отметить, что autoboxing на самом деле является просто синтаксическим сахаром для вызова метода valueOf класса box (например, ['Integer.valueOf (int)'] (http://java.sun.com/javase/ 6/документы/API/Java/языки/Integer.html # valueOf% 28int% 29)). Интересно, что JLS определяет точный unboxing desugaring - используя 'intValue()' и др., Но не бокс-десурагирование. – gustafc

6

Целочисленные объекты в некотором диапазоне (я думаю, возможно, от -128 до 127) получают кэширование и повторное использование. Целые числа за пределами этого диапазона каждый раз получают новый объект.

+1

Этот диапазон может быть расширен с использованием свойства 'java.lang.Integer.IntegerCache.high'. Интересно, что Лонг не имеет этого варианта. –

3

Я полагаю, что Java хранит кэш небольших целых чисел, которые уже «помещены в бокс», потому что они настолько распространены, что экономит много времени для повторного использования существующего объекта, чем для создания нового ,

3

Да, существует странное правило autoboxing, которое срабатывает, когда значения находятся в определенном диапазоне. Когда вы назначаете константу переменной Object, ничто в определении языка не говорит о создании нового объекта должно быть создано. Он может повторно использовать существующий объект из кеша.

На самом деле, JVM обычно будет хранить кэш небольших целых чисел для этой цели, а также такие значения, как Boolean.TRUE и Boolean.FALSE.

3

В Java бокс работает в диапазоне от -128 до 127 для целых чисел. Когда вы используете числа в этом диапазоне, вы можете сравнить их с оператором ==. Для объектов Integer вне диапазона вы должны использовать equals.

3

Это интересный момент. В книге Effective Java предлагается всегда переопределять равные для ваших собственных классов. Кроме того, чтобы проверить равенство для двух экземпляров объекта класса java, всегда используйте метод 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)); 
    } 
} 

возвращается:

true 
true 
+0

@Joel попросил очень другую тему, а не целочисленное равенство, а выполнял поведение среды выполнения. –

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

Выход:

false 
true 

Да первый выход производится для сравнения ссылок; 'a' и 'b' - это две разные ссылки. В пункте 1, на самом деле два ссылки созданы, который похож, как -

Integer a = new Integer(1000); 
Integer b = new Integer(1000); 

Второй выход производится, поскольку JVM пытается сохранить память, когда Integer падает в диапазоне (от -128 до 127). В точке 2 не создается новая ссылка типа Integer для 'd'. Вместо создания нового объекта для ссылочной переменной типа «Ц» типа Integer он присваивается только ранее созданному объекту, на который ссылается «c». Все это делается JVM.

Эти правила сохранения памяти предназначены не только для Integer. для памяти спасительной цели, два экземпляра следующих объектов оберток (в то время как созданные по боксу), всегда будет == где их примитивные значения одинаковы -

  • Boolean
  • Байт
  • символов из \ u0000 к \u007f (7е является 127 в десятичной системе)
  • Short и Integer от -128 к
0

В Java 5 была введена новая функция для сохранения памяти и повышения производительности для объектов объектов типа Integer. Целочисленные объекты кэшируются внутри и повторно используются через те же объекты, на которые ссылаются.

  1. Это применимо для целых значений в диапазоне от -127 до +127 (значения Максимального числа).

  2. Это кэширование Integer работает только при автобоксинге. Целочисленные объекты не будут кэшироваться, если они построены с использованием конструктора.

Для более подробной информации пожалуйста пройти через ссылку ниже:

Integer Cache in Detail

0

Если проверить исходный код Integer obeject, мы найдем источник valueOf метода так же, как это:

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

который может объяснить, почему Integer объектов, которые находятся в диапазоне от -128 (Integer.low) до 127 (0123)), являются теми же ссылочными объектами во время автобоксинга. И мы видим, что существует класс IntegerCache, который заботится о кеш-массиве Integer, который является частным статическим внутренним классом класса Integer.

Существует еще один интересный пример может помочь нам понять эту странную ситуацию:

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  

}