Зачем нужны классы обертки в java
Перейти к содержимому

Зачем нужны классы обертки в java

  • автор:

Классы оболочки в Java

Раньше Вы знакомились с примитивными типами данных (такими, как int, short, boolean и т.д.):

Variables Java_Vertex Academy

Тем не менее, Вы знаете, что Java — объектно-ориентированный язык программирования . Это значит, что в ней «все, что можно, представлено в виде объектов».

Поэтому, у примитивных типов есть объекты-аналоги — так называемые «классы оболочки» , или «wrapper» (с англ. «обертка, упаковка»):

wrap_1

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

О названиях

Имена классов нетрудно запомнить — они повторяют имена примитивных типов данных:

wrap_2

Обратите внимание — все классы оболочки пишутся с большой буквы:

wrap_3

Зачем так усложнять

Но зачем они нужны? Если есть обычный int, short, . , зачем нам Short и Integer ? Или, может, оставить только классы оболочки, и не пользоваться примитивами, раз у них функций больше ?

i-in3cvejg-evan-dennis

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

Например, обычный int занимает меньше места, и если нет необходимости проводить над ним особые операции, Ваш компьютер будет работать быстрее.

В свою очередь, с помощью класса-оболочки Integer можно выполнять специальные операции — например, перевести текст в число (с помощью метода .parseInt() для Integer-а ). Если попробовать сделать это с помощью кода самому придется изрядно повозиться.

Integer и int можно сравнить с компьютером и записной книжкой:

stocksnap_2ci87djpdzkopiya-stocksnap_0axtvi7jb2

Компьютер, безусловно, может больше, чем блокнот — но Вы не будете целый день носить с собой три килограмма для того, чтобы сделать несколько заметок?

Кроме того, есть ситуации, когда нельзя использовать объекты, или наоборот, когда можно использовать только объекты.

Метод .parseInt()

Иногда в объекте типа String содержится число, и Вам нужно с ним работать дальше — умножать, делить, в степень возводить. Но нельзя! Строка же. Что делать?

Зачем нужны классы обертки в 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

Классы-оболочки Java являются Объектным представлением восьми примитивных типов в Java. Все классы-оболочки в Java являются неизменными и final. Начиная с Java 5 автоупаковка и распаковка позволяет легко конвертировать примитивные типы в их соответствующие классы-оболочки и наоборот.

В таблице ниже показаны примитивные типы и их классы-обертки в 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

Зачем нужны классы-оболочки в Java?

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

  • Используйте классы-обертки, когда работаете с коллекциями.
  • Используйте примитивные типы для того, чтобы ваши программы были максимально просты.

Еще одним важным моментом является то, что примитивные типы не могут быть null, а классы-оболочки — могут.

Также классы-оболочки могут быть использованы для достижения полиморфизма.

Вот простая программа, показывающая различные аспекты классов-оболочек в Java :

Классы Integer, Character, Boolean

Часто бывает предпочтительней работать с объектами, а не с примитивными типами. Так, например, при использовании коллекций, просто необходимо значения примитивных типов каким-то образом представлять в виде объектов. Для этих целей и предназначены так называемые классы-обертки (wrapper classes). Для каждого примитивного типа Java существует свой класс-обертки. Такой класс является неизменяемым, то есть, для изменения значения необходимо создавать новый объект. К тому же класс-обертка имеет атрибут final и его нельзя наследовать.

Все классы-обертки (кроме Void) реализуют интерфейс java.io.Serializable, поэтому объекты любого класса-обертки (кроме Void) могут быть сериализованы. Это имеет важное значение для «сетевых» пересылок объектов.

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

Примитивный тип Класс-обертка
byte Byte
short Short
char Character
int Integer
long Long
float Float
double Double
boolean Boolean

Wrapper classes числовых типов (Byte, Short, Integer, Long, Float, Double) наследуются от класса Number, который содержит код, общий для всех классов-оберток числовых типов. Все классы-обертки реализуют интерфейс Comparable.

Классы-обертки числовых типов имеют метод equals(Object), сравнивающий примитивные значения объектов. Но с этим надо быть предельно внимательным. Так в результате выполнения следующего кода

public static void main(String[] args)

мы увидим в консоли следующий текст :

i = 1, b = 1 i.equals(b) = false, b.equals(i) = false

Данный результат связан с тем, что во всех классах-обертках метод equals() сначала производит проверку на совпадение типов (классов), и если нет совпадения, то сразу же возвращает false. В JDK 1.3.1 для класса-обертки Integer метод equals() определен следующим образом :

public boolean equals(java.lang.Object obj)

Класс Number

Абстрактный класс Number является суперклассом для классов Byte, Double, Float, Integer, Long и Short. Наследники Number должны обеспечить методы преобразовывания числовых значений в byte, double, float, int, long и short.

Класс Number имеет один конструктор :

public Number()

Методы класса Number :

Метод Описание
byte byteValue() преобразование значения в тип byte
abstract double doubleValue() преобразование значения в тип double
abstract float floatValue() преобразование значения в тип float
abstract int intValue() преобразование значения в тип int
abstract long longValue() преобразование значения в тип long
short shortValue() преобразование значения в тип short

Класс Integer

Конструкторы класса Integer
  • Integer(int value) — создание объекта Integer на основе аргумента int
  • Integer(String s) — создание объекта Integer на основе строкового аргумента
Поля класса Integer
  • static int MAX_VALUE — максимальная величина типа int
  • static int MIN_VALUE — минимальная величина типа int
  • static Class TYPE — объект класса, представляющий простой тип int
Наиболее значимые методы класса Integer
Метод Описание
byte byteValue() преобразование значения в тип byte
int compareTo(Integer integer) сравнение двух целых чисел
int compareTo(Object o) сравнение значения с другим объектом
Integer decode(String nm) перевод строки в Integer
double doubleValue() преобразование значения в тип double
boolean equals(Object obj) сравнение с другим объектом
float floatValue() преобразование значения в тип float
int hashCode() получение hashcode для обьекта
int intValue() преобразование значения в тип int
long longValue() преобразование значения в тип long
int parseInt(String s) преобразование текстового значения в тип int
int parseInt(String s, int radix) преобразование текстового значения со знаком в системе счисления, определенной во втором аргументе, в тип int
short shortValue() преобразование значения в тип short
String toBinaryString(int i) преобразование целочисленного значения i в текстовый вид с базой 2 (двоичный)
String toHexString(int i) преобразование целочисленного значения i в текстовый вид с базой 16(шестнадцатиричный)
String toOctalString(int i) преобразование целочисленного значения i в текстовый вид с базой 8(восьмиричный)
String toString() преобразование значения в тип String
String toString(int i) преобразование значения i в тип String
String toString(int i, int radix) преобразование целочисленного значения i в строку в заданной системе счисления radix
Integer valueOf(String s) создание объекта Integer, инициализированного величиной, определенной в строковой переменной s
Integer valueOf(String s, int radix) создание объекта Integer, инициализированного величиной, определенной в строковой переменной s, записанной в системе счисления radix

Методы parseInt(), преобразующие текстовое значение в целочисленное, не следует путать с методами valueOf(), возвращающие класс-обертку. Если переданная на вход строка содержит нецифровые символы, то методы возбуждают исключение NumberFormatException.

Дополнительную информацию о классе Integer можно получить на странице Кэширование класса Integer

Класс Byte

Класс Byte является стандартной оболочкой для байтовых величин.

Конструкторы класса Byte
  • Byte (byte value) — создание объекта Byte с определенным значением value;
  • Byte (String s) — создание объекта Byte на основе текстового значения s.
Поля класса Byte
  • static int MAX_VALUE — максимальная величина типа byte
  • static int MIN_VALUE — минимальная величина типа byte
  • static Class TYPE — объект класса, представляющий простой байтовый тип byte
Методы класса Byte
Метод Описание
byte byteValue() получение значения типа byte
int compareTo(Byte byte) сравнение с объектом Byte
int compareTo(Object o) сравнение с другим объектом
static Byte decode(String nm) преобразование строки в Byte
double doubleValue() преобразование значения в double
boolean equals(Object obj) проверка на равенство с другим объектом
float floatValue() преобразование значения в float
int hashCode() получение hash-кода объекта
int intValue() преобразование значения в int
long longValue() преобразование значения в long
static byte parseByte(String s) преобразование текстового значения в byte
static byte parseByte(String s, int radix) преобразование текстового значения в системе счисления radix в байт
short shortValue() преобразование значения в short
String toString() преобразование значения в String
static String toString(byte b) преобразование байтового значения в String
static Byte valueOf(String s) преобразование текстового значения в Byte
static Byte valueOf(String s, int radix) преобразование текстового значения в системе счисления radix в Byte

Класс Boolean

Класс Boolean является оболочкой простого логического объекта. Объект типа Boolean содержит единственное поле логического типа. Кроме того, этот класс включает методы преобразования boolean в String и обратно, а также константы и методы полезные при работе с логическим типом.

Конструкторы класса Boolean
  • Boolean (boolean value) — создание логического объекта на основе аргумента;
  • Boolean (String s) — создание логического объекта на основе текстового значения s [«true» | «false»].
Поля класса Boolean
  • static Boolean FALSE — логический объект, соответствующий значению «ложь»
  • static Boolean TRUE — логический объект, соответствующий значению «истина»
  • static Class TYPE — объект класса, представляющий простой логический тип
Методы класса Boolean
Метод Описание
boolean booleanValue() получение логического значения
boolean equals(Object obj) функция возвращает логическое значение по результату сравнения переданного объекта с текущим
static boolean getBoolean(String name) преобразование текстового значения в логическое
int hashCode() получение hash-кода объекта
String toString() преобразование в текстовое значение
static Boolean valueOf(String s) преобразование текстового значения в Boolean

Класс Void

Класс-обертка Void, в отличии от остальных, НЕ реализует интерфейс java.io.Serializable и не имеет открытого конструктора. Более того, экземпляр класса Void вообще не может быть получен. Он нужен только для получения ссылки на объект, соответствующий void. Эта ссылка представлена статической константой TYPE. Выражение void.class == Void.TYPE вернет «true».

В большинстве случаев, если нужно проверить возвращаемый тип метода (например, через рефлексию) можно использовать void.class — не перепутаешь с Void.class.

Использовать Void можно, например, в случаях, когда имеется обобщенный класс (generic) и необходимо, чтобы метод ничего не возвращал :

interface IVoid  < T doStuff(); >class A implements IVoid  < @Override public Void doStuff() < // . return null; // ничего другого вернуть нельзя (если // не хитрить с рефлексией, конечно) >>

Класс Character

Для хранения символов Java использует специальный тип char. В отличие от языка C/C++, где char представляет собой целочисленный тип с размером 8 бит, в Java для char применяется кодировка Unicode и для хранения Unicode-символов используется 16 бит. Диапазон допустимых значений — от 0 до 65536 (отрицательных значений не существует).

Класс Character является оболочкой вокруг типа char. Чтобы получить значение типа char, содержащее в объекте Character, необходимо вызвать метод charValue().

Конструктор класса Character

Character имеет только один конструктор, которому в качестве параметра передается значение char.

Character ch = new Character('a');

Помимо констант MIN_VALUE и MAX_VALUE, Character содержит две константы MIN_RADIX и MAX_RADIX, которые равны минимальному и максимальному основанию системы счисления, которые используются методами (представленными ниже) для перевода отдельного цифрового символа в его целочисленный эквивалент и наоборот. Основание должно находиться в диапазоне 2–36; цифры свыше 9 представлены буквами от A до Z или их эквивалентами в нижнем регистре.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *