Поиск…


Вступление

Autoboxing - это автоматическое преобразование, которое компилятор Java делает между примитивными типами и соответствующими классами обертки объектов. Пример, преобразование int -> Integer, double -> Double ... Если преобразование идет другим путем, это называется распаковкой. Как правило, это используется в коллекциях, которые не могут содержать объекты, отличные от объектов, где необходимы примитивные типы бокса, прежде чем устанавливать их в коллекции.

замечания

Автобоксирование может иметь проблемы с производительностью при частом использовании в вашем коде.

Использование int и Integer взаимозаменяемо

Поскольку вы используете общие типы с классами утилит, вы часто можете обнаружить, что типы номеров не очень полезны при указании в качестве типов объектов, поскольку они не равны их примитивным аналогам.

List<Integer> ints = new ArrayList<Integer>();
Java SE 7
List<Integer> ints = new ArrayList<>();

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

for (int i = 0; i < 10; i++)
    ints.add(i);

ints.add(i); утверждение эквивалентно:

ints.add(Integer.valueOf(i));

И сохраняет свойства из значения Integer#valueOf например, с теми же Integer объектами, которые кешируются JVM, когда он находится в диапазоне кеширования чисел.

Это также относится к:

  • byte и Byte
  • short и Short
  • float и Float
  • double и Double
  • long и Long
  • char и Character
  • boolean и Boolean

Однако следует проявлять осторожность в неоднозначных ситуациях. Рассмотрим следующий код:

List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
ints.add(3);
ints.remove(1); // ints is now [1, 3]

Интерфейс java.util.List содержит как метод remove(int index) (метод интерфейса List ), так и remove(Object o) (метод, унаследованный от java.util.Collection ). В этом случае бокс не происходит и не вызывается remove(int index) .

Еще один пример странного поведения кода Java, вызванного автобоксированием Целые числа со значениями в диапазоне от -128 до 127 :

Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
System.out.println(a == b); // true
System.out.println(c <= d); // true
System.out.println(c >= d); // true
System.out.println(c == d); // false

Это происходит потому, что >= оператор неявно вызывает intValue() который возвращает int while == сравнивает ссылки , а не значения int .

По умолчанию Java кэширует значения в диапазоне [-128, 127] , поэтому оператор == работает, потому что Integers в этом диапазоне ссылаются на одни и те же объекты, если их значения одинаковы. Максимальное значение кэшируемого диапазона можно определить с -XX:AutoBoxCacheMax опции -XX:AutoBoxCacheMax JVM. Итак, если вы запустите программу с помощью -XX:AutoBoxCacheMax=1000 , следующий код напечатает true :

Integer a = 1000;
Integer b = 1000;
System.out.println(a == b); // true

Использование Boolean в выражении if

Из-за автоматического распаковки в операторе if можно использовать Boolean выражение:

Boolean a = Boolean.TRUE;
if (a) { // a gets converted to boolean
    System.out.println("It works!");
}

Это работает while , do while и условие в операторах for .

Обратите внимание, что если Boolean равно null , в преобразовании будет NullPointerException .

Автоматическая распаковка может привести к NullPointerException

Этот код компилирует:

Integer arg = null;
int x = arg;

Но во время выполнения он будет сбой при исключении java.lang.NullPointerException во второй строке.

Проблема в том, что примитивный int не может иметь null значение.

Это минималистический пример, но на практике он часто проявляется в более сложных формах. NullPointerException не очень интуитивно понятное и часто мало помогает в поиске таких ошибок.

Положитесь на автобоксинг и автоматическую распаковку с осторожностью, убедитесь, что значения unboxed не будут иметь null значений во время выполнения.

Память и вычислительные накладные расходы на автобоксинг

Автобоксинг может иметь значительные накладные расходы памяти. Например:

Map<Integer, Integer> square = new HashMap<Integer, Integer>();
for(int i = 256; i < 1024; i++) {
    square.put(i, i * i); // Autoboxing of large integers
}

как правило, потребляют значительный объем памяти (около 60 кб для 6 тыс. фактических данных).

Кроме того, целые числа в штучной упаковке обычно требуют дополнительных округлений в памяти и, таким образом, делают кэширование процессора менее эффективным. В приведенном выше примере доступ к памяти распространяется на пять разных местоположений, которые могут находиться в совершенно разных областях памяти: 1. объект HashMap , 2. объект Entry[] table , 3. объект Entry , 4. элемент ввода key слов (бокс примитивного ключа), 5. объект value entry (бокс примитивного значения).

class Example {
  int primitive; // Stored directly in the class `Example`
  Integer boxed; // Reference to another memory location
}

Чтение в boxed требует двух обращений к памяти, доступ к primitive только один.

При получении данных с этой карты, казалось бы, невинный код

int sumOfSquares = 0;
for(int i = 256; i < 1024; i++) {
    sumOfSquares += square.get(i);
}

эквивалентно:

int sumOfSquares = 0;
for(int i = 256; i < 1024; i++) {
    sumOfSquares += square.get(Integer.valueOf(i)).intValue();
}

Как правило, приведенный выше код вызывает сбор и сбор мусора объекта Integer для каждой операции Map#get(Integer) . (Подробнее см. Примечание ниже).

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

В потоках Java 8 также есть оптимизированные интерфейсы для примитивных типов данных, таких как IntStream которые не требуют бокса.

Примечание: типичная среда выполнения Java поддерживает простой кэш Integer и другой примитивный объект-оболочку, который используется фабричными методами valueOf и автобоксированием. Для Integer диапазон по умолчанию этого кеша составляет от -128 до +127. Некоторые JVM предоставляют параметр командной строки JVM для изменения размера и диапазона кеша.

Различные случаи Когда Integer и int могут использоваться взаимозаменяемо

Случай 1: используется вместо аргументов метода.

Если метод требует, чтобы объект класса-оболочки был аргументом. Затем, в качестве взаимозаменяемого аргумента может быть передана переменная соответствующего примитивного типа и наоборот.

Пример:

int i;
Integer j;
void ex_method(Integer i)//Is a valid statement
void ex_method1(int j)//Is a valid statement

Случай 2: При передаче возвращаемых значений:

Когда метод возвращает примитивную переменную типа, тогда объект соответствующего класса-оболочки может быть передан как возвращаемое значение взаимозаменяемо и наоборот.

Пример:

int i;
Integer j;
int ex_method()
{...
return j;}//Is a valid statement
Integer ex_method1()
{...
return i;//Is a valid statement
}

Случай 3: При выполнении операций.

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

int i=5;
Integer j=new Integer(7);
int k=i+j;//Is a valid statement
Integer m=i+j;//Is also a valid statement

Pitfall : не забудьте инициализировать или присвоить значение объекту класса-оболочки.

При использовании объекта класса-оболочки и примитивной переменной взаимозаменяемо никогда не забывать или пропустить инициализацию или присвоение значения объекту класса-оболочки иначе он может привести к исключению нулевого указателя во время выполнения.

Пример:

public class Test{
    Integer i;
    int j;
    public void met()
    {j=i;//Null pointer exception
    SOP(j);
    SOP(i);}   
    public static void main(String[] args)
    {Test t=new Test();
    t.go();//Null pointer exception
    }

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



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow