이전 글에서 기본형 vs 참조형에 대해 설명했는데 더 자세히 공부해보겠다.
https://codstudy.tistory.com/93
자바 공부#1_01. 기본형(Primitive type) vs 참조형(Reference type)
Datatype 종류:1. 기본형(primitive type): 실제 '값' 저장2. 참조형(reference type): 객체의 '주소' 저장.(null 또는 객체의 주소(4byte)) 1. 기본형(Primitive type)논리형 - boolean문자형 - char정수형 - byte, short, int, lo
codstudy.tistory.com
| 타입 | 기본형 (primitive type) | 참조형 (Reference Type, 클래스) |
| 메모리 | 스택에 저장 | 힙에 객체로 저장 |
| 값의 유효 범위 | -2³¹ ~ 2³¹-1 | 동일 |
| null 허용 | ❌ (null 불가) | ✅ (null 허용) |
| 객체 메서드 | ❌ 사용 불가 | ✅ .compareTo(), .equals() 등 사용 가능 |
| 컬렉션 사용 | ❌ 불가능 (List 불가) | ✅ 가능 (List 가능) |
| 연산 방식 | 기계 수준의 산술 연산 | 언박싱 후 연산(내부적으로 int로 반환됨) |
| 성능 | 빠름 | 느림(오토박싱/언박싱 overhead) |
자바는 원래 모든 객체는 클래스 기반인데, int는 너무 자주 쓰이니까 특별히 만들어진 기본형.
하지만 자바 컬렉션(List, Set, Map 등)은 객체만 넣을 수 있어서 int는 안 들어감.
List<int> list = new ArrayList<>(); // ❌ 안 됨
List<Integer> list = new ArrayList<>(); // ✅ 됨
그래서 int를 객체로 감싼 클래스가 필요한데 → 그게 Integer!
| 계산, 성능 중요 | int (더 빠름, 가볍고 null 없음) |
| 컬렉션에 넣거나 null 가능성 있음 | Integer (List, Map, JSON 등) |
// int 루프
long t1 = System.nanoTime();
int sum1 = 0;
for (int i = 0; i < 10_000_000; i++) sum1 += i;
long t2 = System.nanoTime();
// Integer 루프
long t3 = System.nanoTime();
Integer sum2 = 0;
for (int i = 0; i < 10_000_000; i++) sum2 += i; // 언박싱 + 박싱 발생
long t4 = System.nanoTime();
System.out.println("int time: " + (t2 - t1));
System.out.println("Integer time: " + (t4 - t3));
Integer x = null;
int y = x; // 💥 여기서 NullPointerException 발생!
Integer age = user.getAge(); // DB에 null 가능성 있음
int realAge = age + 1; // 💥 NPE!
int safeAge = (age != null) ? age : 0;
또는
Optional.ofNullable(age).orElse(0);
| 값이 항상 있고, 연산 목적 | int (성능 ↑, NPE ↓) |
| null 가능성 있음 (DB 등) | Integer (Wrapper) |
| 컬렉션 요소로 사용 | Integer (List<Integer> 등) |
항목 핵심 내용
| int vs Integer 성능 | int가 훨씬 빠르다 (루프/연산 최적화) |
| Integer가 null일 때 | 언박싱 시 NPE 터짐 (주의!) |
| 중요 팁 | 연산용은 무조건 int, null 가능 시엔 Integer + null 체크 필수 |
필요하면:
도 같이 정리해줄게! 😎
이해된 거 같아? 예외 실습 코드 만들어볼까?
| INT (NOT NULL) | int |
| INT (nullable) | Integer ✔ |
자바에서는 DB 값이 null 가능성 있을 때 → Wrapper 클래스(Integer)
NOT NULL 컬럼이면 → int 써도 됨
→ JPA나 MyBatis도 기본적으로 nullable이면 Integer로 매핑
| 대량 데이터 + 연산 없음 + null 가능성 있음 | ✅ Integer (컬렉션에 넣고 꺼낼 수 있음) |
| 대량 데이터 + 전부 값 있음 (NOT NULL) | int도 가능하지만, null 안전성 위해 Integer 쓰는 경우 많음 |
자바는 자주 쓰는 정수 값을 메모리 절약 + 성능 향상을 위해
-128 ~ 127 범위의 Integer 객체는 미리 캐싱해두고 재사용
Integer a = 100;
Integer b = 100;
System.out.println(a == b); // ✅ true → 같은 객체 (캐싱)
Integer c = 1000;
Integer d = 1000;
System.out.println(c == d); // ❌ false → 다른 객체 (캐싱 범위 벗어남)
== 비교는 값이 아닌 객체 주소를 비교하니까,
캐싱 범위 안에 있으면 동일 객체로 참조됨 → true,
범위를 넘으면 new Integer()처럼 따로 생성됨 → false
Integer a = 128;
Integer b = 128;
System.out.println(a == b); // false (다른 객체)
System.out.println(a.equals(b)); // true (값은 같음)
❗ 항상 equals()로 비교해야 안전해!
Map<Integer, String> map = new HashMap<>();
map.put(1000, "hello");
System.out.println(map.get(1000)); // ✅ 잘 나옴
System.out.println(map.get(new Integer(1000))); // ✅ 이것도 됨 (equals 비교)
Integer a = 1000;
Integer b = 1000;
System.out.println(a == b); // ❌ false
System.out.println(map.get(a)); // ✅ 그래도 잘 나옴 (equals 사용)
비교 방식 안전 여부 이유
| == | ❌ 위험함 | 객체 주소 비교 → 캐싱 범위 영향 |
| equals() | ✅ 안전함 | 값 비교 (Object 동등성) |
| Map<Integer, ...>에서 키 비교 | ✅ 문제 없음 | hashCode + equals 사용 |
| switch(Integer) | ✅ 자동 언박싱 → int로 처리됨 (괜찮음) |
항목 주의사항
| Integer는 -128~127까지만 캐싱 | 이 범위 안에서는 ==로 비교해도 true 나올 수 있음 |
| 그 외는 새로운 객체 생성됨 | == 비교하면 false 나옴 (주소 다름) |
| 항상 equals()로 비교하자! | 객체 비교에서는 == 절대 쓰지 마! |
| Map<Integer, V>는 안전함 | 내부적으로 equals()와 hashCode()를 쓰기 때문에 값만 같으면 됨 |