В чем разница между примитивным типом long и объектом Long в Java и когда использовать каждый?
Что проверяют
Понимаете ли вы, что за автоупаковкой стоят настоящие объекты со своими ловушками: сравнение по ссылке, NullPointerException, лишний мусор. Следом обычно дают код с == между двумя Long и просят предсказать результат.
Как отвечать
long: примитив, 64 бита со знаком, отLong.MIN_VALUEдоLong.MAX_VALUE. Хранится прямо в поле, массиве или на стеке, у поля значение по умолчанию0;Long: неизменяемый объект-обёртка. Кроме 8 байт значения у него есть заголовок, поэтому в зависимости от настроек JVM объект занимает 16-24 байта, плюс ссылка на него. Может бытьnull;- автоупаковка: компилятор сам вставляет
Long.valueOf()иlongValue().valueOfкэширует значения от-128до127, всё остальное это новые объекты; - когда
Long: дженерики и коллекции (List<Long>,Map<Long, User>), поля, где «значения нет» это отдельное состояние, напримерidсущности JPA до сохранения; - когда
long: счётчики, арифметика, горячие циклы, большие массивы. Для коллекций примитивов естьlong[],LongStreamи библиотеки вроде fastutil.
Покажите, где обёртка бьёт по производительности незаметно: Long sum = 0L; for (...) sum += x; на каждой итерации распаковывает и создаёт новый объект.
Бонусом упомяните беззнаковые операции: сам long знаковый, но с Java 8 есть Long.compareUnsigned, Long.divideUnsigned и Long.toUnsignedString. Конструктор new Long(...) помечен устаревшим к удалению, вместо него Long.valueOf.
Где спотыкаются
Сравнивают обёртки через ==. Для 100L это случайно работает благодаря кэшу, для 1000L уже нет. Объекты сравнивают через equals или распаковывают явно.
Ловят NullPointerException при распаковке: long total = map.get(id) падает, если ключа нет. Преобразование из Long в long в коде не видно, и искать его приходится по стектрейсу.
Забывают, что Long.valueOf(1L).equals(1) возвращает false: литерал 1 упаковывается в Integer, а equals у обёрток проверяет ещё и тип. По той же причине map.get(1) на Map<Long, ...> молча возвращает null.