Понимание типов данных в Java

Автор выбрал Фонд свободного и открытого исходного кода для получения пожертвования в рамках программы Write for DOnations.

Введение

Java – это язык программирования со статической типизацией. Это означает, что при создании переменной вы также должны указать ее тип данных, который определяет тип информации, которую она хранит. В отличие от языков с динамической типизацией, таких как PHP, в статически типизированных языках необходимо указывать тип данных переменной, что может показаться неудобным.

Тем не менее, знание типов данных и их правильное использование позволяют разработчикам оптимизировать свой код, поскольку каждый тип данных имеет конкретные требования к ресурсам. Кроме того, если вы указали один тип данных и попытались сохранить другой тип, например, по ошибке, вы не сможете скомпилировать код. Таким образом, со статически типизированными языками можно выявлять ошибки еще до начала любых тестирований.

Java имеет два типа данных: примитивный и ссылочный (также известный как непримитивный). В этом учебнике вы будете использовать переменные, чтобы хранить и использовать информацию в программе на Java и узнать о некоторых из часто используемых типов данных в Java. Это не исчерпывающий обзор всех типов данных, но этот руководство поможет вам ознакомиться с тем, какие варианты доступны вам в Java.

Предварительные требования

Чтобы следовать этому учебнику, вам понадобится:

Примитивные Типы

Примитивные типы Java представляют собой простейшие и базовые типы данных в Java. Они представляют сырые значения, такие как числа и символы. Самые часто используемые примитивные типы данных – int (целые числа), boolean (логические значения) и char (символы). Остальные можно найти в официальной документации по типам данных Java.

Целые Числа

Целые числа могут быть как положительными, так и отрицательными. В Java для их хранения используется int. int может вместить достаточно большие числа для большинства целей: от -2,147,483,648 до 2,147,483,647.

Давайте посмотрим, как используется int в примере:

int theAnswer = 42;

Примитивные типы всегда начинаются с прописной буквы (int). Правила синтаксиса Java требуют, чтобы вы сначала указали тип данных (int), а затем его имя (theAnswer). После этого вы присваиваете значение 42 переменной с помощью знака равенства (=).

Вне зависимости от типа данных вы используете переменную, указывая ее имя без добавления каких-либо специальных символов. Это потому, что Java может распознать ее как переменную.

Примечание: Имя переменной theAnswer и всех остальных переменных в этом руководстве написаны в стиле Camel case. Несмотря на то, что нет строгих требований к его использованию, это принятая конвенция именования в Java.

После объявления переменной вы можете использовать ее, обращаясь к ней в методе так:

int theAnswer = 42;
System.out.println("The answer to all questions is " + theAnswer);

На второй строке вы выводите theAnswer в консоль, используя встроенный метод println из пакета System.out. Это самый простой способ проверить переменную, чтобы убедиться, что она объявлена, как ожидается.

Чтобы увидеть этот код в действии, используйте инструмент Java Shell. После установки Java откройте терминал или командную строку на своем локальном компьютере и введите jshell:

  1. jshell

Ваш вывод будет похож на следующий:

Output
| Welcome to JShell -- Version 11.0.16 | For an introduction type: /help intro jshell>

Вы можете вставить примеры кода из этого руководства в консоль. По завершении работы вы можете выйти из jshell, введя /exit.

Чтобы объявить и использовать int, вставьте следующие строки в консоль jshell:

  1. int theAnswer = 42;
  2. System.out.println("The answer to all questions is " + theAnswer);

Вы увидите следующий вывод:

Output
theAnswer ==> 42 The answer to all questions is 42

Этот вывод подтверждает, что вы правильно установили переменную int theAnswer в 42 (theAnswer ==> 42). Вы также успешно использовали theAnswer, передав его в метод, и метод выдал ожидаемое значение переменной.

Значения Логические

Логические могут быть true или false. В Java для их хранения используется boolean. Например, давайте создадим переменную boolean, определяющую, весело ли программировать на Java:

boolean isJavaFun = true;

Вы определяете переменную isJavaFun как true. Альтернативным значением boolean является false.

С использованием указанной переменной можно вывести фразу Java is fun: true следующим образом:

  1. boolean isJavaFun = true;
  2. System.out.println("Java is fun: " + isJavaFun);

Выполнение этих строк в jshell приведет к следующему выводу:

Output
isJavaFun ==> true Java is fun: true

Аналогично примеру с int, метод println выведет аргумент, указанный в скобках. Знак плюс (+) конкатенирует строку “Java is fun: ” с переменной isJavaFun, поэтому на самом деле это всего лишь один аргумент — строка Java is fun: true.

Символы

Для хранения одного буквенно-цифрового символа используется char. Например:

char firstLetter = 'a';

Обратите внимание, что буква a заключена в одинарные кавычки. Одинарные кавычки могут использоваться только для значений char. Двойные кавычки используются для строк, как вы узнаете позже.

char не кажется особенно полезным типом, потому что вероятно, вам не понадобится переменная, назначенная одному символу. Однако char используется как строительный блок для классов символьных строк, таких как String, которые в основном являются коллекциями значений char. Как вы видели в этом разделе, объявление и использование переменных примитивных типов просты, потому что они представляют собой простые значения, такие как целые числа. Эти значения готовы к использованию и не требуют дополнительных операций, таких как создание объектов, вызов методов и так далее.

Типы ссылок

В первом руководстве этой серии, Как написать свою первую программу на Java, вы узнали, что код Java организован в классах и что эти классы используются в качестве шаблонов для создания объектов. Когда таким объектам присваиваются переменные, вы указываете или ссылаетесь на эти объекты. В таких случаях переменные классифицируются как типы ссылок. Эти переменные также известны как непримитивные, потому что переменные примитивных типов не могут указывать на объекты.

Объекты могут быть мощными благодаря своим расширенным свойствам и способности выполнять действия при вызове их методов. Однако без переменных, указывающих на них, эти объекты недоступны и практически бесполезны. Вот почему переменные ссылочного типа являются важными для языка Java и объектно-ориентированного программирования в целом.

Примечание: Переменные ссылочного типа указывают на объекты, созданные из классов. Чтобы избежать путаницы, тип ссылки и созданный объект будут одного и того же класса в следующих примерах.

Однако в сложных программах это редко бывает так. В Java интерфейс представляет собой группу требований к определенному поведению, и эти требования могут быть удовлетворены одним или несколькими классами. Класс, который удовлетворяет требованиям интерфейса, считается реализующим этот интерфейс. Таким образом, в сложных программах обычно объявляется переменная с типом ссылки на интерфейс. Таким образом, вы указываете поведение, которое должно проявлять ваша переменная, не привязывая ее к конкретной реализации этого поведения. Это позволяет легко изменять, на какую реализацию указывает ваша переменная, не изменяя способ ее использования. Этот сложный концепт является частью более продвинутой темы об наследовании и полиморфизме, которая будет предметом отдельного учебника в нашей серии по Java.

В то время как существует всего несколько примитивных типов, ссылочные типы практически неограничены, потому что нет ограничения на количество классов (и интерфейсов), и каждый класс представляет собой ссылочный тип. В языке Java существует много встроенных классов, предоставляющих основные функциональные возможности. Самые часто используемые из них находятся в ядре пакета java.lang. Вы рассмотрите некоторые из них в этом разделе.

Класс String

Класс String представляет собой комбинацию символов, составляющих строку. Чтобы объявить переменную с типом String или любой другой ссылочный тип, вы сначала указываете его тип, за которым следует имя. После этого присваиваете значение с помощью знака равенства. До сих пор это похоже на работу с примитивными типами. Однако ссылочные типы указывают на объекты, поэтому вы должны создать объект, если еще не был создан. Вот пример:

String hello = new String("Hello");

hello – это имя переменной с типом String. Вы присваиваете ей новый объект String. Новый объект String создается с использованием ключевого слова new вместе с именем класса – String в данном случае. Класс String начинается с заглавной буквы. По соглашению все классы и, следовательно, ссылочные типы начинаются с заглавной буквы.

Каждый класс имеет специальный метод, называемый конструктором, который используется для создания новых объектов. Вы можете вызвать этот конструктор, добавив к имени класса скобки (()) в конце. Конструктор может принимать параметры, как в приведенном выше примере, где параметр "Привет" применяется к конструктору для String.

Чтобы подтвердить, что переменная hello ведет себя ожидаемым образом, снова передайте ее методу println следующим образом:

  1. String hello = new String("Hello");
  2. System.out.println(hello);

Запуск этих строк в jshell приведет к следующему выводу:

Output
hello ==> "Hello" Hello

На этот раз вывод подтверждает, что переменная hello установлена в Привет. После этого тот же Привет выводится на новой строке, подтверждая, что метод println() его обработал.

Оболочечные классы

В предыдущем разделе вы работали с ссылочным типом String, который часто используется. Другие популярные ссылочные типы – это так называемые оболочки для примитивных типов. Класс-оболочка оборачивает или содержит примитивные данные, отсюда и его название. У всех примитивных типов есть оболочечные аналоги, и вот несколько примеров:

  • Integer: Для обертывания значений типа int.
  • Character: Для обертывания значений типа char.
  • Boolean: Для обертывания значений типа boolean.

Эти обертки существуют для того, чтобы вы могли превратить простое примитивное значение в мощный объект. У каждой обертки есть готовые методы, связанные с значениями, которые они предназначены хранить.

В качестве примера рассмотрим Integer. В предыдущем разделе вы создали объект String с помощью ключевого слова new. Однако некоторые классы предоставляют, и даже поощряют, использование специальных методов для получения объектов из них, и Integer является одним из таких классов. В случае с Integer использование специального метода в основном связано с оптимизацией ресурсов, но в других случаях это может быть связано с упрощением создания сложных объектов.

В следующем примере вы создаете переменную theAnswer типа Integer со значением 42 с использованием метода valueOf:

  1. Integer theAnswer = Integer.valueOf(42);
  2. System.out.println(theAnswer);

В jshell вы получите следующий вывод:

Output
theAnswer ==> 42 42

Вызывая метод valueOf(42) класса Integer, вы указываете Java создать объект с этим значением. Внутри Java будет проверять, существует ли уже объект с таким значением в кэше. Если существует, объект будет связан с переменной theAnswer. Если нет, будет создан новый объект для переменной theAnswer.

Многие встроенные классы предоставляют такие методы из соображений производительности, и их использование рекомендуется, если не обязательно. В случае с Integer вы все равно можете создать объект с помощью ключевого слова new, но вы получите предупреждение о снятии с обслуживания.

Помимо String и обёрток, существуют и другие полезные встроенные ссылочные типы, которые вы можете найти в java.lang package summary. Чтобы полностью понять некоторые из этих более продвинутых ссылочных типов, требуется дополнительное объяснение или предварительные знания. Поэтому мы рассмотрим некоторые из них в наших следующих уроках из серии по Java.

Literals

Literals представляют собой фиксированные значения, которые можно использовать непосредственно в коде и поэтому могут быть присвоены как примитивным, так и ссылочным типам. Существует несколько типов литералов, которые можно классифицировать следующим образом.

Primitive Type Literals

Вы уже использовали несколько литералов в разделе о примитивных типах. Для каждого примитивного типа есть литерал, такой как те, что были приведены в наших примерах: 42, 'a' и true. Целые числа, такие как 42, являются целочисленными литералами. Аналогично, символы, такие как 'a', являются символьными литералами, а true и false – логическими литералами.

Литералы примитивных типов также могут использоваться для создания значений для ссылочных типов. Литерал int был использован для создания объекта Integer с помощью кода Integer.valueOf(42). Существует также упрощенный способ, и вы можете присвоить значение напрямую, вот так:

Integer theAnswer = 42;

42 – это литерал целого числа, такой же, как и любое целое число, и вы можете присвоить его напрямую переменной theAnswer без дополнительных операторов. Часто можно видеть объявление Integer таким образом, потому что это удобно.

Этот упрощенный подход также работает для литералов других примитивных типов и их соответствующих ссылочных типов, таких как Boolean, например:

Boolean isFun = true;

true – это литерал, который напрямую присваивается переменной isFun типа Boolean. Существует также литерал false, который можно присвоить таким же образом.

Литерал строки

Существует также специальный литерал для ссылочного типа String, который определяется двойными кавычками вокруг его значения. В этом примере это "Hello, World!":

String helloWorld = "Hello, World!";

Использование литералов проще и короче, и поэтому многие программисты предпочитают это. Тем не менее, вы все равно можете объявить переменную String с новым объектом String, как вы уже сделали в разделе для ссылочных типов.

Нулевой литерал

Существует еще один важный литерал: null, который представляет собой отсутствие значения или несуществование объекта. Null позволяет создать ссылочный тип и указать его на null, вместо того чтобы указать на объект. null можно использовать для всех ссылочных типов, но не для примитивных типов.

Существует один нюанс с литералом null: вы можете объявить переменные с его использованием, но не можете использовать эти переменные, пока не присвоите им подходящее, ненулевое значение. Если вы попытаетесь использовать переменную ссылочного типа со значением null, вы получите ошибку. Вот пример:

  1. String initiallyNullString = null;
  2. System.out.println("The class name is: " + initiallyNullString.getClass());

Когда вы попытаетесь выполнить этот код в jshell, вы увидите ошибку, подобную следующей:

Output
initiallyNullString ==> null | Exception java.lang.NullPointerException | at (#4:1)

В зависимости от вашей операционной системы и версии Java ваш вывод может отличаться.

Ошибка java.lang.NullPointerException возникает потому, что вы пытаетесь вызвать метод getClass() класса String (который возвращает имя класса) для переменной initiallyNullString (которая указывает на нулевой объект).

Примечание: Для простоты мы называем java.lang.NullPointerException ошибкой, хотя это технически исключение. Для получения дополнительной информации о исключениях и ошибках смотрите руководство Обработка исключений в Java.

Чтобы исправить ошибку, вам нужно переназначить значение initiallyNullString следующим образом:

  1. String initiallyNullString = null;
  2. initiallyNullString = "not null any longer";
  3. System.out.println("The class name is: " + initiallyNullString.getClass());

Новый, исправленный код выведет следующий результат:

Output
initiallyNullString ==> null initiallyNullString ==> "not null any longer" The class name is: class java.lang.String

Вывод выше показывает, как initiallyNullString сначала null, а затем становится новым объектом String, содержащим "not null any longer". Затем, когда вызывается метод getClass() для созданного объекта, вы получите java.lang.String, где String – это имя класса, а java.lang – его пакет. Наконец, будет выведено полное и содержательное сообщение: "The class name is: class java.lang.String".

Такие объявления значения null более распространены в старом коде. Их использовали для создания переменной сначала, а затем для присвоения ей реального значения, обычно после выполнения некоторой логики, определяющей это значение. Однако начиная с версии Java 8, существует новый тип ссылки под названием Optional, который более подходит для случаев, когда ранее использовался null.

Локальный вывод типа переменной

До сих пор вы использовали некоторые общие типы данных в Java для определения переменных. Однако в Java 10 была введена новая функция под названием локальный вывод типа переменной, которая позволяет использовать ключевое слово var перед новой переменной. С помощью этой функции Java будет выводить (то есть, автоматически угадывать) тип данных из локального контекста. Вывод типа вызывает споры, поскольку это противоречит ранее объясненной многословности определения переменных. Преимущества и недостатки такой функции обсуждаемы, но факт в том, что другие языки со статической типизацией, такие как C++, поддерживают вывод типа.

В любом случае вывод типа не может полностью заменить использование типов данных, потому что это работает только с локальными переменными, которые находятся внутри метода. Давайте рассмотрим пример с var:

  1. var hello = "Hello";
  2. System.out.println(hello);

Вы объявляете переменную hello с ключевым словом var, чтобы указать Java определить её тип данных. Затем вы выводите её на консоль обычным способом, чтобы подтвердить, что она работает как ожидается:

Ouput
hello ==> "Hello" Hello

Этот пример будет работать, если ваша установка Java (и, более конкретно, JDK) выше версии 10. Ключевое слово var не поддерживается в более старых версиях.

В процессе компиляции происходит вывод типов — то есть, когда вы компилируете код. Процесс компиляции преобразует исходный код обычного текста в машинный код и применяет различные оптимизации, включая вывод типов. Это гарантирует, что правильное количество системной памяти доступно для переменных с выводом типов. Таким образом, машинный код, который вы запускаете после компиляции, полностью оптимизирован, как если бы вы вручную указали все типы данных.

В этом примере ключевое слово var работает, потому что переменная является локальной, и тип данных var работает только с локальными переменными. Локальные переменные определяются внутри методов и доступны только внутри методов, поэтому их называют «локальными».

Чтобы показать, что var можно использовать только для локальных переменных, попробуйте разместить его за пределами основного метода, вот так:

  1. public class Hello {
  2. var hello = "Hello";
  3. public static void main(String[] args) {
  4. // example code
  5. }
  6. }

Когда вы вставите вышеприведенный код в jshell, вы получите следующую ошибку:

Output
| Error: | 'var' is not allowed here | var hello = "Hello"; | ^-^

var не разрешено здесь, потому что hello находится за пределами метода и больше не считается локальным. Таким образом, вывод типов не работает для не-локальных переменных, потому что контекст нельзя надежно использовать для определения типа данных.

Хотя использование var может быть вызовом и не требуется, вы, вероятно, столкнетесь с этим, поэтому полезно знать о нем.

Зарезервированные ключевые слова

Когда объявляются переменные в Java, есть еще одно важное правило, которое нужно знать. Существуют зарезервированные ключевые слова, которые нельзя использовать в именах переменных. Например, вы не можете объявить примитив типа int и назвать его new, как показано здесь:

  1. int new = 1;

Если вы попытаетесь выполнить этот пример, вы получите ошибки компиляции, потому что new – это зарезервированное ключевое слово.

Output
| Error: | '.class' expected | int new = 1; | ^ | Error: | <identifier> expected | int new = 1; | ^ | Error: | '(' or '[' expected | int new = 1; | ^ | Error: | unexpected type | required: value | found: class | int new = 1; | ^--^ | Error: | missing return statement | int new = 1; | ^----------^

Ключевое слово new используется для создания новых объектов, и в данном контексте Java не ожидает его. В списке ошибок в предыдущем выводе первая часть является наиболее важной:

Output
| Error: | '.class' expected | int new = 1; | ^

Ошибка '.class' expected означает, что при использовании ключевого слова new Java ожидает, что за ним последует класс. На этом этапе Java не может интерпретировать оператор, и следуют остальные ошибки.

Остальные зарезервированные ключевые слова, такие как abstract, continue, default, for и break, также имеют конкретные значения в Java и не могут использоваться в именах переменных. Полный список зарезервированных ключевых слов можно найти на странице Ключевые слова языка Java. Даже если вы не помните все зарезервированные ключевые слова, вы можете использовать ошибки компиляции для выявления проблемы.

Вывод

В этом учебнике вы узнали о примитивных и ссылочных типах данных в Java, что является сложной, но необходимой темой. Уделите время практике и пройдите примеры несколько раз. Попробуйте изменить некоторые типы данных и значения. Обратите внимание, когда возникают ошибки, а когда нет, чтобы развить чувство успешного выполнения кода.

Для более подробной информации по Java, ознакомьтесь с нашей серией Как писать на Java.

Source:
https://www.digitalocean.com/community/tutorials/understanding-data-types-in-java