Вы действительно разбираетесь в боксе и распаковке?

Java
Вы действительно разбираетесь в боксе и распаковке?

Что такое бокс и распаковка

С точки зрения программиста на C++, многие термины в Java кажутся величественными, но на самом деле они очень просты, особенно упаковка и распаковка. Давайте посмотрим на определения бокса и распаковки:

Упаковка относится к преобразованию примитивного типа в тип-оболочку, а распаковка относится к преобразованию типа-оболочки в примитивный тип данных.

В Java есть восемь основных типов данных: byte/char/boolean/short/int/long/float/double. В мире Java, где все является объектом, конечно, их типы-обертки объектов Byte/Character/Boolean/Short/Integer/Long/Float/Double также незаменимы. Легче всего выразить их взаимосвязь с помощью графика:

На уровне кода пример кода 1 демонстрации упаковки и распаковки выглядит следующим образом:

public class BoxTest {
    public static void main(String[] args) {
        Long objectLong100 = 100L;
        long baseLong100 = objectLong100;
    }
}

вLong objectLong100 = 100L;Вставьте 100L в тип Long и назначьте его переменной типа объекта objectLong100.long baseLong100 = objectLong100;Объект objectLong100 распаковывается и присваивается переменной базового типа baseLong100.

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

Что такое автобоксинг и распаковка

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

Как видно из байт-кода, код примера 1 переписывается компилятором в следующий вид:

public class BoxTest {
    public static void main(String[] args) {
        Long objectLong100 = Long.valueOf(100L);
        long baseLong100 = objectLong100.longValue();
    }
}

Это один из синтаксических сахаров Java (есть еще много синтаксических сахаров Java, см. "Hollis Original | Если вы не понимаете эти 12 синтаксических сахаров, не говорите, что знаете Java") --- автоматическая упаковка и распаковка, что избавляет программистов от необходимости явного преобразования между примитивными типами и типами-оболочками с помощью кода.

Когда делать автобоксинг и распаковку

Сценарий автобоксинга относительно прост:При присвоении базового типа типу-оболочке назначение здесь не обязательно является символом =/-=/+=, это также может быть вызов методавызовет автобокс. следующее:

public class BoxTest {

    public static void main(String[] args) {
        //= 赋值自动装箱
        Long objectLong = 100L;

        //equals 调用自动装箱
        if (objectLong.equals(100L)) {
            System.out.println("objectLong.equals(100L)");
        }
        
        //add 方法调用自动装箱
        List<Long> objectLongList = new ArrayList<>();
        objectLongList.add(objectLong);
    }
}

Точно так же автоматическая распаковка происходит при назначении типа оболочки базовому типу:

public class BoxTest {

    public static void unpacking(long var) {
        System.out.println("var = " + var);
    }
    
    public static void main(String[] args) {
        Long objectLong = 100L;

        //= 赋值自动拆箱
        long basicLong = objectLong;

        //方法调用自动拆箱
        unpacking(objectLong);
    }
}

Но, в отличие от автоупаковки, автораспаковка также происходит, когда:

1. При выполнении математических операций (+, -, *, /, ++, -- и т. д.) между типами-оболочками или между типами-оболочками и примитивными типами.

2. При выполнении операции сравнения ==, != между типом-оболочкой и базовым типом.

1 из них легко понять, ведь все математические операции (например, операции процессора) могут выполняться только между примитивными типами, а не между объектами. 2 Приведите пример, чтобы кратко проиллюстрировать:

public class BoxTest {

    public static void main(String[] args) {

        Long objectLong = 500L;
        long basicLong = 500L;

        if (objectLong == basicLong) {
            System.out.println("objectLong == basicLong");
        } else {
            System.out.println("objectLong != basicLong");
        }
    }
}

выполнить выводobjectLong == basicLong, который представляет собой сравнение между базовым типом и типом оболочки, происходит операция распаковки, поэтому выходные данные равны.Если вы все еще думаете, что basicLong будет упакован в тип-оболочку и сравнен с objectLong, то вы ошибаетесь..

«Готча» в автобоксинге и распаковке

Автоматический бокс и распаковка — это хорошо, но в них тоже много «подводных камней», так что давайте рассмотрим их по порядку. Взгляните на кусок кода:

public class BoxTest {
    public static void main(String[] args) {

        Long objectLong1 = new Long(10);
        Long objectLong2 = new Long(10);

        Long objectLong11 = 10L;
        Long objectLong22 = 10L;

        Long objectLong5 = 1000L;
        Long objectLong6 = 1000L;

        Long objectLong3 = new Long(10);
        long basicLong = 10L;
        
        if(objectLong1 == objectLong2) {
            System.out.println("objectLong1 == objectLong2");
        } else {
            System.out.println("objectLong1 != objectLong2");
        }

        if(objectLong11 == objectLong22) {
            System.out.println("objectLong11 == objectLong22");
        } else {
            System.out.println("objectLong11 != objectLong22");
        }

        if(objectLong5 == objectLong6) {
            System.out.println("objectLong1 == objectLong2");
        } else {
            System.out.println("objectLong1 != objectLong2");
        }

        if (objectLong3 == basicLong) {
            System.out.println("objectLong3 == basicLong");
        } else {
            System.out.println("objectLong3 != basicLong");
        }
    }
}

Сначала запишите пером то, что, по вашему мнению, получится на выходе, а затем посмотрите на результат на снимке экрана ниже, чтобы убедиться, что ваше «выполнение» правильное.. Результаты приведены ниже:

1. Результат сравнения objectLong1 и objectLong2 не удивителен, потому что это две переменные типа-обертки. Поскольку они не являются одним и тем же объектом, они не равны.

2. Сравнение между objectLong3 и basicLongКогда делать автобоксинг и распаковкуУпомянутый сценарий распаковки также можно быстро понять.

3. Сравнение между objectLong5 и objectLong6 не равно.Большинство людей также могут сделать вывод, что и objectLong5, и objectLong6 автоматически упаковываются в объекты-оболочки, и сравнение == между двумя объектами не равно. Но результат сравнения между objectLong11 и objectLong22 может многих удивить. Итак, давайте взглянем на метод, который вызывается, когда Long упаковывается автоматически.Long.valueOf(long value)Исходный код:

public final class Long extends Number implements Comparable<Long> {
    //......此处省略一堆
    private static class LongCache {
        private LongCache(){}

        static final Long cache[] = new Long[-(-128) + 127 + 1];

        static {
            for(int I = 0; I < cache.length; I++)
                cache[I] = new Long(I - 128);
        }
    }
    //......此处省略一堆
    public static Long valueOf(long l) {
        final int offset = 128;
        if (l >= -128 && l <= 127) { // will cache
            return LongCache.cache[(int)l + offset];
        }
        return new Long(l);
    }
    //......此处省略一堆
}

Можно видеть, что когда значение базового типа для автоупаковки находится между [-128~127], объект, кэшированный в LongCache, возвращается напрямую, а LongCache.cache представляет собой массив, в котором хранится значение -128~ Объект-оболочка для 127, выделяемого при первой загрузке Long.class.Это объясняет, почему 10L автоматически помещается в два одинаковых объекта. И 1000L автобоксируется на два объекта, которые не равны. Первый использует один и тот же объект в кеше в LongCache.cache, а второй создает новый объект каждый раз, когда он автоматически упаковывается..

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

  • Исчерпывающий, такой как Byte/Character/Boolean, объект, который кэширует все возможные значения. Таким образом, все их равнозначные объекты-оболочки сравниваются равными.
  • Неисчерпаемые прерывистые, такие как Short/Integer/Long, кэшируют только объекты с общими значениями в диапазоне, все ониОбъект-оболочка для некоторых одинаковых значенийСравнение между равным иобъекты-оболочки для большинства одинаковых значенийСравнение не равное.
  • Неперечислимые и непрерывные, такие как Float/Double, каждый бокс представляет собой новый объект.

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

Резюме и рекомендации

1. Тип-обертка по сути является объектом.При автобоксинге сначала создается объект в куче, а затем значению в объекте присваивается значение базового типа.

2. Не используйте ==/!= для сравнения типов оболочек, оболочек и примитивов. Пожалуйста, используйте метод равенства.

3. При выполнении математических операций тип-оболочка будет автоматически распакован, а затем будут выполняться операции с базовым типом..

Добро пожаловать, чтобы подписаться на публичный аккаунт