Типы данных в Java: какие бывают, чем различаются и что такое ссылки и примитивы
Рассказываем, как джависту не запутаться во всех этих byte, short, boolean, char и String.


Основа любого языка программирования — данные и операции с ними. Java не исключение. Это строго типизированный язык, поэтому типы данных значат в нём очень многое.
Java следит за тем, чтобы все переменные, выражения и значения соответствовали своему типу данных. Поэтому операции, которые допустимы для одного типа, нельзя провести с другим — тип переменной определяет, какие операции с ней можно выполнить.
Когда код на Java компилируется, машина проверяет соответствие типов операндов во всех методах, конструкторах и других операторах. Если в программе есть хотя бы одна недопустимая операция, компилятор не превратит её в байт-код. Поэтому контроль типов данных помогает уменьшить количество ошибок при написании программы.
В этой статье мы рассмотрим:
- какие типы данных есть в Java;
- чем различаются примитивные и ссылочные переменные;
- какие у переменных бывают значения по умолчанию;
- как упаковка и распаковка помогают превратить примитивные переменные в объект.
Какие типы данных есть в Java
В Java типы данных делят на две большие группы: примитивные и ссылочные. В состав примитивных типов (или просто примитивов) входят четыре подвида и восемь типов данных:
1) целые числа (byte, short, int, long);
2) числа с плавающей точкой (float, double);
3) логический (boolean);
4) символьный (char).
Ссылочные типы данных ещё называют ссылками. К ним относятся все классы, интерфейсы, массивы, а также тип данных String.
Хотя у примитивов и ссылок много общего, между ними есть существенные различия. И главное различие — в том, что именно в них хранится.
Примитивные переменные | Ссылочные переменные |
---|---|
Хранят значение | Хранят адрес объекта в памяти, на который ссылаются (отсюда и название). Используются для доступа к объектам (его нельзя получить, если на объект нет ссылки) |
Создаются присваиванием значения | Создаются через конструкторы классов (присваивание только создаёт вторую ссылку на существующий объект) |
Имеют строго заданный диапазон допустимых значений | По умолчанию их значение — null |
В аргументы методов попадают копии значения переменной (это передача по значению) | В методы передаётся значение ссылки — операция выполняется над оригинальным объектом, на который ссылается переменная |
Могут использоваться для ссылки на любой объект объявленного или совместимого типа |
Вот пример использования примитивных и ссылочных типов данных:
int a = 5;
int b = a; //мы создали две переменных и два различных значения, которые содержат число 5
Cat barsik = new Cat(); //мы создали объект Cat, и переменной barsik присвоена ссылка на этот объект
Cat murka = barsik; //создали две ссылки на один и тот же объект Cat
Значения переменных по умолчанию
Как мы уже отмечали, в зависимости от типа данных у каждой переменной есть значение по умолчанию. Оно присваивается при её создании.
В этом примере значения по умолчанию получат все переменные:
private int a;
private double b;
private String text;
А в этом примере значения получают только переменные класса: когда мы создадим класс Cat, по умолчанию weight будет равен 0.0.
public class Cat {
private double weight;
public void setWeight(double weight) {
this.weight = weight;
}
public double getWeight() {
return weight;
}
}
Но локальные переменные нужно инициировать сразу при создании. Если написать просто int sum; , при использовании переменной компилятор выдаст ошибку: java: variable a might not have been initialized.
private static int sum(int[] prices) {
int sum = 0;
for (int price : prices) {
sum += price;
}
return sum;
}
У примитивов есть строгие рамки допустимых значений по умолчанию и диапазоны значений — для удобства мы собрали их в таблицу.

Как используют целочисленные переменные
Целочисленные типы данных различаются только диапазонами значений. Их основная задача — хранить информацию для вычислений.
Тип byte. Эти переменные используют, чтобы работать с потоком данных, который получили из файла или по сети.
//объявляем переменные с типом данных byte
byte a;
byte b;
byte c;
//инициализируем переменные значениями
a = 0;
b = 0;
c = -129; //это пример ошибки во вводе данных. Такое значение не входит в диапазон значений byte
//выводим данные из переменных
System.out.println(a);
System.out.println(b);
Тип short. По сравнению с byte у него увеличенный, но всё же ограниченный диапазон значений. Применяют short редко — например, когда нужно экономить память.
//инициализируем переменную типа short
short primerShort = 255;
Тип int. В языке Java int — самый популярный тип целочисленных данных. При вычислениях в виртуальной машине остальные целочисленные типы (byte, short) занимают столько же памяти, сколько int.
//инициализируем переменную типа int
int closeToMiddle = 0;
Множество классов в Java обладают значениями типа int — например, длина массива внутри класса String выражается целочисленным значением int:
String[] strings = new String[50];
int arrayLength = strings.length;
Если переменная хранит количество элементов в коллекциях List, Set и Map, она тоже относится к типу int:
List<String> strings = new ArrayList<>();
int listSize = strings.size();
Тип возвращаемого значения подсказывает, сколько элементов можно хранить в списке или множестве. Максимум для int — 2 147 483 647.
Тип long применяют, когда нужно работать с большими целочисленными значениями.
//инициализируем переменные типа long
long c = -9223372036854; //при компиляции появится ошибка
long right = -9223372036854L;
По умолчанию компилятор воспринимает целое число как int, а 9 223 372 036 854 намного больше его максимального значения, поэтому в коде программы нужно явно указать тип long.
Зачем нужны числа с плавающей точкой
Тип данных double используют для работы с десятичными числами.
//инициализируем переменную типа double
double sample = 59.36;
Тип float используют как экономичный вариант хранения больших массивов данных с плавающей точкой.
Когда переменной присваивают тип float, язык Java воспринимает её как тип данных double. Чтобы этого не происходило, нужно добавлять в конце переменной символ f или F.
Даже если у переменных float и double будут одинаковые значения, язык Java обработает их по-разному, поэтому они будут занимать разный объём памяти.
//инициализация переменных типа float
float f = 1.457; //Java воспримет эту переменную как тип данных double и выдаст ошибку компиляции
float floatNumber = 27.5f; //правильный способ
float otherFloat = (float) 78.64; //другой вариант инициализации переменной типа float во избежание путаницы с double
Не стоит использовать float, когда в вычислениях нужна точность больше пяти знаков после запятой. Oracle пишет об этом в статье «Primitive Data Types».
Логический и символьный типы данных
Чтобы работать с логическими значениями, используют тип данных boolean — это его единственное применение. У такой переменной может быть только два значения: false (ложь) и true (истина).
//инициализируем переменные типа boolean
boolean isWorking = true;
boolean isAlive = false;
В Java boolean — отдельная переменная. Это не аналог 1 или 0, как, например, в JavaScript и PHP.
Тип данных char используют, чтобы хранить в переменных любые 16-разрядные символы Unicode. Но их нужно записывать строго в одинарные кавычки ' ', и только по одному.
Не стоит путать символьные и строковые переменные — 'ж' не равно "ж", потому что в двойных кавычках хранится тип данных String. А это уже не примитив.
Пример кода
//инициализируем переменные типа char
char symbol1 = 1078; //по индексу символа в таблице UTF-8
char symbol2 = 'ж'; //по значению символа
char symbol3 = '\u0436'; //через шестнадцатеричную форму Unicode (это всё ещё «ж»)
//вызываем вывод информации
System.out.println("symbol1 contains " + symbol1);
System.out.println("symbol2 contains " + symbol2);
System.out.println("symbol3 contains " + symbol3);
//во всех случаях будет выдан один и тот же символ — «ж»
Вывод в консоли
symbol1 contains ж
symbol2 contains ж
symbol3 contains ж
Значения по умолчанию для ссылочных типов данных
В плане дефолтных значений ссылочные переменные проще примитивов. По умолчанию их значение — null: это означает отсутствие ссылки или то, что ссылка ни на что не указывает.
Но если вызвать метод объекта от переменной со значением null, это приведёт к ошибке NullPointerException:
Cat barsik = null;
barsik.meow(); //здесь появится ошибка NullPointerException, потому что у переменной barsik значение null
В ссылочные типы данных входит и String — это класс из стандартной библиотеки Java. Он не относится к примитивам, но его повсеместно используют, чтобы хранить текстовые данные.
Пример использования String:
//создаём строку двойными кавычками ""
String string = "This day was awesome";
//альтернативный способ создания строки — через конструктор
String string = new String ("This day was awesome");
Строчные переменные можно склеивать оператором +, который используют для конкатенации.
Пример кода
String stringFirst = "New adventures ";
String stringSecond= "are waiting ";
String stringThird = "for you";
String string = stringFirst + stringSecond + stringThird;
System.out.println(string);
Вывод в консоли
New adventures are waiting for you
Boxing и unboxing — как превратить примитив в объект
Иногда с примитивами приходится работать как с объектами — например, передавать им значение по ссылке или создавать список из чисел (а списки работают только с объектами).
Поэтому у каждого примитива есть соответствующий ему ссылочный тип — его называют классом-обёрткой. В таких классах хранятся методы для преобразования типов данных, а также другие константы и методы, которые применяются при работе с примитивами.
Тип данных | Класс-обёртка |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
Ссылочные типы данных (обёртки) пишут с прописной буквы, потому что это полноценные классы. А в Java названия всех классов должны начинаться с большой буквы — язык чувствителен к регистру.
Чтобы создать ссылку на примитивный тип данных, нужно использовать соответствующую обёртку:
//пример использования классов-оболочек (упаковка)
long g = 5509768L;
Long boxed;
boxed = new Long(g); //обычное создание через конструктор
boxed = Long.valueOf (g); //фабричный метод
boxed = g; //автоматическая упаковка. Компилятор заменит её на вызов Long.valueOf(g)
Если использовать valueOf, процесс упаковывания становится проще и быстрее, потому что он проводит кэширование и потребляет меньше памяти, а конструктор всегда создаёт новый объект.
//пример использования классов-оболочек (распаковка)
Long boxed = 200L;
long g;
g = boxed.longValue(); //явная распаковка
g = boxed; //автоматическая распаковка
Классы-обёртки полезны, когда нужно одновременно работать и с числами, и с объектами — например, в коллекциях.
В этой статье мы рассмотрели примитивные типы данных (byte, short, int, long, float, double, char и boolean), ссылочные типы данных (String и остальные). Вы узнали, чем они отличаются друг от друга и какие значения принимают по умолчанию.
В следующей статье мы расскажем, что можно делать с этими переменными с помощью арифметических и логических операторов языка Java.