자바에는 8대 자료형이라 불리는 기본 자료형이 있다.
이 기본 자료형을 특별하게 분류하는 이유는 클래스가 아니기 때문이다.
클래스가 아니라는 것은 다른 말로 참조타입(reference type)이 아니라는 뜻이다.
당장 적당한 예시가 떠오르지 않지만, 이 기본 타입들에 대해 객체로 표현해야 하는 경우가 있는데 문제는 참조타입이 아니기 때문에 객체를 생성하지 못하는 문제가 있다.
그래서 Java에는 Wrapper(래퍼) 라고 부르는 클래스가 존재한다.
이 클래스들은 기본 자료형을 참조타입으로 객체를 생성할 수 있도록 해준다.
기본 타입 (primitive type) | 래퍼클래스 (Wrapper class) |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
Java에서는 primitive type 과 Wrapper 타입의 변환을 자동으로 해주는데, 이걸을 AutoBoxing 이라고 한다.
다음 코드는 박싱과 언박싱, 그리고 자동 박싱과 자동 언박싱의 예를 보여준다.
package com.tistory.xxxelppa.section_001;
public class BoxingUnboxing {
public static void main(String[] args) {
Integer myWrapperNumber = new Integer(21); // Boxing
int myPrimitiveNumber = myWrapperNumber.intValue(); // Unboxing
Integer myAutoBoxingNumber = 21; // AutoBoxing
int myAutoUnBoxingNumber = myAutoBoxingNumber; // AutoUnBoxing
}
}
꽤나 편리해 보이는데 왜 되도록 피하라고 했을까?
다음 코드를 보자
private static long sum_with_autoboxing() {
Long sum = 0L;
for (long i = 0; i < Integer.MAX_VALUE; ++i) {
sum += i;
}
return sum;
}
그다지 문제가 있어보이지 않는 0부터 4바이트 정수 자료형 int 의 최대 표현 범위의 값의 누적 합을 계산해 long 타입의 변수에 담는 코드이다.
조금 더 정확히 얘기하면 long 타입의 i 값을 Long 타입의 변수에 누적 합을 구하고 있다.
문제는 sum += i; 이 부분이다.
Wrapper 타입의 변수에 값을 할당할 때 우리 눈에는 그저 sum += i; 만 보이지만
컴파일러는 이 코드를 sum += Long.valueOf(i); 로 바꾸기 때문이다.
즉, 저 라인이 실행 될 때마다 Long 타입의 객체를 생성 한다는 것을 의미한다.
지금의 경우 21억개의 무의미한 객체를 생성했다고 생각하면 될 것 같다.
그래서 위의 코드에서 딱 한 문자인 L 을 l 로 고쳐보았다.
private static long sum_without_autoboxing() {
long sum = 0L;
for (long i = 0; i < Integer.MAX_VALUE; ++i) {
sum += i;
}
return sum;
}
그럼 이게 얼마나 차이가 나는걸까?
package com.tistory.xxxelppa.section_001;
/**
* autoboxing 테스트
*
* [실행 결과]
* use autoboxing : 6644 ms
* not use autoboxing : 609 ms
*
* 되도록 박싱된 기본 타입보다 기본 타입을 사용하고
* 의도하지 않은 오토박싱이 사용되지 않도록 주의 필요
*
*/
public class Main {
public static void main(String[] args) {
long startTime, endTime;
startTime = System.currentTimeMillis();
sum_with_autoboxing();
endTime = System.currentTimeMillis();
System.out.println("use autoboxing : " + (endTime - startTime) + " ms");
startTime = System.currentTimeMillis();
sum_without_autoboxing();
endTime = System.currentTimeMillis();
System.out.println("not use autoboxing : " + (endTime - startTime) + " ms");
}
private static long sum_with_autoboxing() {
Long sum = 0L;
for (long i = 0; i < Integer.MAX_VALUE; ++i) {
sum += i;
}
return sum;
}
private static long sum_without_autoboxing() {
long sum = 0L;
for (long i = 0; i < Integer.MAX_VALUE; ++i) {
sum += i;
}
return sum;
}
}
코드의 맨 위에 주석으로 실행 결과를 기록해 두었다.
매변 결과는 조금씩 다르겠지만, 6000ms 즉 6초나 더 오래 걸린것을 확인할 수 있었다.
물론 이렇게 극단적인 상황이 자주 발생하지는 않겠지만, Auto Boxing 을 사용했을 때 내부에서 어떤 일이 생기는지 알고 있으면 좋을것 같아서 정리해 보았다.
'프로그래밍 언어 > Java' 카테고리의 다른 글
자바에서 인터페이스를 사용하는 이유 (1) | 2017.07.09 |
---|---|
자바 배열 객체에 대해서 (0) | 2017.07.07 |
자바에서 클래스와 객체의 관계 (0) | 2017.06.09 |
java 환경변수 설정 방법과 이유 (0) | 2017.06.08 |
String vs. StringBuilder vs. StringBuffer 예제를 통한 비교 (0) | 2017.06.07 |