Классы оболочки
Очень часто необходимо создать класс, основное назначение которого содержать в себе какое-то примитивное значение. Например, как мы увидим в следующих занятиях, обобщенные классы и в частности коллекции работают только с объектами. Поэтому, чтобы каждый разработчик не изобретал велосипед, в Java SE уже добавлены такие классы, которые называются оболочки типов (или классы обертки, Wrappers).
К оболочкам типов относятся классы Double , Float , Long , Integer , Short , Byte , Character , Boolean , Void . Для каждого примитивного значения и ключевого слова void есть свой класс двойник. Имя класса, как вы видите, совпадает с именем примитивного значения. Исключения составляют класс Integer (примитивный тип int ) и класс Character (примитивный тип char ). Кроме содержания в себе значения, классы оболочки предоставляют обширный ряд методов, которые мы рассмотрим в этом уроке.
Объекты классов оболочек неизменяемые (immutable). Это значит, что объект не может быть изменен.
Все классы обертки числовых типов имеют переопределенный метод equals(Object) , сравнивающий примитивные значения объектов.
2. Конструкторы оболочек
В следующей таблицы для каждого класса оболочки указан соответствующий примитивный тип и варианты конструкторов. Как вы видите каждый класс имеет два конструктора: один на вход принимает значение соответствующего примитивного значения, а второй — значение типа String. Исключения: класс Character , у которого только один конструктор с аргументом char и класс Float , объявляющий три конструктора — для значения float , String и еще double .
Примитивный тип
Оболочка
Аргументы конструктора
Зачем нужны классы обертки в java
В этом руководстве мы рассмотрим плюсы и минусы использования примитивов в Java и соответствующих им классов-оберток.
1. Типы переменных в Java
В языке Java существует два типа переменных: примитивные, например int и boolean, а также ссылочные типы вроде Integer и Boolean (классы-обертки). Для каждого примитивного типа существует, соответствующий ему ссылочный тип.
Классы-обертки являются неизменяемыми: это означает, что после создания объекта его состояние (значение поля value) не может быть изменено; и задекларированы, как final (от этих классов невозможно наследоваться).
Java автоматически производит преобразования между примитивными типами и их обертками:
Integer j = 1; // autoboxing int i = new Integer(1); // unboxing
Процесс преобразования примитивных типов в ссылочные называется автоупаковкой (autoboxing), а обратный ему — автораспаковкой (unboxing).
2. Плюсы и минусы
Решение, переменные каких типов использовать, основывается на необходимой нам производительности приложения, объеме доступной памяти и значений по умолчанию с которыми мы собираемся работать.
2.1 Использование памяти единичными объектами
В зависимости от реализации виртуальной машины, эти значения могут изменяться. Например, в виртуальной машине Oracle значения типа boolean сопоставляются со значениями 0 и 1 типа int (это связано с тем, что в VM нет инструкций для работы с булевыми значениями) и, как результат, занимают в памяти 32 бита.
Переменные примитивных типов хранятся в стеке, позволяя иметь к ним быстрый доступ.
Ссылочные переменные ссылаются на объекты, которые хранятся в куче, к ним мы имеем более медленный доступ (рекомендуем ознакомиться с понятиями Стек и Куча, для лучшего усвоения материала). Ссылочные типы имеют определенные накладные расходы относительно их примитивных аналогов.
Накладные расходы зависят от реализации конкретной JVM. Здесь мы приведем результаты для 64-х битной виртуальной машины со следующими параметрами:

В результате, один экземпляр ссылочного типа на данной JVM занимает 128 бит. За исключением Long и Double, которые занимают 192 бита:
- Boolean — 128 бит
- Byte — 128 бит
- Short, Character — 128 бит
- Integer, Float — 128 бит
- Long, Double — 192 бита
Обратите внимание, переменная типа Boolean занимает в 128 раз больше места чем соответствующий ей примитив, тогда как Integer занимает памяти как 4 int переменные.
2.2 Использование памяти массивами
Более интересно обстоят дела с объемом памяти который занимают массивы, рассматриваемых нами типов.
При создании массивов с различным количеством элементов для каждого типа, мы получаем график:
который демонстрирует как массивы различных типов потребляют память (m) в зависимости от количества содержащихся элементов (e):
- long, double: m = 128 + 64e
- short, char: m = 128 + 64(e/4)
- byte, boolean: m) = 128 + 64(e/8)
- the rest: m = 128 + 64(e/2)
где значения в скобках округляются до ближайшего меньшего.
Это может показаться неожиданным, но массивы примитивных типов long и double занимают больше памяти чем их классы-обертки Long и Double соответственно.

2.3 Производительность
Производительность Java кода, довольно тонкий вопрос, сильно зависящий от аппаратной части устройства на котором он исполняется, от различных оптимизационных процессов, выполняемых компилятором, от конкретной JVM, а также от других процессов, происходящих в операционной системе.
Мы уже упоминали ранее, что переменные примитивных типов живут в стеке, тогда как ссылочные переменные хранят ссылки на объекты расположенные в куче. Это важное свойство, определяющее скорость доступа к данным.
Чтобы продемонстрировать насколько операции над примитивными типами быстрее чем над их классами-обертками, создадим массив из миллиона элементов в котором все элементы одинаковы, кроме последнего. Затем мы попытаемся найти этот элемент и сравним результаты производительности работы массива переменных примитивных типов с массивом ссылочных переменных.
Мы используем известный инструмент тестирования производительности JMH, а результаты операции суммируем в диаграмме:
Как мы видим, даже выполнение такой простой операции требует значительно больше времени для классов-оберток, чем для их примитивных аналогов.
В случае более трудоемких операций, таких как сложение, умножение или деление разница в скорости может значительно увеличиться.
Классы обертки — Java: Введение в ООП
В Java у каждого примитивного типа есть соответствующий «объектный» тип. Например, для int существует пара в виде Integer . Последний представлен классом и называется классом-оберткой.
// Примитивный тип // Можно так var value = 42; // int // Или так int value = 42; // int // Ссылочный тип // Можно так Integer value = 42; // Integer Integer value = new Integer(42); // Integer var value = new Integer(42); // Integer // Так написать нельзя, // потому что невозможно вывести тип value // null может быть присвоено любому ссылочному типу // var value = null;
То же самое и для всех остальных примитивных типов:
| Примитивный тип | Класс-обертка |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| char | Character |
| boolean | Boolean |
Классы-обертки нужны для нескольких целей, которых по разным причинам невозможно решить с помощью примитивных типов.
Совместимость и единообразие
Многие возможности Java завязаны на то, что типы представлены классами, только в этом случае они смогут быть использованы как параметры методов или их возвращаемое значение. К ним, например, относятся обобщенные типы и как их следствие коллекции. Все это мы рассмотрим в следующих курсах.
// Так написать нельзя // var items = new ArrayList; var items = new ArrayListInteger>;
null
Примитивные типы всегда содержат какое-то осмысленное значение. На практике же, значение может быть не определено. Особенно часто такое происходит, когда данные поступают из внешних источников. Для представления таких данных нужно специальное обозначение и возможность использовать это значение. В Java, как и в большинстве других языков, таким значением является null . Его можно использовать только с классами-обертками.
// Ошибка! int value = null; // Работает Integer value = null;
По этой причине, множество методов ожидает на вход классы-обертки, а не примитивные типы.
Утилиты и значения
Классы-обертки содержат в себе полезные значения и методы, для своих типов. Например, в Integer есть данные о максимальном и минимальном значениях для типа, там же можно найти методы, которые преобразуют другие типы в числа.
var max = Integer.MAX_VALUE; var min = Integer.MIN_VALUE; // Преобразование в число // Возвращает Integer var value = Integer.valueOf("42"); // 42 // Возвращает int var value = Integer.parseInt("42"); // 42
Почему так устроено?
Зачем тогда нужны примитивные типы если есть классы-обертки? Ответ кроется в производительности. Работа с примитивными типами как объектами значительно ухудшает производительность программы. Поэтому по умолчанию создаются объекты именно примитивных типов.
// Тип: boolean var hasErrors = true;
Боксинг и анбоксинг (Boxing and Unboxing)
По логике, такая система должна приводить к постоянному конвертированию типов в коде, так как где-то создаются примитивные типы, где-то нужны классы-обертки и наоборот. К счастью, этого почти никогда не происходит благодаря механизму автобоксинга (Autoboxing). Тип преобразуется автоматически в тот момент, когда это нужно.
int value = 42; // int -> Integer Integer wrappedInt = value; // Autoboxing // Integer -> int unboxedInt = wrappedInt; // Auto-unboxing
Открыть доступ
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
- 130 курсов, 2000+ часов теории
- 1000 практических заданий в браузере
- 360 000 студентов
Наши выпускники работают в компаниях:
Классы-обертки
У многих мог возникнуть вопрос: зачем задавать целочисленную переменную не int , а Integer ? Часто классы-обертки используют для того, чтобы сохранять данные в коллекции. Все дело в том, что коллекции — это набор объектов, и для того, чтобы оперировать примитивными типами как объектами, и были придуманы классы обертки.
Рассмотрим классы-обертки для примитивных типов данных.
Данные классы имеют название, которое происходит от названия примитивного типа данных, который они представляют: Double, Float, Long, Integer, Short, Byte, Character, Boolean .

Данные классы очень напоминают класс String . Объект обертку для примитивного типа можно создать как явно (используя конструктор), так и не явно (прямым присвоением примитива классу обертке) с помощью оператора = либо при передаче примитива в параметры метода (типа «класса-обертки»). Последнее еще называют автоупаковка (autoboxing).
public class AutoBoxing < public static void main(String[] args)< Integer a = new Integer(777); //явное создание переменной обертки int b = a; //неявное создание. > >
Как и все объекты, переменные, созданные с помощью классов-оберток, будут храниться в куче ( heap ). Но, есть одно важное замечание, о котором часто любят спрашивать в тестах или на собеседованиях. Оно касается целочисленных классов оберток. Неявное создание таких переменных со значением в диапазоне -128 +127 будут храниться в пуле int-ов (В Java есть пул ( pool ) целых чисел в промежутке [-128;127]. Т.е. если мы создаем Integer в этом промежутке, то вместо того, чтобы каждый раз создавать новый объект, JVM берет их из пула). Потому такие обертки с одинаковыми значениями будут являться ссылками на один объект.

Автоупаковка переменных примитивных типов требует точного соответствия типа исходного примитива типу «класса-обертки». Попытка автоупаковать переменную типа byte в Short , без предварительного явного приведения byte->short вызовет ошибку компиляции.
public class AutoBoxing < public static void main(String[] args)< Integer integerA = new Integer(23); Integer integerB = new Integer(777); integerB = new Integer(888); System.out.println("integerA = " + integerA); Integer integerC = new Integer(666); Integer integerD = new Integer(2412); integerD = new Integer(1991); System.out.println("integerD = " + integerD); > >
Результат работы программы:
integerA = 23 integerD = 1991
Классы-обертки удобны еще тем, что они предоставляют методы для работы с соответствующими типами данных.
Давайте посмотрим на самые популярные:
public class AutoBoxing < public static void main(String[] args)< String a = "45"; double b = Double.parseDouble(a); // наверное, самый популярный метод перевод из строки в целочисленный или дробный тип int c = Integer.parseInt(a); System.out.println(b); System.out.println(c); System.out.println(Integer.MAX_VALUE); // константа максимального значения System.out.println(Integer.bitCount(78)); // представляет число в двоичном виде System.out.println(Float.valueOf("80")); // возвращает целочисленный объект, содержащий значение указанного типа System.out.println(Integer.valueOf("444", 16)); // возвращает целочисленный объект, содержащий целое значение указанного строкового представления в 16-ричной системе счисления > >
Вот и все, что касается классов оберток для примитивных типов данных. Используя их, Вы сможете оперировать примитивными типами, как объектами.