Странный Целочисленный бокс в Java - PullRequest
105 голосов
/ 28 июня 2010

Я только что видел код, подобный этому:

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? Есть ли какое-то странное правило автобокса, которое срабатывает, когда значение Integer находится в определенном диапазоне? Что здесь происходит?

Ответы [ 10 ]

96 голосов
/ 28 июня 2010

Строка true фактически гарантируется спецификацией языка.Из , раздел 5.1.7 :

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

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

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

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

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

25 голосов
/ 05 апреля 2014
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

Да, первый выход создается для сравнения эталона;«а» и «б» - это две разные ссылки.В точке 1 фактически создаются две ссылки, которые похожи на -

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

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

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

  • Boolean
  • Byte
  • Символ от \ u0000 до \u007f (7f - 127 в десятичной дроби)
  • Короткое и целое число от -128 до 127
8 голосов
/ 28 июня 2010

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

4 голосов
/ 17 июня 2013

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

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

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

3 голосов
/ 28 июня 2010

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

3 голосов
/ 28 июня 2010

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

1 голос
/ 26 ноября 2018

Прямое присвоение литерала int ссылке на Integer является примером автобокса, в котором компилятор обрабатывает литеральное значение в код преобразования объекта.

Таким образом, на этапе компиляции компилятор преобразует Integer a = 1000, b = 1000; в Integer a = Integer.valueOf(1000), b = Integer.valueOf(1000);.

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

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

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

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

Кэш инициализируется при первом использовании, когда класс загружается в память из-за статического блока. Максимальный диапазон кэша можно контролировать с помощью опции -XX:AutoBoxCacheMax JVM.

Это поведение кэширования не применимо только для объектов Integer, аналогично Integer.IntegerCache, у нас также есть ByteCache, ShortCache, LongCache, CharacterCache для Byte, Short, Long, Character соответственно.

Вы можете прочитать больше в моей статье Java Integer Cache - почему Integer.valueOf (127) == Integer.valueOf (127) True .

0 голосов
/ 17 ноября 2016

Если мы проверим исходный код объекта 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 (Integer.high), являются одними и теми же ссылочными объектами во время автобоксирования. И мы видим, что класс 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    

}
0 голосов
/ 02 июня 2016

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

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

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

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

Кэш целых чисел в деталях

...