Урок по треку Java 7 мин

Основы Java: типы, обёртки, static

Примитивы и обёртки, автоупаковка и кеш Integer, передача по значению, static, final и порядок инициализации.

С этих вопросов начинается почти любой собес на Java: что такое примитив, чем int отличается от Integer, почему == иногда «работает» на объектах и в каком порядке инициализируется класс.

Восемь примитивов

ТипРазмерЗначение по умолчанию (в поле)
byte8 бит0
short16 бит0
int32 бита0
long64 бита0L
float32 бита0.0f
double64 бита0.0
char16 бит, беззнаковый (UTF-16)'\u0000'
booleanразмер не задан спецификациейfalse

Локальные переменные значений по умолчанию не получают: компилятор не даст прочитать неинициализированную.

Целочисленное переполнение молчит: Integer.MAX_VALUE + 1 равно Integer.MIN_VALUE. Если нужно исключение, есть Math.addExact. Деньги не считают в double: 0.1 + 0.2 даёт 0.30000000000000004, для денег берут BigDecimal или целые копейки в long.

Обёртки и автоупаковка

У каждого примитива есть класс-обёртка: Integer, Long, Character, Boolean... Компилятор сам упаковывает и распаковывает:

Integer a = 5;        // Integer.valueOf(5)
int b = a + 1;        // a.intValue() + 1
Integer n = null;
int c = n;            // NullPointerException при распаковке

Integer.valueOf кеширует значения от -128 до 127 (верхнюю границу можно поднять флагом -XX:AutoBoxCacheMax). Поэтому:

Integer x = 127, y = 127;
System.out.println(x == y);      // true: один объект из кеша
Integer p = 128, q = 128;
System.out.println(p == q);      // false: два разных объекта
System.out.println(p.equals(q)); // true

Правило: объекты сравнивают через equals, == сравнивает ссылки. Кеш есть также у Byte, Short, Long, Character (0..127) и Boolean, но не у Float и Double.

Передача параметров

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

static void change(StringBuilder sb) {
    sb.append("!");          // видно снаружи
    sb = new StringBuilder(); // снаружи не видно
}

static и final

  • static поле одно на класс, а не на объект. static метод не имеет this и не переопределяется, а скрывается (method hiding).
  • final переменную нельзя переприсвоить, final метод нельзя переопределить, от final класса нельзя наследоваться. final ссылка не делает объект неизменяемым: final List<String> list можно наполнять.
  • Константа времени компиляции (static final int MAX = 10) подставляется в код вызывающих классов. Поменяли значение и не перекомпилировали зависимый модуль: там останется старое.

Порядок инициализации

При первом обращении к классу: статические поля и static-блоки сверху вниз, один раз. При каждом new: сначала конструктор родителя, потом инициализаторы полей и блоки экземпляра сверху вниз, потом тело конструктора.

class A {
    static { System.out.println("A static"); }
    { System.out.println("A init"); }
    A() { System.out.println("A()"); }
}
class B extends A {
    static { System.out.println("B static"); }
    { System.out.println("B init"); }
    B() { System.out.println("B()"); }
}
// new B(); new B();
// A static, B static, A init, A(), B init, B(), A init, A(), B init, B()

Частые ошибки

  • Сравнивать Integer или String через == и удивляться, что на тестах работало.
  • Распаковка null в int (например, map.get(key) в арифметике).
  • Ждать, что метод «вернёт» новую ссылку через параметр.
  • Вызывать переопределяемый метод из конструктора родителя: подкласс ещё не проинициализировал свои поля.

Что спросят на собесе

  • Какие примитивы есть и чем int отличается от Integer?
  • Что выведет Integer a = 128, b = 128; a == b и почему?
  • Java передаёт объекты по ссылке или по значению?
  • В каком порядке выполняются static-блоки, блоки инициализации и конструкторы при наследовании?
  • Что значит final для ссылки на коллекцию?

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.