Внутри высокоуровневых языков программирования данные разделяются по типам. Например, строки относятся к типу String, а числа — к типу int.
Зачем нужны типы? Для защиты программы от трудноотловимых ошибок. Типы определяют две вещи:
Язык программирования распознает типы. Поэтому Java не позволит нам умножать строку на строку. Но позволит умножать целое число на другое целое число. Наличие типов и таких ограничений в языке защищает программы от случайных ошибок:
"one" * "two"
Error:
bad operand types for binary operator '*'
first type: java.lang.String
second type: java.lang.String
Каким образом Java понимает, что за тип данных перед ним? Любое значение где-то инициализируется. В зависимости от способа инициализации, становится понятно, что именно находится перед нами.
Например, число — это просто число, не обернутое в кавычки или другие парные символы. А вот строки всегда ограничены двойными кавычками. Например, значение "234"
считается строкой, хотя внутри нее записаны цифры:
// Компилятор понимает что тут число
var age = 33;
По-английски строки в программировании называются strings, а строчки текстовых файлов называются lines. Например, в коде выше одна строчка (lines) и ноль строк (strings). В русском языке иногда может быть путаница, поэтому во всех уроках мы будем говорить использовать такие термины:
Типов данных в Java много, плюс можно создавать свои. Постепенно мы познакомимся со всеми необходимыми и научимся их правильно использовать.
До сих пор при определении переменных мы использовали ключевое слово var
, что может удивить тех, кто имеет какой-то опыт на Java. Обычно определение переменных показывают так:
int x = 3;
String greeting = "Hello Hexlet!";
// Error: incompatible types: java.lang.String cannot be converted to int
int ops = "test";
Пришло время раскрыть карты! Java — это статически типизированный язык. В таких языках тип переменной фиксируется при ее объявлении. В большинстве языков для этого перед именем переменной указывается ее тип — в примере выше это число (int) и строка (String).
Раньше на Java создавали переменные только так, до тех пор пока не появился var
. var
– специальное ключевое слово, которое включает механизм вывода типов. Вывод типов автоматически определяет тип присваиваемого значения и связывает его с переменной. В примерах выше очевидно, где какой тип, тогда зачем его явно прописывать?
Вывод типов в Java появился в 2018 году, но в некоторых других язык он существует не один десяток лет. Первый язык с выводом типов называется ML и появился он аж в 1973 году. С тех пор вывод типов был добавлен в Ocaml, Haskell, C#, F#, Kotlin, Scala и множество других языков.
Вывод типов и предпочтителен в большинстве ситуаций, однако бывает такое, что выводимый тип нас не устраивает. Тогда мы можем указать тип явно.
В этом уроке мы рассмотрим систему типов в Java с высоты птичьего полета, не погружаясь в детали. Но сначала ответим на вопрос, зачем вообще про них знать?
В коде мы все время оперируем данными. Эти данные имеют разную природу, могут быть по-разному организованы, что влияет на удобство работы с ними. Типы преследуют нас на каждом шагу, поэтому без них программирование на Java возможно только на очень базовом уровне.
С другой стороны, не пытайтесь запомнить всю эту информацию про типы наизусть — она дается лишь для общего представления. Все важное о типах вы и так выучите в процессе программирования. Глобально, типы данных в Java делятся на две большие группы:
У этих групп есть различия, которые мы разберем позже, когда познакомимся с null
и объектно-ориентированным программированием. Пока достаточно знать, что имена примитивных типов начинаются с нижнего регистра (int
), а ссылочных с верхнего (String
).
Всего в Java восемь примитивных типов данных:
Рассмотрим первые четыре типа. Это целые числа разного размера:
Посмотрим на примере такого кода:
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 — строка? Дело в том, что она не является примитивным типом. Внутри она представляет собой массив символов. Несмотря на это техническое различие, строки используются наравне с примитивными типами без особых отличий.
Примитивные данные всегда имеют значение, даже если они определяются без инициализации:
int a;
System.out.println(a); // => 0
У каждого примитивного типа есть свое значение по умолчанию:
byte 0
short 0
int 0
long 0
float 0.0
double 0.0
char ''
boolean false
Особняком в Java стоит значение null
. В Java оно не является типом. Это просто конкретное значение со специальным смыслом и логикой работы. Начнем с примера:
// Определение переменной без инициализации значением
// С var такое не сработает, так как невозможно вывести тип
String a;
Что находится внутри переменной a
? Если мы ее распечатаем, то увидим null
. Значение null
используется для ссылочных типов, когда значение не определено.
Как такое возможно? Представьте, что мы хотим извлечь из базы данных пользователя, а его там нет. Что вернет нам запрос в базу? Вот именно для таких ситуаций и нужен null
.
Их гораздо больше, чем может показаться на первый взгляд. Чем дальше мы будем двигаться, тем чаще он начнет встречаться:
var user = // тут делаем запрос в базу
// Если данных нет, то user станет null
// Запись выше равносильна
var user = null;
Из вышесказанного следует важный вывод. Любой ссылочный тип данных может принимать значение null
. То есть, null
является значением любого ссылочного типа. А вот примитивные типы и null
не совместимы. Примитивное значение всегда должно быть определено:
// Error: incompatible types: <nulltype> cannot be converted to int
int x = null;
В программировании регулярно встречаются задачи, когда один тип данных нужно преобразовать в другой. Простейший пример – работа с формами на сайтах.
Данные формы всегда приходят в текстовом виде, даже если значение число. Вот как его можно преобразовать:
// станет int
var number = Integer.parseInt("345");
System.out.println(number); // => 345
Если нужно конвертировать из примитивного типа в примитивный, то все проще. Достаточно перед значением в скобках указать желаемый тип. В результате значение справа преобразуется в значение другого типа, указанного слева:
var result = (int) 5.1;
System.out.println(result); // => 5
Преобразование типов можно использовать внутри составных выражений:
// Дополнительные скобки помогают визуально отделить части выражения друг от друга
var result = 10 + ((int) 5.1);
System.out.println(result); // => 15
https://replit.com/@hexlet/java-basics-data-types
Вам ответят команда поддержки Хекслета или другие студенты.
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
Наши выпускники работают в компаниях:
С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.
Зарегистрируйтесь или войдите в свой аккаунт