Глава 5.3. Типы, значения и переменные

5.3.1. Основные понятия

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

Все типы в Java подразделяются на примитивные и ссылочные. Примитивными являются логический тип boolean, целые числовые типы byte, short, int, long, char и плавающие числовые типы float и double. К ссылочным типам относятся классы, интерфейсы и массивы. Существует также специальный нулевой тип.

Объект в Java — это или динамически созданный экземпляр класса, или динамический созданный массив. Значениями ссылочного типа являются ссылки на объекты. Все объекты, включая массивы, наследуют методы класса Object. Строки представляются объектами класса String.

Переменная — это именованное хранилище данных определенного типа. Точнее говоря:

  • Переменная примитивного типа всегда содержит значение именно этого типа.
  • Переменная типа класс может содержать либо значение null (нулевую ссылку), либо ссылку на объект данного класса или его наследника.
  • Переменная типа интерфейс может содержать либо нулевую ссылку, либо ссылку на объект любого класса, реализующий данный интерфейс.
  • Переменная типа "array of T", где T — примитивный тип, может содержать либо нулевую ссылку, либо ссылку на объект типа "array of T".
  • Переменная типа "array of T", где T — ссылочный тип, может содержать либо нулевую ссылку, либо ссылку на массив, состоящий из данных, совместимых с T по присваиванию.
  • Переменная типа Object может содержать либо нулевую ссылку, либо ссылку на любой объект.

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

5.3.2.1. Тип boolean

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

Пример декларации логической переменной:

boolean b;

5.3.2.2. Целые числовые типы

Значения целых числовых типов лежат в следующих диапазонах.

  • byte: от -128 до 127 включительно (8 бит);
  • short: от -32768 до 32767 включительно (16 бит);
  • int: от -2147483648 до 2147483647 включительно (32 бита);
  • long: от -9223372036854775808 до 9223372036854775807 включительно (64 бита)
  • char: от '\u0000' до'\uffff' включительно, т. е., от 0 to 65535 (16 бит без знака).

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

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

Примеры деклараций целых переменных:

byte b;
short s;
int i;
long l;
char c;

5.3.2.3. Плавающие числовые типы

Java содержит два плавающих типа float и double, которые соответствуют 32-битовой и 64-битовой реализациям формата IEEE 754.

Эти типы содержат следующие специальные значения: "Не число" Float.NaN и Double.NaN, положительная бесконечность +Infinity (или просто Inifinity) и отрицательная бесконечность -Infinity. Отметим также, что в данном формате различаются положительный нуль (+0 или 0) и отрицательный нуль (-0). Значение NaN имеет ту особенность, что оно не равно никакому числовому значению, включая самого себя.

Если хотя бы один из операндов имеет тип double, то второй операнд также приводится к этому типу, и результат операции будет иметь тип double. В остальных случаях все операнды приводятся к типу float, и результат операции также имеет тип float.

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

Примеры деклараций плавающих переменных:

float f;
double d;

5.3.3. Ссылочные типы

Как было указано выше, к ссылочным типам относятся классы, интерфейсы и массивы. В следующем примере

class Point { int[] metrics; }
interface Move { void move(int deltax, int deltay); }

объявлены класс Point, содержащий массив metrics, и интерфейс Move, содержащий метод move.

Два ссылочных типа считаются одинаковыми, если:

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

5.3.3.1. Объекты

Объект  — это либо экземпляр класса, либо массив. Значениями ссылочного типа служат ссылки на объекты, которые часто называются также указателями. Специальным ссылочным значением является нулевая ссылка, которая не указывает ни на какой объект. Способы создания экземпляров классов описаны в гл. 5.7, а массивов — в гл. 5.9.

Помните, что на один объект может указывать любое количество ссылок. Если две переменные указывают на один объект, то состояние объекта может быть изменено через первую ссылку, а затем прочитано через вторую.

5.3.3.2. Класс Object

Стандартный класс Object является предком всех остальных классов Java, которые наследуют его методы. Подробно эти методы описаны в гл. 5.14.

5.3.3.3. Класс String

Экземплярами класса String являются текстовые строки, т. е. цепочки символов Unicode. Объект класса String имеет постоянное (неизменяемое) значение. Строковые константы являются ссылками на экземпляры класса String.

Оператор конкатенации строк неявно создает новый объект класса String.

5.3.4. Переменные

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

5.3.4.1. Виды переменных

В Java существуют семь разновидностей переменных:

  1. Переменная класса — это статическое поле данных в декларации класса или любое поле данных в декларации интерфейса. Переменная класса создается при загрузке ее класса или интерфейса; сразу после создания ей присваивается значение по умолчанию. Переменная класса уничтожается, когда ее класс или интерфейс выгружается (после завершения его финализации).
  2. Переменная реализации — это поле данных в декларации класса, которое не является статическим. Такая переменная создается и получает значение по умолчанию при создании экземпляра данного класса или класса, который является его потомком. Переменная реализации уничтожается, когда соответствующий экземпляр класса удаляется процедурой сборки мусора (после завершения его финализации).
  3. Элементы массива — это неименованные переменные, которые создаются и получают значение по умолчанию при создании массива. Эти переменные уничтожаются, когда массив удаляется процедурой сборки мусора.
  4. Параметры конструктора — это имена значений аргументов, передаваемых конструктору. Для каждого параметра в декларации конструктора создается новая переменная каждый раз, когда вызывается данный конструктор (явно или неявно). Этой переменной присваивается фактическое значение аргумента, переданное конструктору при вызове. Переменная уничтожается после завершения выполнения тела конструктора.
  5. Параметры метода — это имена значений аргументов, передаваемых методу. Для каждого параметра в декларации метода создается новая переменная каждый раз, когда вызывается данный метод. Этой переменной присваивается фактическое значение аргумента, переданное методу при вызове. Переменная уничтожается после завершения выполнения тела метода.
  6. Параметр обработчика исключений создается всякий раз, когда выполняется часть catch оператора try. Его значением становится объект, ассоциированный с возникшим исключением. Переменная уничтожается после завершения выполнения блока catch.
  7. Локальные переменные. Каждый раз, когда в ходе выполнения программы управление передается новому блоку или оператору for, для каждой декларации переменной внутри этого блока или оператора создается соответствующая локальная переменная. Если декларация содержит начальное значение переменной, то ей присваивается это значение. Локальная переменная уничтожается после завершения выполнения блока или оператора, в котором она объявлена.

Следующий пример содержит декларации нескольких переменных перечисленных разновидностей:

class Point {
  static int numPoints;		// numPoints - переменная класса
  int x, y;			// x и y - переменные реализации
  int[] w = new int[10];	// w - массив целых чисел, w[0] - элемент массива
  int setX(int x) {		// x - параметр метода
    int oldx = this.x;		// oldx - локальная переменная
    this.x = x;
    return oldx;
  }
}

5.3.4.2. Начальные значения переменных

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

  • Каждой переменной класса, переменной реализации и каждому элементу массива при создании присваивается значение по умолчанию следующим образом:
    • переменные типов byte, short, int, long получают значение 0 соответствующего типа;
    • переменные типов float и double получают значение +0 соответствующего типа;
    • переменные типа char получают значение '\u0000';
    • переменные типа boolean получают значение false;
    • переменные ссылочных типов получают значение null;
  • Каждому параметру метода и конструктора присваивается фактическое значение соответствующего аргумента, определенное в момент вызова данного метода или конструктора.
  • Параметр обработчика исключений инициализируется соответствующим объектом.
  • Локальной переменной перед ее использованием должно быть присвоено значение либо инициализацией в декларации, либо оператором присваивания.