Java 오토박싱/언박싱과 Integer 캐시
선수학습(1개)
요약
Java 오토박싱(Autoboxing)과 언박싱(Unboxing), Integer 캐시의 동작 원리를 알아봅니다. Integer == 비교에서 127과 128이 다른 결과가 나오는 이유, null 언박싱이 NullPointerException을 일으키는 함정을 코드 동작 중심으로 정리합니다.
오토박싱/언박싱 핵심 정리
| 개념 | 설명 | 예시 |
|---|---|---|
| 오토박싱 | 기본 타입을 자동으로 래퍼 객체로 변환 | Integer a = 5; (int → Integer) |
| 언박싱 | 래퍼 객체를 자동으로 기본 타입으로 변환 | int x = a; (Integer → int) |
| Integer 캐시 | -128 ~ 127 값은 Integer 객체를 미리 만들어 재사용 | Integer.valueOf(100) |
| 캐시 경계 함정 | a == b가 127에서 true, 128에서 false | 주소 비교가 달라짐 |
| null 언박싱 | null을 언박싱하면 NullPointerException | Integer n = null; int x = n; |
기본 타입과 래퍼 클래스 쌩기초
Java에는 숫자를 담는 두 가지 타입이 있습니다.
- 기본 타입 (primitive type):
int,double,boolean,char등. 메모리에 값을 직접 저장합니다. - 래퍼 클래스 (wrapper class):
Integer,Double,Boolean,Character등.int같은 값을 객체로 감싸서 저장합니다.
| 기본 타입 | 래퍼 클래스 |
|---|---|
int | Integer |
long | Long |
double | Double |
boolean | Boolean |
char | Character |
왜 래퍼 클래스가 필요한가?
Java의 컬렉션 (List, Map 등)은 객체만 담을 수 있습니다. int는 기본 타입이므로 List<int>는 만들 수 없고, 대신 List<Integer>를 사용합니다. 이때 숫자를 Integer 객체로 감싸는 역할을 래퍼 클래스가 합니다.
오토박싱과 언박싱 기초
Java 5부터는 기본 타입과 래퍼 클래스 사이의 변환이 자동으로 일어납니다.
오토박싱 (Autoboxing)
기본 타입 값이 래퍼 클래스 타입 자리에 오면, 컴파일러가 자동으로 래퍼 객체를 만들어 줍니다.
내부적으로는 Integer.valueOf(5)가 호출되어 래퍼 객체를 반환합니다. 개발자가 new Integer(5) 또는 Integer.valueOf(5)를 직접 쓰지 않아도 됩니다.
언박싱 (Unboxing)
반대로 래퍼 객체가 기본 타입 자리에 오면, 컴파일러가 자동으로 intValue() 같은 메서드를 호출해 값을 꺼냅니다.
| 변환 방향 | 이름 | 내부 동작 |
|---|---|---|
| 기본 → 래퍼 | 오토박싱 | Integer.valueOf(n) |
| 래퍼 → 기본 | 언박싱 | obj.intValue() |
Integer 캐시 기초
Integer.valueOf(n)은 호출될 때마다 새로운 객체를 만들지 않습니다. 자주 쓰이는 작은 정수값(-128 ~ 127)은 캐시에 미리 Integer 객체를 만들어두고 재사용합니다. 이 범위의 값으로 오토박싱하면 항상 같은 객체를 가리키게 됩니다.
캐시 범위를 벗어나면 새 객체
128 이상이나 -129 이하의 값은 캐시에 없으므로 새 객체가 생성됩니다. 같은 값이라도 서로 다른 객체가 되어 == 비교 결과가 달라집니다.
| 값 범위 | 오토박싱 결과 | a == b | a.equals(b) |
|---|---|---|---|
| -128 ~ 127 | 캐시 객체 재사용 | true | true |
| 128 이상 또는 -129 이하 | 매번 새 객체 | false | true |
왜 하필 -128 ~ 127일까?
byte 타입의 표현 범위(-128 ~ 127)와 같습니다. 작은 정수는 프로그램에서 매우 자주 등장하므로, 이 범위만 미리 만들어두면 메모리와 실행 속도를 절약할 수 있습니다. 큰 값까지 모두 캐시하면 오히려 메모리 낭비가 되기 때문에 Java가 타협한 구간입니다.
null 언박싱 함정 심화
래퍼 객체는 null 값을 가질 수 있습니다(기본 타입과의 결정적 차이). null인 상태에서 언박싱이 일어나면 NullPointerException 이 발생합니다.
int x = a에서 컴파일러는 a.intValue()를 호출하도록 코드를 변환합니다. a가 null이므로 null.intValue()가 되어 NPE가 발생하는 것입니다. null 검사 없이 Integer 변수를 산술 연산에 사용할 때도 같은 문제가 생깁니다.
자주 생기는 실수
| 상황 | 문제 | 해결 |
|---|---|---|
Integer → int 대입 | null이면 NPE | null 검사 후 대입 |
Integer + int 연산 | null이면 NPE | null 검사 후 연산 |
Map.get() 결과를 int로 받기 | 키가 없으면 null 반환 → NPE | getOrDefault() 사용 |