Примитивные типы в Java это восемь встроенных в язык типов данных для простых значений: четыре целочисленных (
byte,
short,
int,
long), два вещественных (
float,
double), символьный
char и логический
boolean (
JLS, раздел 4.2). Переменная примитивного типа хранит само значение, а не ссылку на объект, поэтому не бывает
null и не имеет методов. В этой статье: размер и диапазон каждого типа, суффиксы
L и
f, escape-последовательности, шестнадцатеричная запись, ловушки дробных чисел и советы, с каких типов начинать.
Кратко
- Целые числа: byte (1 байт), short (2), int (4) и long (8). По умолчанию бери int; long нужен, когда значение выходит за пределы примерно 2,1 миллиарда, и тогда литерал пишут с суффиксом L.
- Дробные числа: float (4 байта, около 7 значащих цифр, суффикс f) и double (8 байт, около 16 цифр). Стандартный выбор double; для денег ни тот, ни другой, а BigDecimal.
- char хранит один символ Unicode в 2 байтах и одновременно является числом: (int) 'A' даст 65. Символ пишут в одинарных кавычках, строку в двойных.
- boolean хранит только true или false; в условие if нельзя подставить число, как в C.
- Escape-последовательности \n, \t, \", \\ вставляют в строку символы, которых нет на клавиатуре; подчеркивания в числах (1_000_000) и префикс 0x для шестнадцатеричных чисел делают литералы читаемее.
- 0.1 + 0.2 дает 0.30000000000000004: дробные числа хранятся приближенно, поэтому double сравнивают не через ==, а по разнице меньше небольшого epsilon.
Помню, как впервые открыл IDE и начал писать свою первую программу на Java. Создал переменную, написал
int x = 5, запустил... и понятия не имел, что только что произошло. Почему именно
int? Что за
x? И главное: зачем это все нужно?
Если ты сейчас в похожей ситуации, не переживай, мы все разберем. Сегодня поговорим о примитивных типах данных в Java. Звучит скучно? Ну, это как учить алфавит перед тем, как писать книги. Да, не самое захватывающее занятие, но без этого никуда.
Что вообще такое примитивные типы?
Представь, что Java это огромный склад, а ты кладовщик. Тебе нужно хранить разные вещи: маленькие винтики, огромные коробки, хрупкие стеклянные вазы. Для каждого типа вещей нужна своя полка определенного размера, верно?
Вот примитивные типы в Java это как раз эти "полки" разных размеров для хранения данных. Всего их восемь штук, и каждый предназначен для своего вида информации.
Главное отличие примитивов от объектов (с которыми ты познакомишься позже) в том, что они встроены прямо в язык, работают быстро и занимают фиксированное количество памяти. Это базовые кирпичики, из которых строится все остальное.
Все восемь типов в одной таблице
Чтобы было к чему возвращаться, вот сводка по всем примитивам: размер, диапазон, значение по умолчанию и как выглядит литерал.
| Тип |
Размер |
Диапазон |
По умолчанию |
Пример литерала |
| byte |
1 байт (8 бит) |
от -128 до 127 |
0 |
byte b = 100; |
| short |
2 байта (16 бит) |
от -32,768 до 32,767 |
0 |
short s = 30_000; |
| int |
4 байта (32 бита) |
от -2,147,483,648 до 2,147,483,647 |
0 |
int i = 1_000_000; |
| long |
8 байт (64 бита) |
от -9,223,372,036,854,775,808 до 9,223,372,036,854,775,807 |
0L |
long l = 8_300_000_000L; |
| float |
4 байта (32 бита) |
по модулю от 1.4E-45 до 3.4E38, около 6-7 значащих цифр |
0.0f |
float f = 19.99f; |
| double |
8 байт (64 бита) |
по модулю от 4.9E-324 до 1.8E308, около 15-16 значащих цифр |
0.0 |
double d = 3.14; |
| char |
2 байта (16 бит) |
от 0 до 65,535 (от '\u0000' до '\uffff') |
'\u0000' |
char c = 'A'; |
| boolean |
спецификацией не задан |
true или false |
false |
boolean b = true; |
Значение по умолчанию получают только поля классов и элементы массивов; локальную переменную компилятор заставит инициализировать явно (
JLS, раздел 4.12.5). И еще: у каждого примитива есть класс-обертка (
Integer,
Double,
Boolean и другие), она нужна там, где требуется объект, например в коллекциях: подробности в статье
про обертки, запаковку и распаковку.
Целочисленные типы: когда нужны целые числа
Табл. Характеристики целочисленных типов Java![Таблица характеристик целочисленных типов Java: byte, short, int, long и char, размер в битах, минимальное и максимальное значение]()
Начнем с самых популярных: типов для хранения целых чисел. Их аж пять штук! Сначала я тоже не понимал, зачем столько. Ну есть число 5, и все, какая разница?
А разница вот в чем: каждый тип "съедает" разное количество памяти и может хранить числа в определенном диапазоне.
byte: самый маленький
Размер: 1 байт (8 бит)
Диапазон: от -128 до 127
Честно? Я
byte в реальной работе использую крайне редко. Разве что когда работаешь с картинками или сетевыми пакетами, где каждый байт на счету. Для обычных программ он слишком мелкий.
byte temperature = 25;
byte frozenWater = -10;
// А вот так уже не получится: число слишком большое!
// byte bigNumber = 200; // Ошибка компиляции!
short: чуть побольше
Размер: 2 байта (16 бит)
Диапазон: от -32,768 до 32,767
Тоже редкий гость в коде. Может пригодиться, если у тебя миллион чисел и нужно экономить память. Но обычно проще использовать...
int: твой лучший друг
Размер: 4 байта (32 бита)
Диапазон: от -2,147,483,648 до 2,147,483,647
Вот это да!
int это рабочая лошадка Java. Процентов 90 всех целочисленных переменных в коде именно
int. Диапазон огромный, работает быстро, памяти много не жрет.
int age = 25;
int yearlyIncome = 50000;
int populationOfCity = 1500000;
// Можно записывать большие числа с подчеркиваниями для читаемости
int million = 1_000_000; // Так гораздо понятнее!
System.out.println("Миллион: " + million); // Выведет: Миллион: 1000000
Кстати, видишь эти подчеркивания в числе? Эта фича появилась в Java 7, и она офигенно удобная. Компилятор их просто игнорирует, но для человека читать становится в разы легче.
С Java 10 у локальных переменных тип можно не писать: слово
var заставит компилятор вывести его из значения справа (
JEP 286). Знать об этом стоит, но пока учишь типы, лучше писать их явно: так сразу видно, где
int, а где
double.
var count = 5; // компилятор сам выведет int
var ratio = 2.5; // а здесь будет double
long: для по-настоящему больших чисел
Размер: 8 байт (64 бита)
Диапазон: от -9,223,372,036,854,775,808 до 9,223,372,036,854,775,807
Когда
int уже не хватает (например, работаешь с миллисекундами с начала эпохи Unix или считаешь национальный долг), приходит на помощь
long.
long worldPopulation = 8_300_000_000L; // Обрати внимание на L в конце!
long millisecondsSince1970 = System.currentTimeMillis();
System.out.println("Миллисекунд с 1970 года: " + millisecondsSince1970);
// Выведет что-то типа: Миллисекунд с 1970 года: 1734527654321
Важно! Видишь букву
L в конце числа? Без нее Java подумает, что это
int, и если число слишком большое, получишь ошибку. Можно писать и маленькую
l, но она похожа на единицу, так что лучше использовать большую
L.
char: особенный тип для символов
Размер: 2 байта (16 бит)
Диапазон: от 0 до 65,535 (без знака!)
Технически
char тоже целочисленный, но используется для хранения символов. Java работает с Unicode, поэтому один
char может хранить любой символ: латиницу, кириллицу, иероглифы, эмодзи (ну, почти).
char letter = 'A';
char cyrillic = 'Я';
char digit = '5'; // Это символ, не число!
// Можно записывать через Unicode-код
char smiley = '\u263A'; // ☺
System.out.println(letter); // A
System.out.println(smiley); // ☺
// А еще char это число!
char a = 'A';
System.out.println((int) a); // 65: это код символа 'A' в Unicode
Важный момент: символы пишутся в одинарных кавычках
'A', а строки в двойных
"Hello". Это разные вещи!
Escape-последовательности: как записать "невидимые" символы
Табл. Escape-последовательности, используемые в языке Java![Таблица escape-последовательностей Java: backspace, табуляция, перевод строки, перевод страницы, возврат каретки, кавычки и обратная косая черта с их кодами Unicode]()
А теперь прикольная штука. Как записать символ, которого нет на клавиатуре? Например, перенос строки или табуляцию? Для этого в Java есть специальные комбинации с обратным слэшем
\, они называются escape-последовательности.
Вот самые важные, которые будешь использовать постоянно:
\n: перенос строки (new line). Это твой лучший друг при выводе текста:
System.out.println("Привет\nМир!");
// Выведет:
// Привет
// Мир!
\t: табуляция (tab). Удобно для форматирования текста:
System.out.println("Имя:\tИван\nВозраст:\t25");
// Выведет:
// Имя: Иван
// Возраст: 25
\": двойная кавычка. Нужна, когда хочешь вставить кавычки в строку:
System.out.println("Он сказал: \"Java это круто!\"");
// Выведет: Он сказал: "Java это круто!"
\': одинарная кавычка (для char):
char quote = '\'';
System.out.println(quote); // '
\\: сам обратный слэш. Да, чтобы вывести один слэш, нужно написать два:
System.out.println("Путь к файлу: C:\\Users\\Student\\file.txt");
// Выведет: Путь к файлу: C:\Users\Student\file.txt
Есть еще
\r (возврат каретки),
\b (backspace) и
\f (прогон страницы), но честно, я их за 10 лет ни разу не использовал. Запоминай то, что действительно нужно:
\n,
\t,
\" и
\\.
Системы счисления: шестнадцатеричные числа
Иногда в коде увидишь странные числа типа
0xFF или
0x1A2B. Это шестнадцатеричная система счисления. Программисты ее любят, потому что она удобно связана с байтами.
int hex = 0xFF; // Это 255 в десятичной системе
int anotherHex = 0x1A; // Это 26
System.out.println(hex); // 255
System.out.println(anotherHex); // 26
Совет новичку: не паникуй, когда видишь такие числа. На первых порах просто знай, что
0x в начале означает шестнадцатеричное число. Позже разберешься подробнее.
Есть и двоичная запись с префиксом
0b:
int mask = 0b1010; это 10 в десятичной системе. Она появилась в Java 7 вместе с подчеркиваниями и пригодится там, где важен каждый бит, например при работе с флагами.
А вот восьмеричные числа (которые начинаются с нуля, типа
077) лучше вообще забудь. Они в современном коде почти не используются, а вот проблем могут создать кучу. Случайно напишешь
int x = 010, думая, что это десять, а на самом деле получишь восемь. Неприятно, правда?
Вещественные типы: когда нужны дроби
Табл. Характеристики вещественных типов Java![Таблица характеристик вещественных типов Java: float и double, разрядность, диапазон и точность]()
Хорошо, с целыми числами разобрались. А если нужно работать с дробями? Скажем, посчитать среднюю температуру или цену со скидкой?
float: число с плавающей точкой обычной точности
Размер: 4 байта (32 бита)
Точность: ~6-7 значащих цифр
Честно говоря,
float в реальной работе я использую крайне редко. Он не очень точный и требует добавления буквы
f в конце числа, что бесит.
float price = 19.99f; // Не забывай про f!
float temperature = -3.5f;
System.out.println(price); // 19.99
double: для точных вычислений
Размер: 8 байт (64 бита)
Точность: ~15-16 значащих цифр
Вот это уже серьезно!
double это стандартный выбор для работы с дробными числами в Java. Точнее и удобнее, а по скорости на современных процессорах разницы почти нет.
double pi = 3.14159265359;
double bankBalance = 1500.75;
// Можно использовать научную нотацию
double speed = 3.0e8; // Это 3.0 × 10^8 (скорость света в м/с)
System.out.println("Пи примерно равно: " + pi); // Пи примерно равно: 3.14159265359
System.out.println("Скорость света: " + speed); // Скорость света: 3.0E8
Особенности работы с числами с плавающей точкой
Тут есть один коварный момент, о котором нужно знать сразу. Компьютеры не могут хранить дробные числа абсолютно точно. Посмотри на этот пример:
double a = 0.1;
double b = 0.2;
double sum = a + b;
System.out.println(sum); // Ожидаешь 0.3? А получишь 0.30000000000000004
Да-да, такое бывает! Это не баг Java, а особенность
представления дробных чисел в памяти компьютера. Для большинства задач эта погрешность незаметна, но если работаешь с деньгами, никогда не используй
double! Для финансовых расчетов есть специальный класс
BigDecimal (с ним познакомишься позже на курсе JavaRush).
А еще у
float и
double есть специальные значения:
double positive = 5.0 / 0.0; // Положительная бесконечность
double negative = -5.0 / 0.0; // Отрицательная бесконечность
double notANumber = 0.0 / 0.0; // "Не число" (NaN - Not a Number)
System.out.println(positive); // Infinity
System.out.println(negative); // -Infinity
System.out.println(notANumber); // NaN
// Проверить на NaN можно так:
if (Double.isNaN(notANumber)) {
System.out.println("Это не число!");
}
Когда впервые увидел, что деление на ноль не вызывает ошибку, а дает бесконечность, был в шоке. Но это на самом деле удобно: программа не падает, и ты можешь проверить результат.
boolean: истина или ложь
И наконец, самый простой тип.
boolean может хранить только два значения:
true (истина) или
false (ложь). Все.
Размер: спецификация языка его не задает. В вычислениях JVM обращается с
boolean как с
int, а в массивах JVM от Oracle отводит на каждое значение 1 байт (
JVMS, раздел 2.3.4). Так что "1 бит" это миф: меньше байта не бывает.
boolean isJavaFun = true;
boolean isEasy = false;
boolean iAmLearning = true;
// Чаще всего используется в условиях
if (isJavaFun) {
System.out.println("Java это весело!"); // Это выполнится
}
if (isEasy) {
System.out.println("Java легко дается"); // А это нет
}
В отличие от C, где
0 означало ложь, а любое другое число истину, в Java для логических значений есть отдельный тип. Это круто, потому что избавляет от кучи ошибок. Попробуешь написать
if (1), и компилятор сразу ругнется.
Практические советы для начинающих
После всей этой теории дам несколько советов из личного опыта:
1. Начни с int и double
На первых порах используй только
int для целых чисел и
double для дробных. Остальные типы освоишь по мере необходимости. Серьезно, не усложняй.
2. Не экономь память без причины
Видел код, где новички пытаются везде использовать
byte вместо
int "для экономии памяти". Друзья, сегодня это не имеет смысла. Используй
byte только когда действительно нужно (работа с сетью, файлами, изображениями).
3. Помни про L и f
Для
long добавляй
L в конце, для
float букву
f. Без них получишь ошибки или неожиданное поведение.
4. Не сравнивай double через ==
Из-за погрешности вычислений два
double могут быть почти равны, но не точно равны. Лучше проверяй, что разница между ними меньше какого-то маленького числа (epsilon).
double a = 0.1 + 0.2;
double b = 0.3;
// Плохо:
if (a == b) { // Может не сработать!
System.out.println("Равны");
}
// Хорошо:
double epsilon = 0.0001;
if (Math.abs(a - b) < epsilon) {
System.out.println("Практически равны"); // Это сработает
}
Итог
Поздравляю! Ты только что освоил фундамент Java. Примитивные типы это как буквы алфавита. Сами по себе они простые, но из них строится все остальное.
Дальше тебя ждут операторы (сложение, вычитание, сравнение), условия (
if-
else), циклы... и куча интересного! Так что вперед! Следующий шаг: узнать, что со всеми этими типами можно делать. А там уже недалеко и до первой настоящей программы.
Вопросы и ответы
Сколько примитивных типов в Java и какие они?
Восемь: четыре целочисленных (
byte,
short,
int,
long), два вещественных (
float,
double), символьный
char и логический
boolean. Строка
String не примитив, а класс. Список закреплен в спецификации языка (JLS, раздел 4.2) и не менялся с первой версии Java.
Чем примитивный тип отличается от ссылочного?
Переменная примитивного типа хранит само значение, а ссылочная (
String, массив, любой объект) хранит адрес объекта в памяти. Примитив не может быть
null, у него нет методов, и он занимает фиксированный объем памяти. Чтобы положить примитив в коллекцию, его заворачивают в класс-обертку:
Integer для
int,
Double для
double и так далее.
Какое значение по умолчанию у примитивных типов?
Для полей класса и элементов массива: 0 у
byte,
short,
int и
long, 0.0 у
float и
double,
'\u0000' у
char и
false у
boolean (JLS, раздел 4.12.5). Локальная переменная в методе значения по умолчанию не получает: если использовать ее без инициализации, компилятор выдаст ошибку.
Почему 0.1 + 0.2 в Java не равно 0.3?
float и
double хранят числа в двоичном виде по стандарту IEEE 754, а 0.1 и 0.2 в двоичной системе это бесконечные дроби, которые приходится округлять. Сумма округленных значений дает 0.30000000000000004. Поэтому
double не сравнивают через
==, а проверяют, что разница меньше небольшого epsilon, а для денег берут
BigDecimal.
Чем float отличается от double и что выбирать?
float занимает 4 байта и держит около 7 значащих цифр,
double 8 байт и около 16 цифр. Литерал без суффикса (
3.14) уже имеет тип
double, для
float нужен суффикс
f. По умолчанию выбирай
double: он точнее, а по скорости на современных процессорах разницы почти нет.
float оправдан там, где важна память, например в больших массивах или графике.
Сколько памяти занимает boolean в Java?
Спецификация языка размер
boolean не задает. Внутри JVM с ним работают как с
int, а в массивах JVM от Oracle отводит на каждое значение 1 байт (JVMS, раздел 2.3.4). Так что "1 бит" это миф: меньше байта
boolean не занимает, а в вычислениях на стеке ведет себя как 4-байтовый
int.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ