Java wrapper class and caching, boxing & unboxing
1. wrapper class
기본 자료형(Primitive Type)을 객체(Reference Type) 로 감싸서 다룰 수 있는 클래스
byte -> Byte, int -> Integer, char -> Character, boolean -> Boolean 등
왜 필요한가?
기본 자료형을 객체로 사용해야하는 경우 필요함.
1. java의 많은 기능들은 객체(Object)를 통해 사용할 수 있도록 설계되어 있음.
그래서 Object의 자식이 아닌 기본 자료형을 사용할 수 없고 wrapper 를 통해 사용함.
Collections, Generic, Reflection , Annotaion 등
2. wrapper 는 기본 제공 기능이 있어 유용함.
equals(), parseInt(), toString(), compareTo(), Integer.MAX_VALUE 등
3. null 표현 가능
DB, json등과 연동 시 없는 값에 대한 표기를 유용하게 할 수 있음
특징
- 불변(immutable): 한 번 생성되면 값을 바꿀 수 없다.
변수에 새 값을 할당하면, 새 값의 주소를 참조하게되며 기존값은 참조를 잃을 뿐임 - null 허용: 객체이기 때문에 null 값을 가질 수 있다.
- 캐싱 처리: Integer, Byte, Short, Long, Character의 일부 값은 내부적으로 캐싱되어 효율성을 높여줌
예를 들어 -128 ~ 127 범위의 Integer 값은 재사용 - 성능 이슈: boxing/unboxing이 자주 발생하면 성능에 영향을 줄 수 있다. (불필요한 객체 생성)
2. wrapper class caching
작은 수의 연산이 빈번한 프로그래밍의 특성에 따라 성능적 관점에서 캐싱이 도입되어 있음-128 ~ 127 :Byte, Short, Integer, Long0~127 :Charactertrue/false :Boolean
그래서, 완전 캐싱인 Boolean을 제외하고는 값 비교에 유의해야함 equals() 권장
Short a = 127;
Short b = 127;
System.out.println(a == b); // true
Long x = 128L;
Long y = 128L;
System.out.println(x == y); // false
System.out.println(x.equals(y)); // true
캐싱에 의해서 wrapper의 생성에 있어서도 차이가 발생
valueOf()의 경우 캐싱이 동작하지만 new의 경우 항상 새 객체를 생성하기 때문에 valueOf()의 메모리 효율이 더 좋다.
Integer a = Integer.valueOf(100);
Integer b = Integer.valueOf(100);
System.out.println(a == b); // true (같은 캐시 객체)
Integer c = new Integer(100);
Integer d = new Integer(100);
System.out.println(c == d); // false (새 객체 각각 생성)
그래서 Short, Integer, Boolean, Long등의 wrapper를 만드는 경우 valueOf()를 사용하는게 좋다!
Integer i = Integer.valueOf(100); // new Interger() is deprecated
Long l = 200L; // 오토 박싱은 내부적으로 Long.valueOf(200L) 동작
특수한 목적을 가진 프로그래밍을 위해서 캐싱 범위를 조절할 수 있다 (써본적 없다..)
java -Djava.lang.Integer.IntegerCache.high=1000 ClassName
3. wrapper class boxing & unboxing
기본형 타입을 wrapper로 변경할 때(boxing), 반대로 wrapper를 기본형으로 바꿀 때(unboxing) 발생한다.
1) 값이 null인 wrapper를 기본형으로 바꾸면 nullPointerException이 발생하기 때문에 유의해야한다.
2) 반복적인 boxing/unboxing은 성능을 저하시킬 수 있다.
- 객체 생성 및 GC 비용 증가 :기본자료형은 스택, 객체는 힙에 저장되기 때문에 객체 생성 비용 및 GC 비용도 증가함
- 메서드 호출 비용 증가 :박싱은 valueOf() 메서드를 호출하기 때문에 기본형에 비해 연산 비용이 큼
+보너스
valueOf()와 new()를 공부하다보니 부동소수점 이슈가 생각남
double을 BigDecimal로 바꿀때 유의할 것
절대 new BigDecimal(double) 쓰면 안됨! new BigDecimal(0.1); 안티코딩
double을 문자열로 변경하고 쓰기 또는 valueOf() 사용하기
new BigDecimal("0.1")
BigDecimal.valueOf(0.1)