img

Типы дaнных в Java

21 ноября
20:00
Бесплатный вебинар
Введение в Docker
Ведущий — Филипп Игнатенко.
Руководитель центра разработки
Записаться
img
img

Введение

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

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

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

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

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

  • Среда, в которой можно выполнять программы Java по примерам. Чтобы настроить ее на локальном компьютере, вам понадобиться следующее:
  • Java (версии 11 или выше), установленный на вашем компьютере, вместе с компилятором, который входит в состав JDK (Java Development Kit – набор для разработки Java-приложений)
  • Для компиляции и запуска примеров кода в этом руководстве используется Java Shell – утилита типа REPL (Read-Evaluate-Print Loop – цикл чтение-вычисление-печать), которую можно запустить из командной строки

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

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

Целые числа

К понятию «целые числа» относятся как отрицательные, так и положительные целые числа. В Java для хранения таких чисел используется тип int. Этот тип может хранить достаточно большие числа – от -2,147,483,648  до 2,147,483,647.

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

int theAnswer = 42;

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

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

Примечание: имя переменной theAnswer и всех других переменных в этом руководстве записано с использованием верблюжьего регистра. Несмотря на то, что не существует каких-либо строгих требований в отношении его использования, это все же является общепринятым соглашением о присвоении имен в 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;
    
    System.out.println("The answer to all questions is " + theAnswer);
    

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

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

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

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

Логические значения – это истина (true) и ложь (false). Для хранения этих значений в Java используется тип данных boolean. Давайте, например, создадим переменную типа boolean, которая бы определяла, интересен ли Java:

boolean isJavaFun = true;

Мы определим переменную isJavaFun как true. Обратный вариант - false.

Используя эту переменную, мы можем напечатать предложение Java is fun: true:

  1. boolean isJavaFun = true;
    
    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 используется в качестве строительных блоков для классов символьных строк, таких как 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".

Для того, чтобы убедиться, что переменная hello ведет себя так, как нужно, передайте ее снова в метод println:

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

Запустите эти строки в jshell, после чего вы получите следующий результат:

Output
hello ==> "Hello"
Hello

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

Классы-обертки

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

  • Integer: содержит значения типа int.
  • Character: содержит значения типа char.
  • Boolean: содержит значения типа boolean.

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

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

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

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

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

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

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

Кроме классов String и классов-оберток существуют и другие не менее полезные встроенные ссылочные типа, которые вы можете найти в пакете java.lang. Для того, чтобы хорошо понять, что из себя представляют некоторые из более сложных ссылочных типов, потребуются дополнительные пояснения или некоторые первоначальные знания. Именно поэтому мы рассмотрим некоторые из них в наших следующих уроках серии, посвященной Java.

Литералы

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

Литералы примитивного типа

Мы уже успели воспользоваться несколькими литералами в разделе «Примитивные типы данных». Литерал существует для каждого примитивного типа, например, как в наших случаях, 42'a' и true. Целые числа, такие как 42, - это целочисленные литералы. По аналогии такие символы, как 'a', являются символьными литералами, а true и false - логическими.

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

Integer theAnswer = 42;

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

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

Boolean isFun = true;

true - это литерал, которые присваивается непосредственно переменной isFun, которая имеет тип Boolean. Кроме того, существует литерал false, который вы можете присвоить переменной аналогичным образом.

Строковые литералы

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

String helloWorld = "Hello, World!";

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

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

Есть еще один довольно важный тип литерала – нулевой литерал (null). Он обозначает отсутствие значения или факт того, что объект не существует. Нулевой литерал позволяет нам создать переменную ссылочного типа и указать для нее значение null, вместо того, чтобы передавать ей ссылку на объект. null можно использовать для всех ссылочных типов, но при этом нельзя использовать для примитивных. 

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

  1. String initiallyNullString = null;
    
    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() (который возвращает имя класса) для переменной initiallyNullString (которая указывает на нулевой объект).

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

Для того, чтобы устранить эту ошибку, нам нужно переназначить значение initiallyNullString:

  1. String initiallyNullString = null;
    
    initiallyNullString = "not null any longer";
    
    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".

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

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

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

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

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

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

Ouput
hello ==> "Hello"
Hello

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

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

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

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

  1. public class Hello {
    
    var hello = "Hello";
    
    public static void main(String[] args) {
    
    // example code { // код из примера }
    
    }
    
    }
    

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

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

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

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

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

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

  1. int new = 1;
    

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

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

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

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

Есть и другие ключевые слова, которые также имеют особое значение в Java и которые нельзя использовать в качестве имен переменных, например, abstractcontinuedefaultfor и break. Полный список зарезервированных ключевых слов можно найти на странице «Ключевые слова языка Java». Даже если вы не помните все зарезервированные ключевые слова, вы можете воспользоваться ошибками при компиляции, чтобы выявить проблему. 

Заключение

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

Ссылка
скопирована
Получите бесплатные уроки на наших курсах
Все курсы
Программирование
Скидка 25%
Java-разработчик с нуля
Освойте backend-разработку и программирование на Java, фреймворки Spring и Maven, работу с базами данных и API. Создайте свой собственный проект, собрав портфолио и став востребованным специалистом для любой IT компании
Получи бесплатный
вводный урок!
Пожалуйста, укажите корректный e-mail
отправили вводный урок на твой e-mail!
Получи все материалы в telegram и ускорь обучение!
img
Еще по теме:
img
Гипервизор - это программное обеспечение для виртуализации, используемое для создания и запуска виртуальных машин (ВМ). Гипервиз
img
Виртуализация серверов позволяет запускать несколько виртуальных машин на одном физическом сервере. Запуск виртуальных машин (ВМ
img
Сегодня мы рассмотрим, как настроить и использовать PHP в проекте. Но прежде чем начать, нужно понять, что такое PHP. Что такое
img
Как разработчик, вы знаете, что HTML расшифровывается как HyperText Markup Language (язык разметки гипертекста). HTML — это язык
img
Бесконечные споры вокруг искусственного интеллекта приводят к путанице. Существует много терминов, которые кажутся похожими, но
img
SVG расшифровывается как масштабируемая векторная графика. Это веб-дружелюбный векторный формат файлов, используемый для отображ
21 ноября
20:00
Бесплатный вебинар
Введение в Docker