기본 래퍼 클래스(Primitive Wrapper Class)는 8개의 기본형을 캡슐화(객체로 변환)하여, 이를 다른 클래스나 메서드에서 객체로 사용할 수 있도록 해주는 클래스이다 (feat. Wikipedia)
기본 래퍼 클래스의 메모리 저장 방식
- 기본 래퍼 클래스(참고 : 참조형)는 Stack 영역에 실제 값이 저장된 공간의 주소를 저장하고, Heap 영역에 실제 값을 저장한다.
- ex)
기본 래퍼 클래스 종류
기본형 | 기본 래퍼 클래스 |
---|---|
byte | Byte |
char | Character |
int | Integer |
float | Float |
double | Double |
boolean | Boolean |
long | Long |
short | Short |
오토 박싱(Auto Boxing), 오토 언박싱(Auto Unboxing)
박싱 : 기본형을 기본 래퍼 클래스의 객체(인스턴스)로 변환하는 것
언박싱 : 기본 래퍼 클래스의 객체(인스턴스)에 저장된 실제 값을 기본형으로 변환하는 것
- 오토 박싱과 오토 언박싱은 자바 컴파일러가 상황에 따라 박싱과 언박싱을 자동으로 처리해주는 것을 의미한다. (JDK 1.5 버전부터 도입)
Integer i_ref = 1000; // 오토 박싱 // Integer i_ref = Integer.valueOf(1000); // 박싱 int i_pre = i_ref; // 오토 언박싱 // int i_pre = i_ref.intValue(); // 언박싱 int sum_prem = i_pre + i_ref; // 오토 언박싱 // int sum_prem = i_pre + i_ref.intValue(); // 언박싱 Integer sum_ref = i_pre + i_ref; // 오토 언박싱과 오토 박싱 // Integer sum_ref = Integer.valueOf(i_pre + i_ref.intValue()); // 언박싱과 박싱
기본 래퍼 클래스(ex) Integer) 참고 사항
- Java 9 버전부터는 기본 래퍼 클래스의 생성자 사용이 deprecated 처리되었다.
Integer i1 = 777; Integer i2 = Integer.valueOf(777); Integer i3 = Integer.valueOf("777"); // 박싱 Integer i4 = "777"; // 컴파일 오류 Integer i5 = new Integer(777); // 컴파일 오류 Integer i6 = new Integer("777"); // 컴파일 오류
- 자바 컴파일러는 오토 박싱 시 생성자 대신 Integer.valueOf()를 사용하며, 해당 메서드는 캐싱된 Integer 객체를 반환한다.
- JVM은 -128에서 127까지의 int 값에 대한 Integer 객체를 캐싱하고, 이러한 객체들은 Heap 영역의 내의 Constant Pool에 생성된다. 메모리 절약 및 속도 향상의 이점이 있다.
Integer i1 = 127; Integer i2 = 127; System.out.println(i1 == i2); // 출력 : true -> i1과 i2는 같은 객체를 참조 -> 주소 비교 System.out.println(i1.equals(i2)); // 출력 : true -> Integer의 값 비교 Integer i3 = 128; Integer i4 = 128; System.out.println(i3 == i4); // 출력 : false -> i3와 i4는 다른 객체를 참조 -> 주소 비교 System.out.println(i3.equals(i4)); // 출력 : true -> Integer의 값 비교
// Integer 클래스의 valueOf() 메서드 public final class Integer extends Number implements Comparable<Integer>, Constable, ConstantDesc { ... @IntrinsicCandidate public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); } ... }
- JVM은 -128에서 127까지의 int 값에 대한 Integer 객체를 캐싱하고, 이러한 객체들은 Heap 영역의 내의 Constant Pool에 생성된다. 메모리 절약 및 속도 향상의 이점이 있다.
-
기본 생성 클래스는 불변 객체이므로, 값이 변경되면 새로운 객체가 생성된다. 따라서, 잦은 연산은 성능 저하(메모리 낭비, 속도 저하 등)가 발생할 수 있다.
- 예시 1 : 객체의 주소
Integer i = 1000; System.out.println(System.identityHashCode(i)); // 출력 : 1915318863 i += 500; System.out.println(System.identityHashCode(i)); // 출력 : 1283928880
- 예시 2 : 연산 속도 비교 : 기본형 vs 기본 래퍼 클래스
long startTime_pre = System.nanoTime(); // 1. 기본형 연산 int sum_pre = 0; for (int i = 0; i < 10_000; i++) { sum_pre += i; } long endTime_pre = System.nanoTime(); System.out.println("기본형 실행 시간: " + (endTime_pre - startTime_pre) + " ns"); // ---------------> 기본형 실행 시간: 101979 ns long startTime_ref = System.nanoTime(); // 2. 기본 래퍼 클래스 연산 Integer sum_ref = 0; // Integer.valueOf(0); for (int i = 0; i < 10_000; i++) { // for문의 코드 실행할 때마다 새로운 객체 생성 sum_ref += i; // sum_ref = Integer.valueOf(sum_ref.intValue() + i); } long endTime_ref = System.nanoTime(); System.out.println("기본 래퍼 클래스 실행 시간: " + (endTime_ref - startTime_ref) + " ns"); // ---------------> 기본 래퍼 클래스 실행 시간: 1822995 ns // 실행시간 차이가 크다. // 즉, 잦은 연산이 필요할 때 CPU와 메모리를 낭비하고 싶지 않다면 // 기본 타입인 int을 사용하거나 // 객체를 재사용할 수 있는 새로운 클래스를 정의해야 할 것이다.
- 예시 1 : 객체의 주소
- 참고 자료
https://stackoverflow.com/questions/13098143/why-does-the-behavior-of-the-integer-constant-pool-change-at-127
https://stackoverflow.com/questions/3689745/what-exactly-does-comparing-integers-with-do
https://stackoverflow.com/questions/3131136/integers-caching-in-java
https://inpa.tistory.com/entry/JAVA-%E2%98%95-wrapper-class-Boxing-UnBoxing
https://jaehoney.tistory.com/101