#05 불필요한 객체의 생성을 피하자
객체 생성을 위해 필요한 리소스를 아끼기 위해, 기능적으로 동일한 객체는 매번 생성하기보다는 하나를 계속 이용하는 게 좋습니다.
불필요한 객체 생성을 피하는 방법에 대해 알아보겠습니다.
아래 코드를 보면 String 불변(immutable) 객체를 이용하는 2가지 예입니다.
둘 다 "test"라는 문자열을 가지고 있지만, 내부를 살펴보면 서로 다르게 생성됩니다.
String s1 = new String("test"); // new String
String s2 = "test"; // literal pool
매번 새로운 인스턴스를 생성하는 new 연산자 방식과 어떤 코드에서도 동일한 문자열 리터럴을 가지게 하는 리터럴 풀 방식이 있습니다.
아래 그림을 보면 s1과 s4는 new 연산자 방식을 통해 String 객체를 생성합니다.
매번 새로운 인스턴스를 생성하기 때문에 메모리 힙 영역에 항상 새로이 할당됩니다.
기능적으로 동일한 객체가 매번 새로 생성되기 때문에 불필요한 객체가 늘어나게 됩니다.
s2와 s3처럼 리터럴 풀 방식 통해 String 객체를 생성하면, 힙 영역의 리터럴 풀 메모리 영역에 할당됩니다.
만약 같은 값을 갖는 String 객체가 나오더라도 하나만 만들어서 공유합니다.
s2와 s3는 "test"라는 같은 String 객체를 참조합니다.
따라서 불필요한 객체를 매번 생성하지 않아도 됩니다.
참고로 두 개 이상의 객체가 같은 객체를 참조하는 것을 앨리어싱이라 합니다.
불변 객체의 불필요한 객체 생성을 막으려면 생성자보다는 static 팩토리 메서드를 이용하는 게 좋습니다.
예를 들어, 아래와 같이 생성자인 new Boolean("true") 보다는 static 팩토리 메서드인 Boolean.valueOf("true")를 이용하는 게 더 좋습니다.
Boolean b1= new Boolean("true");
Boolean b2 = Boolean.valueOf("true");
static 팩토리 메서드를 이용하면 미리 생성된 Boolean 객체를 재사용할 수 있습니다.
가변 객체도 불변 객체처럼 재사용이 가능합니다.
아래 코드는 생년월일에 따라 고등학생인지 여부를 확인하는 isHighSchoolStudent 메서드를 가진 Student 클래스입니다.
가변 객체인 Date 객체를 가지고 있지만 이 메서드에서는 한번 값이 지정되면 절대 변경되지 않습니다.
하지만 isHighSchoolStudent 메서드는 매번 호출될 때마다 Calendar 객체와 TimeZone, Date 객체를 불필요하게 생성합니다.
아래 코드는 static 초기화를 이용하여 불필요한 객체를 매번 생성하지 않도록 했습니다.
하지만 이 코드의 단점은 isHighSchoolStudent 메서드를 호출하지 않는다면 쓸데없이 START_DATE, END_DATE 객체만 초기화하게 됩니다.
이를 해결하기 위해 늦 초기화(Lazy intialization)를 이용할 수 있지만, 성능 개선에 비해서 구현이 많이 복잡해지기 때문에 권장하진 않습니다.
이전 글에서도 언급한 적 있지만 Autoboxing이란, 기본형(Primitive Type)과 해당 wrapper 객체 간 변환을 Java 컴파일러가 자동으로 해주는 것을 말합니다.
Boxing : 기본형을 해당 참조형으로 변환. (int -> Integer)
Unboxing : 참조형을 해당 기본형으로 변환. (Integer -> int)
아래 코드를 보면 Long sum 참조형 변수에 long i 기본형 변수의 값을 계속 더하게 됩니다.
이때 long 기본형 변수를 Long 참조형 변수로 변환하는 오토 박싱이 발생하게 됩니다.
이는 불필요한 Long 인스턴스를 생성하는 문제가 발생합니다.
오토 박싱으로 인한 문제가 발생하지 않도록 주의해야 합니다.
코딩을 하다 보면 재사용에 대해 매번 고민하는데 주의해야겠습니다.