#Java valueOf메서드들 Java의 몇몇 클래스는 valueOf라는 메서드를 가지고 있다. 대표적으로 원시 데이터 타입을 클래스로 나타낸 Wrapper클래스들이 있다. Integer, Character, Double, String 등은 모두 valueOf메서드를 가지고 있다. valueOf메서드의 기능을 보면 이 것들은 인자로 전달한 값을 해당 valueOf메서드를 호출한 클래스의 객체로 반환한다. 걷보기에는 new키워드를 사용해서 객체를 생성하는 것과 별반 다를게 없어 보인다. 하지만, valueOf메서드를 통한 객체과 new키워드를 통한 객체 생성은 분명 차이가 있다. 그리고 가능하면 valueOf메서드 사용을 권장한다. 그 이유는 무엇일까?
#객체를 얻는 것과 객체를 생성하는 것의 차이
Integer i1 = 10;
Integer i2 = new Integer(10);
Integer i3 = Integer.valueOf(10);
System.out.println(i1 == i2);
System.out.println(i1 == i3);
위 코드의 출력값은 무엇일까? true true? 정답은 false true이다.(Java8 기준) i1은 오토박싱 기능에 의해 자동으로 10값을 가진 Integer 객체를 얻는다. i2는 10값을 인자로 받아 Integer 객체를 생성한다. i3는 valueOf메서드를 통해 10값을 가진 Integer 객체를 얻는다. 이후의 == 비교를 하는데 i1, i2, i3는 클래스 객체이기 때문에 해시코드를 통해 비교를 수행한다. 즉, i1과 i3는 같은 객체이고 i2는 다른 객체라는 것이다. 이유는 객체를 얻는가, 생성하느가에 차이에 있다. Integer클래스를 포함한 모든 Wrapper클래스는 내부적으로 캐시가 존재한다. 오토박싱과 valueOf메서드는 이 캐시를 통해서 객체를 가져오며 new는 캐시와 상관업이 새로운 객체를 생성한다. 그래서 valueOf메서드가 존재함에도 new로 객체로 생성한다는 것은 비효율적이다. 쓸때없이 메모리만 차지하고, 불필요한 객체를 생성하는 것에 자원을 소비한다.
#Integer클래스의 내부 ##IntegerCache 내부클래스 대표적으로 Integer클래스를 통해 알아보자. Integer클래스는 내부적으로 IntegerCache라는 private으로 선언된 내부클래스가 존재한다. 이 클래스는 Integer 값의 캐시를 생성하고 관리한다. 아래는 IntegerCache클래스의 static 블록 코드이다.
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
위처럼 Integer클래스는 내부적으로 캐시를 유지한다. 그런데 재미있는 것은 캐시의 범위가 -128부터 127까지이다.(위 코드에는 안나와있지만 low변수는 상수이며 -128의 값을 가진다) Integer의 값 범위에 비하면 하염없이 작다. 이점을 유이해야한다.
##Integer.valueOf메서드
public static Integer valueOf(int i){
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
위 코드는 Integer클래스의 valueOf정적메서드이다. 코드를 보면 먼저 캐시를 확인하는 것을 볼 수 있다. 캐시 범위 내 이면 캐시에 저장된 객체를 반환하고, 그게 아니라면 새롭게 객체를 생성해서 반환한다. 그래서 캐시 범위를 넘어서는 값을 사용하면 객체비교의 결과가 달라진다.
Integer i1 = 500;
Integer i2 = new Integer(500);
Integer i3 = Integer.valueOf(500);
System.out.println(i1 == i2);
System.out.println(i1 == i3);
위 코드의 결과는 false false이다. 값이 캐시의 범위를 넘어서기 때문에 오토박싱, valueOf메서드 모두 새로운 객체를 생성해서 반환하기 때문이다. 이 경우에는 결국 i1, i2, i3 모두 new키워드로 새로 객체를 생성하는 격이다.
##캐시 범위 조절 참고로 캐시의 크기는 사용자가 임의로 조정할 수 있다. -XX:AutoBoxCacheMax라는 옵션을 통해서 캐시의 크기을 지정할 수 있다. 이 옵션으로 지정된 값은 java.lang.Integer.IntegerCache.high 속성에 저장되며, 캐시를 생성할때 참고된다.
#String클래스 문자열의 경우 숫자와는 다르게 동작하긴 하지만, 역시 캐시가 존재한다. 문자열의 캐시는 JVM단위로 관리된다. 그래서 아래 코드의 경우 false true의 출력을 보인다.
String s1 = "hello";
String s2 = new String("hello");
String s3 = String.valueOf("hello");
System.out.println(s1 == s2);
System.out.println(s1 == s3);
#참고 위 코드들은 객체를 비교하기 위해 ==연산자를 사용했다. 객체를 ==연산자로 비교하면 객체의 해시코드로 비교를 한다. 만일 값이 같은지 비교를 하고자 한다면 equals메서드를 사용해야한다.