В каких случаях использовать примитивы, а в каких обертки примитивов в Java?
Что проверяют
Понимаете ли вы, во что обходится обёртка: объект в куче, возможный null и автоупаковка, которая срабатывает незаметно. Следом почти всегда показывают сравнение двух Integer через == и спрашивают, что выведет код.
Как отвечать
Примитивы (int, long, double, boolean и другие) хранят значение напрямую, не бывают null и имеют значение по умолчанию: 0, false. Обёртки (Integer, Long, Boolean и так далее) это полноценные объекты: заголовок, ссылка, место в куче, работа для сборщика мусора.
Примитив по умолчанию:
- локальные переменные, счётчики, арифметика, циклы;
- большие массивы чисел:
int[]на миллион элементов занимает около 4 МБ, аList<Integer>в несколько раз больше, ведь каждый элемент отдельный объект; - стримы по числам:
IntStream,mapToInt,sum()без упаковки на каждом шаге.
Обёртка, когда без неё никак:
- дженерики и коллекции:
List<Integer>,Map<String, Long>, примитив в параметр типа не подставляется; - нужно «значение отсутствует»: поле сущности JPA
Long id, гдеnullзначит «ещё не сохранена», необязательный параметр запроса, поле DTO, которое клиент мог не прислать. Сintвы не отличите ноль от отсутствия; - API работает с
Object: рефлексия черезMethod.invoke, сериализация, старые API без дженериков.
Упомяните кэш: Integer.valueOf возвращает один и тот же объект для значений от -128 до 127, поэтому == на маленьких числах случайно «работает», а на 1000 уже нет. Сравнивать обёртки нужно через equals или после распаковки.
Где спотыкаются
Сравнивают Integer через == и не видят бага, пока значения маленькие. Отдельная ловушка: Long.valueOf(1).equals(1) возвращает false, потому что аргумент упакуется в Integer.
Получают NullPointerException на ровном месте: int x = map.get(key) при отсутствующем ключе распаковывает null. То же в тернарном операторе, где одна ветка примитив, а другая обёртка со значением null.
Используют Map<String, Integer> или List<Long> в горячем цикле и удивляются нагрузке на GC. Каждый put(key, count + 1) за пределами кэша создаёт новый объект.