Какие типы данных существуют в Java и чем они отличаются?

«Какие типы данных существуют в Java и чем они отличаются?» — вопрос из категории Основы программирования, который задают на 10% собеседований QA Тестировщик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Java типы данных строго разделены на примитивные (primitive) и ссылочные (reference). Это ключевое отличие языка.

Примитивные типы (8 штук)

Хранят значения непосредственно в стеке (stack memory). Имеют фиксированный размер.

Тип Размер Диапазон / Описание Пример
byte 8 бит -128 .. 127 byte b = 100;
short 16 бит -32,768 .. 32,767 short s = 1000;
int 32 бит -2³¹ .. 2³¹-1 (≈ ±2.1 млрд) int i = 100000;
long 64 бит -2⁶³ .. 2⁶³-1 long l = 100000L; (требуется суффикс L)
float 32 бит Числа с плавающей точкой float f = 3.14f; (требуется суффикс f)
double 64 бит Числа с плавающей точкой (двойная точность) double d = 3.14;
char 16 бит Символ Unicode (0 .. 65,535) char c = 'A';
boolean не строго true или false boolean flag = true;

Особенности: Для примитивов используются операторы сравнения (==, >), а арифметические операции (+, -) работают непосредственно со значениями.

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

Хранят в переменной ссылку (адрес) на объект в куче (heap memory). Все остальные типы — ссылочные.

  • Классы (Classes): String, Object, ArrayList, пользовательские классы (Person).
  • Интерфейсы (Interfaces): List, Comparable.
  • Массивы (Arrays): int[], String[][].

Ключевые отличия от примитивов:

  1. Сравнение: Оператор == для ссылочных типов сравнивает адреса в памяти, а не содержимое объектов. Для сравнения содержимого используется метод .equals().
    String s1 = new String("text");
    String s2 = new String("text");
    System.out.println(s1 == s2);      // false (разные объекты в памяти)
    System.out.println(s1.equals(s2)); // true  (одинаковое содержимое)
  2. Значение по умолчанию: Для полей класса примитивы имеют значения по умолчанию (например, 0, false), а ссылочные типы — null.
  3. Упаковка/Распаковка (Boxing/Unboxing): Автоматическое преобразование между примитивным типом и его классом-оберткой (например, intInteger).
    Integer boxedInt = 42; // Автоупаковка (int -> Integer)
    int primitiveInt = boxedInt; // Автораспаковка (Integer -> int)

Правильный выбор типа: Используйте int/double для чисел по умолчанию, String для текста, boolean для флагов. Классы-обертки (Integer, Double) нужны, когда примитив нельзя использовать (например, в коллекциях ArrayList<Integer>).