Автор: Widowmaker1984

Вопросы 24, 25 (оболочки и упаковка)

"Что вы знаете о классах оболочках?"
"Что такое автоупаковка (boxing/unboxing)?"


1. Классы-оболочки (или "обертки") являются объектным представлением восьми примитивных типов в Java. Все классы-оболочки являются неизменными и final, т. е. когда мы присваиваем им новое значение, фактически на замену прежнему объекту создается новый.

1.1. Ниже показаны примитивные типы и их классы-обертки в Java:

byte <-> Byte (аргументы: byte или String)
short <-> Short (аргументы: short или String)
int <-> Integer (аргументы: int или String)
long <-> Long (аргументы: long или String)
float <-> Float (аргументы: float, double или String)
double <-> Double (аргументы: double или String)
char <-> Character (аргументы: char)
boolean <-> Boolean (аргументы: boolean или String)

скрытый текст1.1.1. Числовые классы имеют общего предка — абстрактный класс Number, в котором описаны шесть методов, возвращающих числовое значение, содержащееся в классе, приведенное к соответствующему примитивному типу: byteValue(), doubleValue(), floatValue(), intValue(), longValue(), shortValue(). Эти методы переопределены в каждом из шести числовых классов-оболочек.

1.2. Зачем нужны классы-оболочки в Java? Разработчиками языка Java было принято решение отделить примитивные типы и классы-оболочки, указав при этом следующее:

- Используйте классы-обертки, когда работаете с коллекциями.
- Используйте примитивные типы для того, чтобы ваши программы были максимально просты (также они занимают меньше места).
- Еще одним важным моментом является то, что примитивные типы не могут быть null, а классы-оболочки — могут.
- С помощью класса-оболочки можно выполнять специальные операции: например с помощью Integer можно перевести текст в число (с помощью метода .parseInt()). Если попробовать сделать это с помощью кода самому придется изрядно повозиться.
- Также классы-оболочки могут быть использованы для достижения полиморфизма.

1.2.1. Каждый класс — обертка умеет получать свой тип данных из строки, для этого используется метод pasre:

ИмяТипа.parseИмяТипа(string s);
Integer i = Integer.parseInt("123123");
Boolean b = Boolean.parseBoolean("true");

2. Создание объекта-оболочки из переменной примитивного типа называется упаковкой (boxing), а получение значения примитивного типа из объекта-оболочки — распаковкой (unboxing).

2.1. Для понимания смысла упаковки-распаковки нужно понимать, как работает исполняемая среда и программа на уровне процессора, а также понимать, что ООП — это всего лишь надстройка над классическим структурным программированием. Промежуточный байт-код во время выполнения программы преобразуется исполняемой средой в команды конкретного микропроцессора.

Есть 3 основных способа хранения, обработки данных и их взаимодействия с программой: процессор-регистр, процессор-стек, и процессор-память. Команды работы с регистрами самые короткие и быстрые, со стеком — короткие, но выполняются на большее число тактов процессора, а команды работы с памятью — самые длинные и медленные. При этом в большинстве случаев простые типы данных хранятся и обрабатываются в регистрах процессора и в стеке, классы и более сложные типы данных хранятся в так называемой "куче" - динамической памяти, которая контролируется исполняемой средой. Механизм кучи достаточно сложен и работа с кучей по времени всегда дольше.

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

Упаковку-распаковку придумали для того, чтобы избежать потери производительности программного обеспечения: если критически важна скорость — распаковываем свои данные в простой компактный формат, если скорость не критична — упаковываем в объектный тип и упрощаем программу. Кроме скорости возможна ситуация со значительными затратами ресурсов: к примеру, если необходимо создать массив из миллиона однобайтовых элементов.

2.2. Начиная с Java 5 автоупаковка и распаковка позволяет легко конвертировать примитивные типы в их соответствующие классы-оболочки и наоборот. В тех случаях, когда по контексту требуются объекты (присваивание, вызов метода с передачей параметров), а мы используем значения примитивных типов (переменные или выражения типа 2 * 3), всегда происходит автоупаковка. Объектам-оболочкам можно присваивать значения примитивных типов, а переменным примитивных типов - значения переменных-оболочек, при этом при необходимости автоматически создаются объекты-оболочки с соответствующими значениями (автоупаковка) или наоборот, примитивные значения извлекаются из оболочек (автораспаковка):

int a = 5;
Integer b = 10;
a = b; // OK, атораспаковка
b = a * 123; // OK, автоупаковка

2.2.1. Автоупаковка применяется компилятором Java в следующих условиях:

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

2.2.2. Распаковка применяется компилятором Java в следующих условиях:

- Когда объект передается в качестве параметра методу, который ожидает соответствующий примитивный тип.
- Когда объект присваивается переменной соответствующего примитивного типа.
- В выражениях, в которых один или оба аргумента являются экземплярами классов-обёрток (кроме операции == и !=).

2.2.3. Автоупаковка и распаковка могут использоваться с операторами сравнения. Следующий фрагмент кода иллюстрирует, как это происходит:

Integer in = new Integer(25);
if (in < 35)

2.2.4. Автоупаковка и распаковка при перегрузке метода. Автоупаковка и распаковка выполняется при перегрузке метода на основании следующих правил:

- Расширение «побеждает» упаковку (В ситуации, когда становится выбор между расширением и упаковкой, расширение предпочтительней).
- Расширение «побеждает» переменное количество аргументов.
- Упаковка «побеждает» переменное количество аргументов.

2.3. Что нужно учитывать:
- При сравнении оператором ‘==’ может возникнуть путаница, так как он может применяться и к примитивным типам, и к объектам. Когда этот оператор применяется к объектам, он фактически сравнивает ссылки на объекты а не сами объекты.
- Смешение объектов и примитивных типов в операторах равенства и отношения. Если мы сравниваем примитивный тип с объектом, происходит распаковка объекта, который может бросить NullPointerException если объект null.
- Ухудшение производительности. Автоупаковка или распаковка ухудшают производительность приложения, поскольку это создает нежелательный объект, из-за которого сборщику мусора приходится работать чаще.

Комментарии


Лучшее   Правила сайта   Вход   Регистрация   Восстановление пароля

Материалы сайта предназначены для лиц старше 16 лет (16+)