1. Введение
В языке Java существуют два разных семейства данных. Первое — это примитивные типы: int, char, double, boolean и другие. Они просты и быстры: значение хранится напрямую в ячейке памяти. Второе семейство — это ссылочные типы, то есть объекты: переменная хранит ссылку на место, где объект расположен в памяти.
Иногда требуется использовать примитив как объект. Но примитивы изначально объектами не являются. В таких случаях помогают специальные классы-обёртки.
Типы-обёртки — это классы, которые хранят внутри себя значение примитива и позволяют обращаться с ним как с объектом. Например, для int — класс Integer, для double — Double, для char — Character, для boolean — Boolean. Такие классы предоставляют методы: Integer.parseInt(), Double.isInfinite(), Character.isLetter(), Boolean.parseBoolean() и др.
Пример получения Integer-объекта из int-примитива:
// Примитив
int a = 10;
// Объект-обёртка
Integer b = Integer.valueOf(10);
В переменной a хранится само число 10. А переменная b хранит ссылку на объект, который внутри себя содержит 10.
2. Зачем нужны обёртки
Примитив — это всегда конкретное значение, его нельзя сделать равным null. Объект может не существовать и хранить пустое значение — это удобно, когда нужно обозначить «неизвестно» или «отсутствует».
У примитива нет методов. Нельзя вызвать у числа что-то вроде x.toString(). Обёртка предоставляет такие возможности: Integer.parseInt("123") превращает строку в число, а число можно превратить в строку с помощью toString().
Итого: обёртки нужны там, где требуется именно объект — возможность хранить null, вызывать методы, передавать значения в API, которое принимает только объекты (например, коллекции типа List<Integer>).
Полный список типов-обёрток
| Примитивный тип | Класс-обёртка |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3. Упаковка и распаковка
Когда вы вручную создаёте объект-обёртку, вы берёте примитив и «упаковываете» его внутрь объекта:
int primitive = 42;
Integer wrapper = Integer.valueOf(primitive); // упаковка
Чтобы достать обратно примитивное значение — «распаковать» — используют, например, intValue():
Integer wrapper = Integer.valueOf(42);
int primitive = wrapper.intValue(); // распаковка
Автоупаковка (Autoboxing)
Компилятор сам добавит вызов valueOf(), если вы присваиваете примитив переменной-обёртке:
int a = 10;
Integer b = a; // автоупаковка (Integer.valueOf(a))
Автораспаковка (Unboxing)
Обратная ситуация — когда объект-обёртка используется там, где нужен примитив:
Integer c = 20;
int d = c; // автораспаковка (c.intValue())
int a = 10; // примитив
Integer b = a; // автоупаковка
Integer c = Integer.valueOf(20);
int d = c; // автораспаковка
4. Подводные камни
Сравнение обёрток. Оператор == для объектов сравнивает ссылки, а не значения:
Integer m = 200;
Integer n = 200;
System.out.println(m == n); // false, потому что это разные объекты
Java кэширует обёртки чисел в диапазоне от -128 до 127, поэтому иногда == вернёт true, а иногда — false. Для сравнения значений всегда используйте equals():
Integer x = 100;
Integer y = 100;
System.out.println(x == y); // true, попали в кеш
System.out.println(x.equals(y)); // true, сравнение по значению
NullPointerException при автораспаковке. Если обёртка равна null, а вы пытаетесь её распаковать — получите исключение:
Integer value = null;
int primitive = value; // Ошибка! NullPointerException
Производительность и память. Обёртки медленнее примитивов и занимают больше памяти. В горячих участках кода и при работе с большими массивами чисел выбирайте примитивы.
5. Примеры
Примеры работы с Integer и Double
Преобразование строки в число:
String text = "123";
int number = Integer.parseInt(text);
System.out.println(number); // 123
Проверка особых значений вещественных чисел:
double d = 1.0 / 0;
System.out.println(Double.isInfinite(d)); // true
double nan = 0.0 / 0.0;
System.out.println(Double.isNaN(nan)); // true
Примеры работы с Character
char хранит один символ; обёртка Character даёт удобные проверки:
char ch = 'A';
Character wrapper = ch; // автоупаковка
System.out.println(Character.isLetter(ch)); // true
System.out.println(Character.isDigit(ch)); // false
System.out.println(Character.toLowerCase(ch)); // 'a'
Примеры работы с Boolean
Примитив boolean — это только true/false. Обёртка Boolean позволяет хранить ещё и null:
Boolean flag = null; // допустимо
flag = Boolean.TRUE; // специальная константа
System.out.println(flag); // true
Преобразование строк в логические значения:
String s1 = "true";
String s2 = "false";
boolean b1 = Boolean.parseBoolean(s1);
boolean b2 = Boolean.parseBoolean(s2);
System.out.println(b1); // true
System.out.println(b2); // false
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ