Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Классы-обертки над примитивами в Java
Класс-обертка (Wrapper Class) — это объектный класс, который оборачивает примитивный тип данных и предоставляет методы для работы с ним. Это необходимо, потому что в Java примитивные типы — это не объекты, а простые значения.
Зачем нужны обертки?
Три основные причины:
1. Collections требуют объекты
Коллекции (List, Set, Map) работают только с Object, не с примитивами:
// ❌ Это не скомпилируется:
List<int> numbers = new ArrayList<>();
// ✅ Нужна обертка:
List<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
// Тоже самое для других типов:
List<Double> prices = new ArrayList<>();
List<Boolean> flags = new ArrayList<>();
List<Long> timestamps = new ArrayList<>();
2. Null значения
Примитив не может быть null, но обертка может:
int primitiveInt = null; // ❌ Ошибка компиляции
Integer wrappedInt = null; // ✅ OK
// Практический пример:
public class User {
private String name;
private Integer age; // Может быть null, если возраст не указан
private Integer salary; // Может быть null, если информация конфиденциальна
public User(String name) {
this.name = name;
this.age = null; // Нет информации о возрасте
}
}
3. Методы и константы
Обертки предоставляют полезные методы и константы:
// Integer:
Integer max = Integer.MAX_VALUE; // 2147483647
Integer min = Integer.MIN_VALUE; // -2147483648
// Парсинг:
int num = Integer.parseInt("42");
int hex = Integer.parseInt("FF", 16); // 255
// Конвертация:
String binary = Integer.toBinaryString(42); // "101010"
String hex = Integer.toHexString(255); // "ff"
// Double:
boolean isInfinite = Double.isInfinite(1.0 / 0.0);
boolean isNaN = Double.isNaN(0.0 / 0.0);
// Boolean:
Boolean result = Boolean.parseBoolean("true");
Все типы оберток
primitive → Wrapper Class
─────────────────────────────
int → Integer
long → Long
double → Double
float → Float
boolean → Boolean
char → Character
short → Short
byte → Byte
Автобоксинг и Анбоксинг
В Java 5+ есть автоматическое преобразование между примитивом и оберткой:
// Autoboxing (примитив → обертка):
Integer wrapped = 42; // int автоматически упакуется в Integer
// Unboxing (обертка → примитив):
int primitive = wrapped; // Integer автоматически распакуется в int
// Полная версия без автобоксинга:
Integer wrapped = Integer.valueOf(42);
int primitive = wrapped.intValue();
Практические примеры использования
Пример 1: Работа с коллекциями
public class StatisticsCalculator {
private final List<Integer> numbers = new ArrayList<>();
public void addScore(int score) {
numbers.add(score); // Автобоксинг: int → Integer
}
public double calculateAverage() {
return numbers.stream()
.mapToInt(Integer::intValue) // Анбоксинг: Integer → int
.average()
.orElse(0.0);
}
public Optional<Integer> findMax() {
return numbers.stream()
.max(Integer::compareTo);
}
}
Пример 2: API с nullable значениями
public class UserDTO {
private String name;
private Integer age; // Может быть null
private Double salary; // Может быть null
private Boolean isActive; // Может быть null
public UserDTO(String name, Integer age, Double salary, Boolean isActive) {
this.name = name;
this.age = age;
this.salary = salary;
this.isActive = isActive;
}
// Конструктор для минимальной информации:
public UserDTO(String name) {
this(name, null, null, true);
}
}
// Использование:
UserDTO user1 = new UserDTO("Alice", 30, 50000.0, true);
UserDTO user2 = new UserDTO("Bob"); // age и salary = null
UserDTO user3 = new UserDTO("Charlie", 25, null, true); // salary скрыта
Пример 3: Кэширование оберток
// Java кэширует Integer значения от -128 до 127:
Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true (один и тот же объект в памяти)
Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false (разные объекты)
// Правильное сравнение оберток:
if (a.equals(b)) { // ✅ Правильно
System.out.println("Equal");
}
if (a == b) { // ❌ Опасно, может дать неправильный результат
System.out.println("Same object");
}
Производительность
Обертки медленнее примитивов:
// Бенчмарк:
public void benchmarkPrimitive() {
long sum = 0;
for (int i = 0; i < 1_000_000; i++) {
sum += i; // Работает с примитивом int
}
// ~1-2 мс
}
public void benchmarkWrapper() {
Long sum = 0L;
for (int i = 0; i < 1_000_000; i++) {
sum += i; // Идёт анбоксинг и автобоксинг
}
// ~10-20 мс (в 10 раз медленнее!)
}
Когда что использовать?
| Ситуация | Используй |
|---|---|
| Коллекция (List, Set, Map) | Обертка (Integer, Double) |
| Нужен null | Обертка (Integer, Boolean) |
| Много вычислений | Примитив (int, double) |
| Свойство в классе может не иметь значения | Обертка |
| Критична производительность цикла | Примитив |
| Работа с Stream API | Обертка (или специальные IntStream, LongStream) |
Частая ошибка: NullPointerException при анбоксинге
// Опасно:
public void process(Integer value) {
int primitive = value; // Может быть NullPointerException если value = null
System.out.println(primitive + 10);
}
process(null); // Crash!
// Правильно:
public void process(Integer value) {
if (value == null) {
System.out.println("No value provided");
return;
}
int primitive = value;
System.out.println(primitive + 10);
}
// Или используй Optional:
public void process(Optional<Integer> value) {
int primitive = value.orElse(0);
System.out.println(primitive + 10);
}
Вывод
Классы-обертки — это неотъемлемая часть Java, которая позволяет:
- Использовать примитивы в коллекциях
- Работать с null значениями
- Вызывать методы работы с типами
Но помни: обертки медленнее и тяжелее примитивов. Используй их когда нужно, но в критичных по производительности местах предпочитай примитивы и специальные коллекции (IntStream, IntArrayList).