Примитивные типы данных
Мы несколько раз упоминали, что Java — строго типизированный язык. На практике это означает, что каждая переменная может хранить определенный тип данных, т.е. если мы говорим, что переменная х хранит значение 5, то мы должны указать явно, что х — целочисленная переменная; в дальнейшем она сможет хранить исключительно целые числа (строку либо дробное число мы записать в эту переменную уже не сможем).
По этой же логике, если переменная y = 4.5, то мы явно объявляем, что она хранит вещественные числа. С одной стороны, такая строгая типизация накладывает лишние ограничения на разработчика, с другой — она уменьшает вероятность ошибок и делает процесс разработки более стандартизированным.
int x = 365; //создали целочисленную переменную х double y = 3.14; //создали вещественную переменную у
Вселенная Java базируется на 2 столпах: примитивах и объектах.
Объекты — это сложные данные, доступ к которым можно получить по ссылке в оперативной памяти. С ними мы познакомимся отдельно.
Примитивы — это простые данные, которые хранят в себе определенные значения (например, число 7 или любое другое, которое вам нравится.
Существует 8 примитивных типов данных. У них есть 2 отличия:
- диапазон значений, которые они могут хранить (например, числа в определенном диапазоне, логические значение true/false либо символы таблицы Юникод);
- размер выделенной для них памяти.
Рассмотрим примитивные типы, разделив их на 3 группы:
Категория | Тип | Размер (бит) | MIN | MAX | Пример |
---|---|---|---|---|---|
byte | 8 | -128 | 127 | byte b = 65; | |
char | 16 | 0 | 65536 | char c = ‘A’; char c = 65; | |
Целочисленные | short | 16 | -32768 | 32767 | short s = 65; |
int | 32 | -2147483648 | 2147483647 | int i = 65; | |
long | 64 | -9223372036854775808L | 9223372036854775807L | long l = 65L; long l = 65; | |
С плавающей | float | 32 | -1.4e-45f | -1.4e-45f | float f = 65.55f; |
точкой | double | 64 | -4.9e-324 | 1.7e+308 | double d = 65.55; |
Логический тип | boolean | — | false, true | boolean b = true; |
Позволим себе следующую аналогию. Когда мы объявляем переменную какого-то типа, мы как бы говорим системе: выдели нам столько-то места в оперативной памяти — я туда хочу записать значение в определенном диапазоне. Чем больше значений эта переменная сможет хранить — тем больше места для себя она потребует. Не важно, на сколько вы его заполните: полностью или совсем чуть-чуть — эта память будет выделена полностью в соответствии с требованиями.
1. Целые числа — byte, short, char, int, long
Эти типы данных хранят целые числа (за исключением char , который хранит символы по стандарту Unicode); В Табличке указаны минимальные и максимальные значения.
Например, если вы хотите в переменную записать число 2500000000 то для него нужно создать переменную типа long , т.к. только она из целочисленных сможет вместить данное значение (2500000000 > максимального значения диапазона integer (2147483647)). Создав переменную типа long , вы выделите в оперативной памяти 64 бита (или 8 байт) под нее. Кстати, когда будете выбирать тип переменной, учитывайте то, как она будет изменяться в будущем, потому как данный тип должен быть способным вместить значения переменной, в случае если она будет увеличиваться/уменьшаться.
long bigNumber = 2_500_000_000L;
Особо внимательные заметили, что вконце стоит латинская буква L . JVM по умолчанию все целые числа интерпретирует как int . Чтобы явно указать принадлежность к типу long можно добавить в конце числа l или L .
Практический совет: используйте всегда только L. Дело в том, что строчная буква ‘l’ во многих шрифтах очень похожа на единицу, что приводит к механическим ошибкам.
А что будет, если попытаться присвоить переменной значение другого типа?
//ошибка компиляции: мы пытаемся в переменную типа `byte` поместить число типа `int`, которое больше диапазона переменной `byte` byte b = 129; //ошибка компиляции: мы пытаемся в переменную типа `short` поместить число типа `int`, которое больше диапазона переменной `short` short s = 3276700; //ошибка компиляции: мы пытаемся в переменную типа `int` поместить число типа `long` (несмотря на то, что 1 явно входит в диапазон `integer` - мы явно указали, что значение имеет тип `long` (`L` вконце числа) поэтому хранить его должна переменная типа `long`) int l = 1L; //ошибка компиляции: по умолчанию компилятор читает все целые числа как `integer`, а текущее число не входит в диапазон `int` long l = 2_500_000_000;
При этом, следующий код компилируется нормально:
//Java считывает 127 как `int` и присваивает его в переменную типа `byte`, т.к. он входит в диапазон указанного типа byte b = 127; //Java считывает 2_500 как `int` и присваивает его в переменную типа `long`, т.к. он входит в диапазон указанного типа long l = 2_500; //Java считывает 2_500_000_000L как `long` - мы указали специально такую рекомендацию, т.к. знали. что текущее число выходит за рамки диапазона `integer`. Далее Java присваивает его в переменную типа `long` - здесь вообще не может быть каких-либо конфликтов long l1 = 2_500_000_000L;
- Компилятор по умолчанию читает целое число как integer , если мы не указали обратного.
- Для того, чтобы переменной присвоить значение, необходимо чтобы оно (значение) входило в диапазон допустимых значений типа переменной.
Может сложиться ложное впечатление, что повсеместное использование более узких типов, например byte , будет снижать потребление памяти (т.к. на byte выделяется 8 бит, а на integer — 32). В основном, byte используется для уменьшения расхода памяти при хранении данных в массивах. Если вычисления над переменной проходят в основном коде, то более узкие типы будут приведены к 32-битному int . это связано с тем, что большинство систем 32 или 64 разрядные — и реализацией JVM под эти системы, соответственно, тоже. Поэтому нет смысла экономить на каждой переменной и можно смело объявлять их как int (если значение помещается в этот тип данных).
Символьный тип данных char
Для хранения символов Java использует специальный тип char , который сохраняет данные согласно стандарту Unicode (за каждым символом закреплен код). Диапазон допустимых значений — от 0 до 65536 (отрицательных значений не существует). Простыми словами: символ можно задать напрямую, указав значение в одинарных кавычках ( ‘A’ ), либо с помощь его кода в кодировке Юникод.
char ch1 = 'a'; // присваиваем сам символ char ch2 = 97; // присваиваем код симвала 'a' System.out.println("Что мы здесь получим: " + ch1 + ch2);
Некоторые символы, отсутствующие на клавиатуре, можно задавать с помощью escape-последовательностей, содержащих символ «\», за которым следует буквенный символ, идентифицирующий escape-последовательность. Например:
\t — табуляция
\n — перевод строки
\r — возврат каретки
\» — двойные кавычки
\\ — backslash
2. Числа с плавающей точкой (вещественные) — float, double
Числа с плавающей точкой применяются при вычислении выражений, в которых требуется точность с учетом десятичных знаков. Существует два типа с плавающей точкой: float и double, которые представляют числа одинарной и двойной точности.
Тип | Точность дробной части |
---|---|
float | 7-8 цифр после запятой |
double | 17 цифр после запятой |
Java по умолчанию все числа с плавающей точкой определяет как double , чтобы задать число как float — необходимо добавить в его конец «f» или «F». Если есть необходимость указать определенно, что это double — поставьте по аналогии в конце D или d .
double num = 4.2; float floatNum = 4.2f;
float — число одинарной точности. Его можно использовать, когда точность десятичных знаков не очень важна.
Скажем, 7-8 цифр после запятой, скорее всего, будет достаточной при калькуляции денежных вычислений, но недостаточной в научных исследованиях. В то же время, современные процессоры оптимизированы под вычисления значений двойной точности ( double ), поэтому можете смело использовать этот тип данных.
3. Логический тип
Этот тип данных может хранить одно из 2х значений: true или false . Заметьте, в JS (как и в некоторых других языках) можно было привести 1 к true , в Java такое невозможно. Используется в логических операциях, таких как циклы while , и в ветвлении при помощи if , switch .
Размер выделяемой памяти зависит от реализации JVM. Обычно: 8 бит (в массивах) и 32 бита (не в массивах используется int).
boolean switch = true; boolean isALie = 7 > 10; // false
В логических переменных может использоваться укороченная запись:
if (isTrue) // равноценно `if (isTrue == true)` while (!isTrue) // равноценно `while (isTrue == false)`
Несколько слов про дефолтные значения примитивов: если вы объявили переменную примитивного типа на уровне класса, но не инициализировали ее, то JVM присвоит ей значение 0 по умолчанию ( false если переменная типа boolean ); если объявите примитивную переменную в теле метода, и попытаетесь ею воспользоваться, не инициализировав, то получите ошибку компиляции, т.к. компилятор в процессе выполнения работы (в runtime) создаст переменную, но не присвоит ей никакое значение.
Кстати, подсказка для решения задач: если в процессе вычислений мы пытаемся записать вещественное число в целочисленную переменную, то дробная часть отбрасывается, игнорируя всякие правила математики.
int a = 5/2; // ‘a’ будет равно 2, т.к. нецелая часть отбросится.
Задача 1
Посчитайте площадь прямоугольника, у которого стороны равняются 20.1см и 33.6см.
Нажмите, чтобы подсмотреть решение
Задача 2
Общее число людей, живущих на Земле, приблизительно равняется 7,6 млрд человек. Сумма наличных денег в мире — 4,5 трлн долларов. Сколько полных (без учета центов) долларов припадает на одного Землянина?
Нажмите, чтобы подсмотреть решение