Integer это класс-обертка для примитива int: объект, внутри которого лежит одно поле типа int. Обертка нужна там, где примитив не подходит: в коллекциях, в дженериках и везде, где значения может не быть, ведь Integer в отличие от int умеет быть null. Превращение одного в другое происходит само собой, это называется автоупаковкой и распаковкой. А главная ловушка класса это сравнение: два Integer сравнивают методом equals(), потому что оператор == сверяет ссылки, а не числа.

Кратко

  • Integer это класс-обертка примитива int. Внутри одно поле типа int, снаружи полноценный объект со своими методами и константами.
  • Integer может быть null, а int нет. Отсюда и главный риск: арифметика с null-переменной падает с NullPointerException.
  • Запись Integer a = 3; работает благодаря автоупаковке: примитив сам превращается в объект. Обратное превращение это распаковка.
  • Сравнивать два Integer нужно через equals(). Оператор == сверяет ссылки, поэтому на числах больше 127 он даст false там, где значения совпадают.
  • Значения от -128 до 127 кэшируются, и на них == срабатывает как будто правильно. Именно отсюда растет самая известная путаница с Integer.
  • Класс дает константы MAX_VALUE (2147483647), MIN_VALUE, SIZE (32), BYTES (4), TYPE и методы parseInt, valueOf, toString, max, min.
В данной статье мы поговорим о классе Integer. Рассмотрим такие вопросы:
  • что такое классы-обертки;
  • автоупаковка/распаковка примитивов;
  • работа класса Integer, его методы и константы.
Кот, завернутый в лист оберточной бумаги, наружу торчит только морда

Классы-обертки примитивных типов

Как известно, в Java есть различные типы данных, которые условно можно разделить на два блока:
  • примитивные;
  • ссылочные.
Примитивных типов данных в Java несколько:
  • целые числа: byte, short, int, long;
  • числа с плавающей точкой (вещественные): float, double;
  • логический тип данных: boolean;
  • символьный тип данных: char.
У каждого примитивного типа данных есть свой класс-обертка. Ссылочный тип данных, который “оборачивает” своего примитивного младшего брата в Java-объект. Ниже приведены примитивные типы данных и соответствующие им классы обертки:
Примитивный тип Класс обертка
byte Byte
short Short
int Integer
long Long
float Float
double Double
boolean Boolean
char Character
В практическом смысле у примитивов и их классов-оберток много общего. Большинство операций выполняются идентично. Однако у классов-оберток есть ряд характеристик, не свойственных примитивам. Во-первых, это классы: при работе с классами-обертками мы работаем с объектами. Во-вторых (все последующее вытекает из пункта во-первых), данные объекты могут быть null. В-третьих, классы-обертки предоставляют ряд констант и методов, упрощающих работу с определенным типом данных. В данной статье мы подробнее рассмотрим работу с классом Integer.

The Класс Integer в Java

Класс Integer это класс-обертка примитивного типа int. Внутри данного класса содержится единственное поле типа int. Будучи классом оберткой, Integer предоставляет различные методы для работы с int, а также ряд методов для преобразования int в String и String в int.

Как создать Integer

Ниже мы рассмотрим различные примеры работы с классом. Начнем с создания. Чаще всего используется (да и проще всего использовать) следующий вариант создания:
Integer a = 3;
То есть инициализация Integer переменной в данном случае аналогична инициализации int переменной. Более того, Integer переменную можно проинициализировать значением int переменной:
int i = 5;
Integer x = i;
System.out.println(x); // 5
В кейсе выше неявно происходит автоупаковка. Мы поговорим еще о ней ниже. Помимо вариантов инициализации, указанных выше, переменную Integer можно создать, как и прочие объекты, с использованием конструктора и ключевого слова new:
Integer x = new Integer(25);
System.out.println(x);
Однако так дольше писать и дольше читать, поэтому данный вариант наименее распространен. Более того, начиная с Java 9 оба конструктора класса помечены как устаревшие. В документации сказано прямо: «It is rarely appropriate to use this constructor. The static factory valueOf(int) is generally a better choice, as it is likely to yield significantly better space and time performance». То есть штатный способ получить Integer из числа это Integer.valueOf(25), а из строки Integer.parseInt или тот же valueOf. Конструктор пока работает, но в новом коде его лучше не писать.

Что можно делать с Integer

С Integer переменными можно делать все то, что и с int переменными. Их можно:
Складывать

Integer a = 6;
Integer b = 2;
Integer c = a + b;
System.out.println(c); // 8
Вычитать

Integer a = 6;
Integer b = 2;
Integer c = a - b;
System.out.println(c); // 4
Умножать

Integer a = 6;
Integer b = 2;
Integer c = a * b;
System.out.println(c); // 12
Делить

Integer a = 6;
Integer b = 2;
Integer c = a / b;
System.out.println(c); // 3
Инкрементировать

Integer a = 6;
a++;
++a;
System.out.println(a); // 8
Декрементировать

Integer a = 6;
a--;
--a;
System.out.println(a); // 4
Однако при всем этом нужно быть осторожным и помнить, что Integer это ссылочный тип данных, и переменная данного типа может быть null. В таком случае (если переменная равна null) лучше воздержаться от арифметических операций (да и любых других, в которых null ничего хорошего не сулит). Приведем пример:
Integer a = null;
Integer b = a + 1; // Здесь мы упадем с "Exception in thread "main" java.lang.NullPointerException"
System.out.println(b);
Операции сравнения в основной массе проводятся также, как и в примитивном типе int:
Integer a = 1;
Integer b = 2;

System.out.println(a > b);
System.out.println(a >= b);
System.out.println(a < b);
System.out.println(a <= b);
Output:

false
false
true
true

Сравнение двух Integer: почему == обманывает

Особняком стоит операция сравнения двух Integer переменных. И дело тут в том, что Integer это ссылочный тип данных, и его переменные хранят ссылки на значения, а не сами значения (объекты). Проявления этого факта можно наблюдать при выполнении следующего фрагмента кода:
Integer a = 1;
Integer b = 1;
Integer c = new Integer(1);

System.out.println(a == b); // true
System.out.println(a == c); // false
Результат первого равенства будет true, а второго false. Так происходит, потому что в первом случае мы сравниваем две переменные (“а” и “b”), которые хранят в себе ссылки на один и тот же объект. А во втором случае мы сравниваем две переменные, ссылающиеся на два разных объекта (при создании переменной “c” мы создали новый объект). Устаревший конструктор тут оставлен намеренно: только он гарантированно создает новый объект. Если написать Integer.valueOf(1), вернется ссылка на объект из кэша, и второе сравнение тоже даст true. В обычном коде так писать не нужно, это демонстрация. Приведем еще один интересный пример:
Integer a = 1;
Integer b = 1;

Integer x = 2020;
Integer y = 2020;

System.out.println(a == b); // true
System.out.println(x == y); // false
Как мы видим, результат первого сравнения у нас true, а результат второго false. Тут все дело в кэшировании. Все целые числа в промежутке от -128 до 127 включительно (данные значения можно кастомизировать) кэшируются. Поэтому когда мы создаем новую переменную и присваиваем ей целочисленное значение, лежащее в промежутке от -128 до 127, мы не создаем новый объект, а присваиваем переменной ссылку на уже созданный объект в кэше. Теперь, зная этот факт, пример выше не кажется таким уж мистическим. Переменные а и b ссылаются на один и тот же объект, на объект из кэша. А во время инициализации переменных x и y мы каждый раз создавали новый объект, и данные переменные хранили в себе ссылки на разные объекты. А как известно, оператор == сравнивает значения переменных, а значением ссылочных переменных являются ссылки. Чтобы безошибочно проверять на равенство две Integer переменные, необходимо использовать (как бы банально это ни звучало) метод equals. Перепишем пример выше:
Integer a = 1;
Integer b = 1;

Integer x = 2020;
Integer y = 2020;

System.out.println(a.equals(b)); // true
System.out.println(x.equals(y)); // true

Автоупаковка и распаковка Integer

Что такое автоупаковка и распаковка? При создании новых Integer переменных мы использовали такую конструкцию:
Integer a = 2020;
Таким образом мы создавали новый объект без использования ключевого оператора new. Такое возможно благодаря механизму автоупаковки примитивного типа int. Обратная процедура проявляется при присваивании примитивной переменной int значения ссылочной Integer переменной:
Integer a = 2020;
int x = a;
В данном случае мы вроде бы присвоили ссылку (а именно ссылка на объект является значением переменной “а”) примитивной переменной. Но по факту благодаря механизму автораспаковки в переменную “х” записалось значение 2020. Автоупаковка и распаковка это весьма распространенное явление в Java. Часто оно происходит само собой, иногда даже без ведома программиста. Но знать об этом явлении все же нужно. У нас на Javarush есть интересная статья на эту тему.

Константы класса Integer

Класс Integer предоставляет различные константы и методы для работы с целыми числами. В данном разделе мы более подробно рассмотрим некоторые из них на практике. Начнем с констант. В таблице ниже приведены все константы класса:
Константа Описание
SIZE Количество битов в двоичной системе счисления, занимаемое типом int
BYTES Количество байтов в двоичной системе счисления, занимаемое типом int
MAX_VALUE Максимальное значение, которое вмещает в себя тип int
MIN_VALUE Минимальное значение, которое вмещает в себя тип int
TYPE Возвращает объект типа Class от типа int
Посмотрим на значения всех этих констант, выполнив следующий код:
public static void main(String[] args) {
        System.out.println(Integer.SIZE);
        System.out.println(Integer.BYTES);
        System.out.println(Integer.MAX_VALUE);
        System.out.println(Integer.MIN_VALUE);
        System.out.println(Integer.TYPE);
}
В результате получим следующий вывод:

32
4
2147483647
-2147483648
int

Методы класса Integer

А теперь кратко рассмотрим наиболее используемые методы класса Integer. Итак, “топ” возглавляют методы по преобразованию числа из строки, либо преобразованию строки из числа.

parseInt: строка в int

Начнем с преобразований строки в число. Для этих целей служит метод parseInt, сигнатура ниже:
  • 
    static int parseInt(String s)
    
Данный метод преобразует String в int. Продемонстрируем работу данного метода:
int i = Integer.parseInt("10");
System.out.println(i); // 10
Если преобразование невозможно, например мы передали слово в метод parseInt, будет брошено исключение NumberFormatException. У метода parseInt(String s) есть перегруженный собрат:
  • 
    static int parseInt(String s, int radix)
    
Данный метод преобразует параметр s в int. В параметре radix указывается, в какой системе счисления изначально записано число в s, которое необходимо преобразовать в int. Примеры ниже:
System.out.println(Integer.parseInt("0011", 2)); // 3
System.out.println(Integer.parseInt("10", 8));   // 8
System.out.println(Integer.parseInt("F", 16));   // 15
Методы parseInt возвращают примитивный тип данных int.

valueOf: строка в Integer

У данных методов есть аналог, метод valueOf. Некоторые вариации данного метода внутри себя просто вызывают parseInt. Отличие от parseInt в том, что результатом работы valueOf будет Integer, а не int. Рассмотрим ниже все варианты данного метода и пример его работы:
  • static Integer valueOf(int i) возвращает Integer, значением которого является i;
  • static Integer valueOf(String s) аналогичен parseInt(String s), но результатом будет Integer;
  • static Integer valueOf(String s, int radix) аналогичен parseInt(String s, int radix), но результатом будет Integer.
Примеры:
int a = 5;
Integer x = Integer.valueOf(a);
Integer y = Integer.valueOf("20");
Integer z = Integer.valueOf("20", 8);

System.out.println(x); // 5
System.out.println(y); // 20
System.out.println(z); // 16

toString: число в строку

Мы рассмотрели методы, позволяющие перевести String в int/Integer. Обратная процедура достигается с помощью методов toString. У любого Integer объекта можно вызвать метод toString и получить его строковое представление:
Integer x = 5;
System.out.println(x.toString()); // 5
Однако в связи с тем, что метод toString часто вызывается у объектов неявно (например, при передаче объекта на печать в консоль), данный метод редко используется разработчиками в явном виде. Есть также и статический метод toString, который принимает в себя int параметр и переводит его в строковое представление. Например:
System.out.println(Integer.toString(5)); // 5
Однако, как и не статический метод toString, использование статического в явном виде можно встретить редко. Более интересен статический метод toString, который принимает 2 целочисленных параметра:
  • static String toString(int i, int radix) переведет i в строковое представление в системе счисления radix.
Пример:
System.out.println(Integer.toString(5, 2)); // 101

toBinaryString, toOctalString и toHexString: три ходовые системы счисления

Для трех самых частых систем счисления в классе есть отдельные методы, и указывать radix вручную не нужно:
  • static String toBinaryString(int i) переведет i в двоичную запись;
  • static String toOctalString(int i) в восьмеричную;
  • static String toHexString(int i) в шестнадцатеричную.
Примеры:
System.out.println(Integer.toBinaryString(5));  // 101
System.out.println(Integer.toOctalString(64));  // 100
System.out.println(Integer.toHexString(255));   // ff
Есть у этой троицы и важное отличие от toString(int i, int radix). Они работают с числом как с беззнаковым: в документации сказано «Returns a string representation of the integer argument as an unsigned integer in base 2», и то же самое про базы 8 и 16. На положительных числах разницы не видно, а на отрицательных она принципиальная:
System.out.println(Integer.toString(-1, 2));    // -1
System.out.println(Integer.toBinaryString(-1)); // 11111111111111111111111111111111
Кстати, для третьего задания из домашки пригодится именно toOctalString.

max и min: наибольшее и наименьшее из двух

В классе Integer есть пара методов для нахождения максимума и минимума из двух чисел:
  • static int max(int a, int b) вернет наибольшее из значений среди переданных переменных;
  • static int min(int a, int b) вернет наименьшее из значений среди переданных переменных.
Примеры:
int x = 4;
int y = 40;

System.out.println(Integer.max(x,y)); // 40
System.out.println(Integer.min(x,y)); // 4

Заключение

В данной статье мы рассмотрели класс Integer. Поговорили о том, что это за класс, что такое классы обертки. Рассмотрели класс с практической точки зрения. Разобрали примеры арифметических операций, в том числе операций сравнения. Взглянули на тонкости сравнения двух Integer переменных, разобрали такое понятие как кэшированные объекты. Также мы упомянули о таком явлении, как автоупаковка и распаковка примитивных типов данных. Кроме того, мы успели рассмотреть некоторые методы класса Integer, а также некоторые константы. Привели примеры перевода чисел из одной системы счисления в другую.

Домашнее задание

  1. Изучить, какие еще есть методы класса Integer (изучать можно на сайте с официальной документацией), написать в комментариях, какой из изученных вами методов (без учета тех, которые приведены в статье) наиболее полезен по вашему мнению (будет использоваться вами наиболее часто). А также привести обоснование своего мнения.

    P.S. Тут нет правильных ответов, но данная активность позволит лучше изучить класс.

  2. Решить небольшую простенькую задачку для закрепления материала.

    У нас есть два числа:

    1100001001 в двоичной системе счисления
    33332 в пятеричной системе счисления

    Необходимо используя только методы класса Integer определить максимум среди двух данных чисел, а затем вывести разницу между максимальным и минимальным значением в троичной системе счисления.

  3. Перевести максимально возможное значение Integer в восьмеричную систему счисления и вывести на экран количество цифр в полученном числе (количество считать программно).

Вопросы и ответы

Чем Integer отличается от int?

Int это примитив: восемь байт памяти под само число и никаких методов. Integer это объект-обертка, внутри которого лежит то же самое число, но снаружи есть методы, константы и возможность быть null. Примитив всегда имеет значение, объект может его не иметь. Поэтому в коллекциях и дженериках используется именно Integer: они умеют работать только с объектами.

Почему два Integer нельзя сравнивать через ==?

Потому что Integer это ссылочный тип, и оператор == сверяет не числа, а ссылки. Две переменные с одинаковым значением могут указывать на разные объекты, и тогда == вернет false. Для сравнения значений есть метод equals, он смотрит именно на число внутри обертки.

Почему тогда для маленьких чисел == все-таки возвращает true?

Из-за кэша. Значения от -128 до 127 включительно виртуальная машина кэширует: при автоупаковке она не создает новый объект, а отдает ссылку на уже готовый из кэша. Поэтому две переменные со значением 1 указывают на один и тот же объект и == дает true, а две переменные со значением 2020 указывают на разные объекты и == дает false. Именно это делает ошибку такой коварной: на маленьких числах код работает и не вызывает подозрений.

Что такое автоупаковка и распаковка?

Автоупаковка это автоматическое превращение примитива в обертку, распаковка это обратное превращение. Строка Integer a = 3 внешне выглядит как присваивание числа, но на самом деле компилятор подставляет туда создание объекта Integer. А строка int x = a наоборот достает число из объекта. Программисту ничего писать не нужно, все происходит само.

Почему арифметика с Integer может упасть с NullPointerException?

Потому что для арифметики значение нужно распаковать, то есть достать число из объекта. Если переменная равна null, доставать не из чего, и виртуальная машина бросает NullPointerException. Причем ошибка возникнет на самой обычной с виду строке вроде a + 1. С примитивом int такого случиться не может, у него всегда есть значение.

Чем parseInt отличается от valueOf?

Результатом. Метод parseInt возвращает примитив int, метод valueOf возвращает объект Integer. Внутри некоторые варианты valueOf просто вызывают parseInt, а потом упаковывают результат. Оба умеют принимать вторым аргументом систему счисления и оба бросают NumberFormatException, если строку разобрать не удалось.

Читайте также