Непоследовательное поведение на java ==

Рассмотрим этот код:

class test {
   public static void main(String[] args) {
      test inst_test = new test();
      int i1 = 2000;
      int i2 = 2000;
      int i3 = 2;
      int i4 = 2;
      Integer Ithree = new Integer(2); // 1
      Integer Ifour = new Integer(2); // 2
      System.out.println( Ithree == Ifour );
      inst_test.method( i3 , i4 );
      inst_test.method( i1 , i2 );
   }
   public void method( Integer i , Integer eye ) {
      System.out.println(i == eye );
   }
}

Он печатает:

false
true
false

Я понимаю первый false, оператор == только проверяет, работают ли две ссылки на один и тот же объект, что в данном случае не так.

Следующие true и false заставили меня почесать голову. Почему Java считает i3 и i4 равными, а i1 и i2 разными? Оба были преобразованы в Integer, разве оба не должны оцениваться как ложные? Есть ли практическая причина такого несоответствия?


person andandandand    schedule 18.07.2009    source источник
comment
Думаю, FindBugs сообщит вам о вашей ошибке.   -  person Tom Hawtin - tackline    schedule 19.07.2009


Ответы (7)


Автоупаковка примитивов в объекты (как используется в ваших вызовах method, использует кеш небольших значений. #190697" rel="noreferrer">раздел 5.1.7 Спецификации языка Java:

Если упаковываемое значение p равно true, false, byte, char в диапазоне от до , или int или короткое число между -128 и 127, то пусть r1 и r2 будут результатами любых двух преобразований упаковки с. Всегда так, что r1 == r2.

Не менее интересна и дискуссионная часть спецификации, следующая за ней. Примечательно, что JVM может кэшировать больше значений, если захочет - вы не можете быть уверены в результатах выполнения:

Integer i1 = 129;
Integer i2 = 129;
boolean b = (i1 == i2);
person Jon Skeet    schedule 18.07.2009
comment
Это какая-то очень злая логика - я думаю, это сделано из соображений производительности? - person Michael Stum; 19.07.2009
comment
Действительно ли в этом есть польза? Я думаю, что это запутанное дизайнерское решение. - person andandandand; 19.07.2009
comment
6u14 (и различные предыдущие p-варианты) имеют возможность увеличить интернированный диапазон. Это важно для определенных тестов и стилей программирования. - person Tom Hawtin - tackline; 19.07.2009
comment
Мораль этой истории должна заключаться в том, что при сравнении объектов equals() всегда подходит, даже для оберток для примитивных типов. - person sjlee; 19.07.2009

При автоупаковке целые числа от -128 до 127 кэшируются, и возвращается тот же объект-оболочка. То же самое с логическими значениями и значениями char между и 

Это то, что вы получаете большую часть времени, однако это зависит от реализации JVM.

person amorfis    schedule 18.07.2009
comment
Раньше я думал, что это тоже зависит от JVM, но на самом деле это указано в спецификации. - person Jon Skeet; 19.07.2009
comment
(Вернее, это указано для этих значений, но не для других.) - person Jon Skeet; 19.07.2009

Это связано с тем, что в боксе целые числа ниже определенного значения (я думаю, 128) относятся к какому-то предварительно сконструированному объекту, а более высокие значения — к новым объектам.

person Jorn    schedule 18.07.2009

Autoboxing использует Integer.valueOf(i), а не new Integer(i), для создания объекта класса Integer.

Как уже говорили другие, valueOf() использует кеш, в основном для эффективности использования пространства.

Не используйте == для ссылочных типов, это почти всегда ошибка.

person starblue    schedule 19.07.2009

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

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

(код от солнца JDK 1.6)

это похоже на интернирование строк, так как оно экономит память и позволяет проверять равенство с использованием ссылки (например, == вместо equals)

person dfa    schedule 18.07.2009

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

Просто не забывайте никогда не использовать == для объектов, вы никогда не знаете, что произойдет.

person Janusz    schedule 18.07.2009
comment
да, как уже говорили некоторые, не используйте ==, если только не ищутся конкретные эффекты, которые подкреплены тем, что происходит, и обычно используются: Boolean booLean = Boolean.valueOf(i.intValue() == eye.intValue()); System.out.println(booLean.toString()); - person Nicholas Jordan; 01.10.2009

Автобокс использует некоторый механизм кэширования. Обычно вы никогда не должны полагаться на ==, всегда используйте equals для проверки равенства.

person fastcodejava    schedule 28.01.2010