Java: Какие бывают типы
В этом уроке мы рассмотрим систему типов в Java с высоты птичьего полета, не погружаясь в детали. Но сначала ответим на вопрос, зачем вообще про них знать?
В коде мы все время оперируем данными. Эти данные имеют разную природу, могут быть по-разному организованы, что влияет на удобство работы с ними. Типы преследуют нас на каждом шагу, поэтому без них программирование на Java возможно только на очень базовом уровне.
С другой стороны, не пытайтесь запомнить всю эту информацию про типы наизусть — она дается лишь для общего представления. Все важное о типах вы и так выучите в процессе программирования. Глобально, типы данных в Java делятся на две большие группы:
- Примитивные — предопределены в Java
- Ссылочные или не примитивные — создаются самим программистом, за исключением String и Array
У этих групп есть различия, которые мы разберем позже, когда познакомимся с null и объектно-ориентированным программированием. Пока достаточно знать, что имена примитивных типов начинаются с нижнего регистра ( int ), а ссылочных с верхнего ( String ).
Всего в Java восемь примитивных типов данных:
Рассмотрим первые четыре типа. Это целые числа разного размера:
- byte — занимает в памяти 1 байт, значит может хранить числа от -128 до 127
- short — занимает в памяти 2 байта
- int — занимает в памяти 4 байта
- long — занимает в памяти 8 байт
Посмотрим на примере такого кода:
byte x = 3; // Отработает без проблем // Error: incompatible types: possible lossy conversion from int to byte byte y = 270;
Определение переменной y завершилось с ошибкой, потому что мы указали тип byte, но присвоили переменной значение 270, которое выходит за множество допустимых значений.
Возникает закономерный вопрос. Зачем аж четыре типа для хранения чисел? Почему бы не сделать один, в который влезает почти любое большое число?
Технически так сделать можно, но мы находимся в мире инженерных решений. У любого решения всегда есть обратная сторона, поэтому невозможно сделать идеально — придется чем-то пожертвовать. В данном случае, объемом занимаемой памяти. Если оставить только long, то программа, активно оперирующая числами, начнет занимать слишком много места в оперативной памяти, что может быть критично.
Такая же логика использовалась для типов float и double. Они оба отвечают за рациональные числа. Разница лишь в том, что double — это двойной float, то есть в памяти он занимает в два раза больше места.
Создатели Java полагаются на разумность программистов, на их способность правильно подобрать нужные типы в зависимости от задачи. Для каких-то экстремальных приложений так и происходит, но в типичной разработке все просто. Программисты выбирают int для целых чисел и double для рациональных.
Рассмотрим оставшиеся типы данных.
Тип boolean отвечает за логические значения true и false . Им посвящен целый раздел, там мы про него и поговорим.
Особняком стоит тип char — символ. Это не строка, у него другой способ определения — через одиночные кавычки:
char ch = 'a'; // Error: incompatible types: java.lang.String cannot be converted to char char ch2 = "b";
Строка, состоящая из одного символа — это не символ. Кажется, нелогично, но с точки зрения типов все так и должно быть, со временем вы это прочувствуете.
Извлечение символа из строки извлекает как раз символ, а не строку, состоящую из одного символа:
"hexlet".charAt(1); // 'e'
Хорошо, а где тип данных String — строка? Дело в том, что она не является примитивным типом. Внутри она представляет собой массив символов. Несмотря на это техническое различие, строки используются наравне с примитивными типами без особых отличий.
Задание
Выведите на экран результат конкатенации слова hexlet, символа — и числа 7
Упражнение не проходит проверку — что делать?
Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:
- Обязательно приложите вывод тестов, без него практически невозможно понять что не так, даже если вы покажете свой код. Программисты плохо исполняют код в голове, но по полученной ошибке почти всегда понятно, куда смотреть.
В моей среде код работает, а здесь нет
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Мой код отличается от решения учителя
Это нормально , в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.
В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Прочитал урок — ничего не понятно
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.
Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.
Полезное
- Попробуйте поиграть с конкатенированием строк и символов в jshell
- Статья о дробных числах
Java/Типы данных
В Java есть 8 примитивных типов, которые делят на 4 группы, вот они:
- Целые числа — byte, short, int, long
- Числа с плавающей точкой (иначе вещественные) — float, double
- Логический — boolean
- Символьный — char
Целочисленные типы [ править ]
Целочисленные типы различаются между собой только диапазонами возможных значений, например, для хранения номера элемента в таблице Менделеева пока хватит переменной типа byte.
| Тип | Размер (бит) | Диапазон |
|---|---|---|
| byte | 8 бит | от -128 до 127 |
| short | 16 бит | от -32768 до 32767 |
| char | 16 бит | беззнаковое целое число, представляющее собой символ UTF-16 (буквы и цифры) |
| int | 32 бит | от -2147483648 до 2147483647 |
| long | 64 бит | от -9223372036854775808L до 9223372036854775807L |
Пример использования целочисленных типов:
public class IntegralTypes public static void main(String[] args) byte b = 216; // Вот тут будет ошибка, т.к. у нас диапазон от -128 до 127! short s = 1123; int i = 64536; long l = 2147483648L; // Постфикс l или L обозначает литералы типа long System.out.println(i); System.out.println(b); System.out.println(s); System.out.println(l); > >
Символы тоже относят к целочисленным типам из-за особенностей представления в памяти и традиций.
public class Characters public static void main(String[] args) char a = 'a', b, c = 'c'; b = (char) ((a + c) / 2); // Можно складывать, вычитать, делить и умножать // Но из-за особенностей арифметики Java результат приходится приводить к типу char явно System.out.println(b); // Выведет символ 'b' > >
Типы с плавающей точкой [ править ]
| Тип | Размер (бит) | Диапазон |
|---|---|---|
| float | 32 | от 1.4e-45f до 3.4e+38f |
| double | 64 | от 4.9e-324 до 1.7e+308 |
public class FloatingPointTypes public static void main(String[] args) double a, b = 4.12; a = 22.1 + b; float pi = 3.14f; // При использовании типа float требуется указывать суффикс f или F // так как без них типом литерала будет считаться double float anotherPi = (float) 3.14; // Можно привести явно double c = 27; double d = pi * c; System.out.println(d); > >
Логический тип [ править ]
| Тип | Размер (бит) | Значение |
|---|---|---|
| boolean | 8 (в массивах), 32 (не в массивах используется int) | true (истина) или false (ложь) |
В стандартной реализации Sun JVM и Oracle HotSpot JVM тип boolean занимает 4 байта (32 бита), как и тип int. Однако, в определенных версиях JVM имеются реализации, где в массиве boolean каждое значение занимает по 1-му байту.
Ссылочные [ править ]
Ссылочные типы — это все остальные типы: классы, перечисления и интерфейсы, например, объявленные в стандартной библиотеке Java, а также массивы.
Строки [ править ]
Строки это объекты класса String, они очень распространены, поэтому в некоторых случаях обрабатываются отлично от всех остальных объектов. Строковые литералы записываются в двойных кавычках.
public class Strings public static void main(String[] args) String a = "Hello", b = "World"; System.out.println(a + " " + b); // Здесь + означает объединение (конкатенацию) строк // Пробел не вставляется автоматически // Строки конкатенируются слева направо, надо помнить это когда соединяешь строку и примитив String c = 2 + 2 + ""; // "4" String d = "" + 2 + 2; // "22" d = "" + (2 + 2); // а теперь d тоже "4" String foo = "a string"; String bar = "a string"; // bar будет указывать на тот же объект что и foo String baz = new String("a string"); // Чтобы гарантированно создать новую строку надо вызвать конструктор System.out.println("foo == bar ? " + (foo == bar)); // == сравнивает ссылки на объекты System.out.println("foo равен bar ? " + (foo.equals(bar))); // Метод equals служит для проверки двух объектов на равенство System.out.println("foo == baz ? " + (foo == baz)); System.out.println("foo равен baz ? " + (foo.equals(baz))); > >
Эта программа выведет:
Hello World
foo == bar ? true
foo равен bar ? true
foo == baz ? false
foo равен baz ? true
Обертки [ править ]
Если требуется создать ссылку на один из примитивных типов данных, необходимо использовать соответствующий класс-обертку. Также в таких классах есть некоторые полезные методы и константы, например минимальное значение типа int можно узнать использовав константу Integer.MIN_VALUE. Оборачивание примитива в объект называется упаковкой (boxing), а обратный процесс распаковкой (unboxing).
| Тип | Класс-обертка |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| char | Character |
| float | Float |
| double | Double |
| boolean | Boolean |
int i; Integer boxed; // Обычное создание объекта boxed = new Integer(i); // Фабричный метод boxed = Integer.valueOf(i); // Автоматическая упаковка, компилятор просто вставит вызов Integer.valueOf boxed = i;
Рекомендуется использовать valueOf, он может быть быстрее и использовать меньше памяти потому что применяет кэширование, а конструктор всегда создает новый объект.
Получить примитив из объекта-обертки можно методом Value.
Integer boxed; int i; // Явная распаковка i = boxed.intValue(); // Автоматическая распаковка i = boxed;
Примитивные типы Java


Как уже говорилось, в Java определены следующие примитивные типы:
- целые типы;
- вещественные типы;
- булевский тип.
За оператором объявления примитивной переменной может следовать оператор инициализации » = «, с помощью которого созданной переменной присваивается начальное значение.
1. Целые типы переменных

Целые типы различаются по размеру отведенной для них памяти. Характеристики целочисленных типов приведены в табл. 1.1. Табл. 1.1. Характеристики целочисленных типов Java Как видно из приведенной таблицы, целые переменные, за исключением типа char , считаются в языке Java переменными со знаком. Целочисленные константы могут задаваться в программе одним из трех способов: в виде десятичных, шестнадцатеричных и восьмеричных значений. По умолчанию все числа интерпретируются как десятичные и относятся к типу int . Явно указать принадлежность к типу long можно, добавив в конце числа букву «l» или букву «L» . Шестнадцатеричное значение задается с помощью символов «0x» или «0X» , за которым значение числа (цифры 0-9 и буквы A-F или a-f ), например: 0x7FFF . Число в восьмеричной записи должно начинаться с нуля, за которым следует одна или несколько восьмеричных цифр, например 077777 . Восьмеричные и шестнадцатеричные числа могут быть как положительными, так и отрицательными и изменяются в тех же диапазонах, что и числа в десятичном представлении (например, шестнадцатеричные числа типа byte имеют максимальное значение 0x7F и минимальное значение – 0x80 , а восьмеричные – соответственно 177 и – 200 ) Примеры объявления целых переменных:
int x = 0; long i, j, k; byte a1 = 0xF1, a2 = 0x07; short r1 = 017;
Символы в Java определяются с помощью ключевого слова char и реализованы с использованием стандарта Unicode. Можно задать константу-символ в программе или как обычный символ. Символьное значение должны быть заключено в пару одиночных апострофов, например:
char symbol='f';
Другой способ записи символов: пара символов «\u» , за которой следует четырехзначное шестнадцатеричное число (в диапазоне от 0000 до FFFF ), представляющее собой код символа в Unicode, например:
char symbol = '\u0042';

Некоторые символы, отсутствующие на клавиатуре, можно задавать с помощью так называемых escape-последовательностей, содержащих символ » \ «, за которым следует буквенный символ, идентифицирующий escape-последовательность, как показано в табл. 1.2. Табл. 1.2. Escape-последовательности, используемые в языке Java
2. Вещественные типы переменных

Язык Java поддерживает числа и переменные с плавающей точкой обычной и двойной разрядности – типы float и double . Для чисел с плавающей точкой нужно указывает целую и дробную часть, разделенные точкой, например 4.6 или 7.0 . Для больших чисел можно использовать экспоненциальную форму записи (для отделения мантиссы от порядка используется символ «e» или символ «E» ), например, число -3,58×107 записывается как –3.58E7 , а число 73,675×10-15 – как 73.675e-15 . Характеристики вещественных типов Java представлены в табл. 2.1. Табл. 2.1. Характеристики вещественных типов Java Переменные с плавающей точкой могут хранить не только численные значения, но и любой из особо определенных флагов (состоянии): отрицательная бесконечность, отрицательный нуль, положительная бесконечность, положительный нуль и «отсутствие числа» (not-a-number, NaN ). Все константы с плавающей точкой подразумеваются принадлежащими к типу double . Чтобы задать число типа float , необходимо добавить в его конец символ «f» или символ «F» . Примеры объявления переменных с плавающей точкой:
float x1 = 3.5f, x2 = 3.7E6f, x3 = -1.8E-7f; double z = 1.0;
3. Булевский тип переменных
Переменные булевского типа (логические переменные) могут принимать одно из двух значений: «истина» или «ложь» и используются в языках программирования в операциях отношения (сравнения) и логических операциях. Так, результатом сравнения
5 > 3
будет «истина», а результатом сравнения
будет «ложь». В отличие от C, где результату «ложь» сопоставлено целое значение типа int , равное 0, а результату «истина» – ненулевое значение типа int , и, соответственно, результатам сравнения присваивается целое значение (обычно 0 или 1), в Java для булевских переменных введен свой, отдельный тип данных. Переменные булевского типа в Java задаются с помощью ключевого слова boolean и могут иметь лишь одно из двух значений: true или false, например
boolean switch = true;
Типы данных в Java: какие бывают, чем различаются и что такое ссылки и примитивы
Рассказываем, как джависту не запутаться во всех этих byte, short, boolean, char и String.



Василий Порядин
Программист, преподаватель Skillbox. Пишет про Java.
Основа любого языка программирования — данные и операции с ними. Java не исключение. Это строго типизированный язык, поэтому типы данных значат в нём очень многое.
Java следит за тем, чтобы все переменные, выражения и значения соответствовали своему типу данных. Поэтому операции, которые допустимы для одного типа, нельзя провести с другим — тип переменной определяет, какие операции с ней можно выполнить.
Когда код на Java компилируется, машина проверяет соответствие типов операндов во всех методах, конструкторах и других операторах. Если в программе есть хотя бы одна недопустимая операция, компилятор не превратит её в байт-код. Поэтому контроль типов данных помогает уменьшить количество ошибок при написании программы.
В этой статье мы рассмотрим:
- какие типы данных есть в Java;
- чем различаются примитивные и ссылочные переменные;
- какие у переменных бывают значения по умолчанию;
- как упаковка и распаковка помогают превратить примитивные переменные в объект.
Какие типы данных есть в Java
В Java типы данных делят на две большие группы: примитивные и ссылочные. В состав примитивных типов (или просто примитивов) входят четыре подвида и восемь типов данных:
2) числа с плавающей точкой (float, double);
3) логический (boolean);
4) символьный (char).
Ссылочные типы данных ещё называют ссылками. К ним относятся все классы, интерфейсы, массивы, а также тип данных String.
Хотя у примитивов и ссылок много общего, между ними есть существенные различия. И главное различие — в том, что именно в них хранится.
| Примитивные переменные | Ссылочные переменные |
|---|---|
| Хранят значение | Хранят адрес объекта в памяти, на который ссылаются (отсюда и название). |
Вот пример использования примитивных и ссылочных типов данных:

Как используют целочисленные переменные
Целочисленные типы данных различаются только диапазонами значений. Их основная задача — хранить информацию для вычислений.
Тип 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:
ListString> 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.
Читайте также:
- Ключевое слово var в Java: что, зачем и почему
- 7 этапов взросления программиста: Михаил Флёнов о пути в профессию
- Ползай, как муравей, летай, как пчела: алгоритмы, которые придумала сама природа