JavaRush/Java блог/Java Developer/Обёртки, распаковка и запаковка
Автор
Aditi Nawghare
Инженер-программист в Siemens

Обёртки, распаковка и запаковка

Статья из группы Java Developer
участников
Привет! Ты уже неплохо знаком с примитивными типами, и немало с ними поработал. Обёртки, распаковка и запаковка - 1У примитивов в программировании, и в Java в частности, есть множество преимуществ: они занимают мало памяти, за счет чего повышается эффективность работы программы, и четко разделены по диапазонам значений. Однако в процессе изучения Java мы уже не раз, словно мантру, повторяли — “в Java все является объектом”. А ведь примитивы — прямое опровержение этих слов. Объектами они не являются. Получается, принцип “все является объектом” является ложным? На самом деле нет. В Java у каждого примитивного типа есть свой брат-близнец — класс-обертка (Wrapper). Что такое обертка? Обертка — это специальный класс, который хранит внутри себя значение примитива. Но поскольку это именно класс, он может создавать свои экземпляры. Они будут хранить внутри нужные значения примитивов, при этом будут являться настоящими объектами. Названия классов-оберток очень похожи на названия соответствующих примитивов, или полностью с ними совпадают. Поэтому запомнить их будет очень легко.
Wrapper Classes for Primitive Data Types
Primitive Data Types Wrapper Classes
int Integer
short Short
long Long
byte Byte
float Float
double Double
char Character
boolean Boolean
Объекты классов оберток создаются так же, как и любые другие:
public static void main(String[] args) {

   Integer i = new Integer(682);

   Double d = new Double(2.33);

   Boolean b = new Boolean(false);
}
Классы-обертки позволяют нивелировать недостатки, которые есть у примитивных типов. Самый очевидный из них — примитивы не имеют методов. Например, у них нет метода toString(), поэтому ты не сможешь, например, преобразовать число int в строку. А вот с классом-оберткой Integer — запросто.
public static void main(String[] args) {

   Integer i = new Integer(432);

   String s = i.toString();
}
Возникнут сложности и с обратным преобразованием. Допустим, у нас есть строка, про которую мы точно знаем, что она содержит число. Тем не менее, в случае с примитивным типом int мы никак не сможем это число из строки достать и превратить, собственно, в число. Но благодаря классам-оберткам такая возможность у нас появилась.
public static void main(String[] args) {

   String s = "1166628";

   Integer i = Integer.parseInt(s);

   System.out.println(i);
}
Вывод: 1166628 Мы успешно получили число из строки и присвоили его в переменную-ссылку Integer i. Кстати, по поводу ссылок. Ты уже знаешь, что параметры передаются в методы по-разному: примитивы — по значению, а объекты — по ссылке. Ты можешь использовать это знание при создании своих методов: если твой метод работает, например, с дробными числами, но тебе нужна логика именно передачи по ссылке, ты можешь передать в метод параметры Double/Float вместо double/float. Кроме того, помимо методов в классах-обертках есть очень удобные для использования статические поля. Например, представь, что перед тобой сейчас стоит задача: вывести в консоль максимально возможное число int, а после — минимально возможное. Задачка вроде элементарная, а все равно — без гугла вряд ли справишься. А классы-обертки легко позволяют решать такие “бытовые задачи”:
public class Main {
   public static void main(String[] args) {

       System.out.println(Integer.MAX_VALUE);
       System.out.println(Integer.MIN_VALUE);
   }
}
Такие поля позволяют не отвлекаться от выполнения более серьезных задач. Не говоря уж о том, что в процессе печати числа 2147483647 (это как раз MAX_VALUE) не мудрено и опечататься:) Кроме того, в одной из прошлых лекций мы уже обращали внимание на то, что объекты классов-оберток являются неизменяемыми (Immutable).
public static void main(String[] args) {

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

   b = a;
   a = 1;
   System.out.println(b);
}
Вывод: 0 Объект, на который изначально указывала ссылка а, не изменил свое состояние, иначе значение b тоже изменилось бы. Как и в случае со String, вместо изменения состояния объекта-обертки в памяти создается абсолютно новый объект. Почему же создатели Java, в конечном итоге, приняли решение оставить в языке примитивные типы? Раз уж все должно являться объектом, и у нас уже есть классы-обертки, которыми можно выразить все, что выражают примитивы, почему вообще не оставить в языке только их, а примитивы удалить? Ответ прост — производительность. Примитивные типы потому и называют примитивными, потому что они лишены многих “тяжеловесных” особенностей объектов. Да, у объекта есть много удобных методов, но ведь они не всегда тебе нужны. Иногда тебе нужно просто число 33, или 2,62, или значение true/false. В ситуациях, когда все преимущества объектов не имеют значения и не нужны для работы программы, примитивы справятся с задачей гораздо лучше.

Автоупаковка/автораспаковка

Одной из особенностей примитивов и их классов-оберток в Java является автоупаковка/автораспаковка (Autoboxing/Autounboxing) Обёртки, распаковка и запаковка - 2 Давай разберемся с этим понятием. Как мы с тобой уже узнали ранее, Java — объектно-ориентированный язык. Это значит, что все программы, написанные на Java, состоят из объектов. Примитивы не являются объектами. Но при этом переменной класса-обертки можно присваивать значение примитивного типа. Этот процесс называется автоупаковкой (autoboxing). Точно так же переменной примитивного типа можно присваивать объект класса-обертки. Этот процесс называется автораспаковкой (autounboxing). Например:
public class Main {
   public static void main(String[] args) {
       int x = 7;
       Integer y = 111;
       x = y; // автораспаковка
       y = x * 123; // автоупаковка
   }
}
В строке 5 мы присваиваем примитиву x значение y, который является объектом класса-обертки Integer. Как видишь, никаких дополнительных действий для этого не нужно: компилятор знает что int и Integer, по сути, одно и то же. Это и есть автораспаковка. Так же происходит и автоупаковка в строке 6: объекту y легко присваивается значение примитивов (х*123). Это пример автоупаковки. Именно поэтому добавляется слово "авто": для присваивания ссылок-примитивов объектам их классов-оберток (и наоборот) не требуется ничего делать, все происходит автоматически. Удобно, да? :) Еще одно очень большое удобство автоупаковки/автораспаковки проявляется в работе методов. Дело в том, что параметры методов тоже подлежат автоупаковке и автораспаковке. И, например, если какой-то из них принимает на вход два объекта Integer — мы легко можем передать туда обычные примитивы int!
public class Main {
   public static void main(String[] args) {

       printNumber(7);//обычный int, даже без переменной
   }

   public static void printNumber(Integer i) {
       System.out.println("Вы ввели число " + i);
   }
}
Вывод: Вы ввели число 7 Точно так же работает и наоборот:
public class Main {
   public static void main(String[] args) {

       printNumber(new Integer(632));
   }

   public static void printNumber(int i) {
       System.out.println("Вы ввели число " + i);
   }
}
Важный момент, о котором нужно помнить: автоупаковка и распаковка не работают для массивов!
public class Main {
   public static void main(String[] args) {

       int[] i = {1,2,3,4,5};

       printArray(i);//ошибка, не компилируется!
   }

   public static void printArray(Integer[] arr) {
       System.out.println(Arrays.toString(arr));
   }
}
Попытка передать массив примитивов в метод, который принимает на вход массив объектов, вызовет ошибку компиляции. Напоследок, еще раз кратко сравним примитивы и обертки Примитивы:
  • имеют преимущество в производительности
Обертки:
  • Позволяют не нарушать принцип “все является объектом”, благодаря чему числа, символы и булевы значения true/false не выпадают из этой концепции
  • Расширяют возможности работы с этими значениями, предоставляя удобные методы и поля
  • Необходимы, когда какой-то метод может работать исключительно с объектами
Комментарии (183)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Kaz
Уровень 17
5 февраля, 13:38
Мне нужна помощь тут:
public static void main(String[] args) {

   Integer a = new Integer(0); //создали объект Integer, назвали a, передали ссылку на 0
   Integer b = new Integer(0);   //создали объект Integer, назвали b, передали ссылку на 0

   b = a;   //объекту b ссылающемуся на 0, попытались присвоить ссылку от а,
               //который тоже ссылался на 0. Ок, допустим, объект b immortable и
              //и изменяться он не собирается, но ведь он все равно ссылается на 0

   a = 1;  //после этого объекту а, передают ссылку на 1.. ну допустим, теперь
             //создается новый объект a, потому что первый менять нельзя (или  я просто
             //понимаю суть его неменяемости).. но дело не в этом.

   System.out.println(b);
           //дело в том, что чего удивляться что в консоль выводится 0. Конечно, там будет 0!
}
и конечно, если сделать наоборот:
Integer a = new Integer(0);
Integer b = new Integer(0);
 a = 1;
b = a;

System.out.println(b);
То тоже ничего удивительно - выведется 1! Короче, о чем был этот абзац в лекции? О_о
Алексей Backend Developer Expert
14 февраля, 14:45
Приветствую, здесь дело в том, что это объекты, если ты подобное сделаешь с другими объектом, то ситуация будет в точности да наоборот. Пример: int[] a = new int[]{1, 1, 1}; int[] b = new int[]{2, 2, 2}; b = a; a[0] = 5; a[1] = 7; a[2] = 9; System.out.println(b); То в переменной b будут значения (5, 7, 9).
Kaz
Уровень 17
15 февраля, 20:07
Спасибо! Ты хотел сказать "здесь дело в том, что это НЕИЗМЕНЯЕМЫЕ объекты". Если честно, всё равно слабо понимаю суть этой "неизменяемости".. В общем, попробую разжевать для себя еще раз:
Integer a = new Integer(1); //объект a хранит ссылку на 1
   Integer b = new Integer(0); //объект b хранит ссылку на 0

   b = a; // b берет из объекта a саму ссылку, которая адресует к 1
   a = 2;
/*в этот момент создается типа новый объект Integer,
 который хранит ссылку на 2. Точно так же как b присвоил
ссылку первоначального a, теперь a присваивает ссылку
нового объекта адресующего к 2. Тут важно понимать,
что есть ссылка от объекта, а есть сам объект. Так вот
тот объект (который хранил значение 1 - не изменился,
потому что это объект класса-обертки)*/
Итак, в консоль выведется 1, а не 2 а теперь из примера Алексея:
int[] a = new int[]{1, 1, 1};
int[] b = new int[]{2, 2, 2};

b = a; // b начинает ссылаться на то же, на что ссылается a
a[0] = 5; /*а так как это обычные объекты (а не неизменяемые),
то объект, на который ссылался a МЕНЯЕТСЯ, а именно меняет свои ЗНАЧЕНИЯ
и не создается никакой новый объект. */
a[1] = 7;
a[2] = 9;

System.out.println(b); /*поэтому когда мы обращаемся к b,
то внутри b хранится некая ссылка на объект и это тот
 самый объект, который изменился после манипуляций
с a, поэтому b выведет 5, 6, 7 */
Алексей Backend Developer Expert
16 февраля, 13:13
Да, именно про НЕИЗМЕНЯЕМЫЕ объект я хотел тебе донести))) А Суть в том, что ты просто должен это знать, и что бы из-за незнания не допускались ошибки в будущем! Что бы ты к НЕИЗМЕНЯЕМЫМ объектам не относился как к ИЗМЕНЯЕМЫМ)))
B ATAKE
Уровень 19
21 марта, 16:26
Если ты помнишь блок про Стринги, то там тоже была подобная фигня (ведь Стринг тоже НЕИЗМЕНЯЕМЫЙ объект). Переменная Странга хранит ссылку на объект. Но если мы изменяем значение переменной, то мы не меняем значение в той части памяти, на которую ссылается переменная стринг - нет! В другом куске памяти создается новая строка (на которую ссылается наша ссылка, хранящаяся в переменной Стринг). Насколько я понимаю, все именно так.
Магсумова Диана
Уровень 108
Expert
24 января, 15:35
Очень хорошая статья 👍
Roman Kibenko
Уровень 9
18 января, 03:37
Не все в Java является объектом, кроме того, что все)
Максим Li Java Developer
29 ноября 2023, 04:50
Всё хорошо!
IT
Уровень 19
14 ноября 2023, 02:12
Если вы так и не поняли, то примитивы объектами не являются)
Pavel Fetisov
Уровень 25
15 января, 15:00
Всё в Java является объектом, кроме того, что объектом не является!
Kaz
Уровень 17
5 февраля, 13:22
Всё в JAVA является объектом; Примитивы объектом не являются; //чтобы не было ошибки при компиляции кода, придумали классы-обертки
Anonymous #3361169
Уровень 47
1 ноября 2023, 08:17
👍
Артур
Уровень 34
29 сентября 2023, 09:56
Отличная статья, как и все этого Автора
Anatoly Enterprise Java Developer
17 сентября 2023, 11:05
понятно
26 июля 2023, 06:52
не всё в java является объектом, например в java существуют операторы
Dmitry Vidonov
Уровень 29
Expert
12 августа 2023, 09:38
Это уже душнота пошла!))
Mamba84
Уровень 30
17 июля 2023, 11:06
Thank you, Aditi Nawghare!