규칙5 불필요한 객체는 만들지 말라
- 기능적으로 동일한 객체는 필요할 떄마다 만드는 것보다 재사용하는 편이 낫다.
- 객체를 재사용하는 프로그램은 더 빠르고 더 우아하다.
- 변경 불가능 ( immutable ) 객체는 언제나 재사용할 수 있다
절대로 피해야 할 극단적인 예
String s = new String(“CjhProject”); // No!!
위 문장은 실행될 떄마다 String 객체를 만드는데 “CjhProject” 그 자체로도 String 객체다.
String s = “CjhProject”; // 이게 더 낫다.
생성자와 정적 팩터리 메서드를 함꼐 제공하는 변경 불가능 클래스의 경우 생성자 대신 정적 팩터리 메서드를 이용하면 불필요한 객체 생성을 피할 수 있을떄가 많다. 예를 들어, Boolean(String) 보다는 Boolean.valueOf(String) 쪽이 대체로 바람직하다. 생성자는 호출할 떄마다 새 객체를 만들지만, 정적 팩터리 메서드는 그럴 필요도 없고 실제로 그러지도 않을 것이다.
Public class Person {
private final Date birthDate;
// 이렇게 하면 안 된다!
public boolean isBabyBoomer() {
// 생성 비용이 높은 객체를 쓸데없이 생성한다.
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone(“GMT”));
gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
Date BoomStart = gmsCal.getTime();
gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
Date boomEnd = gmtCal.getTime();
return birthDate.compareTo(boomStart) >= 0 &&
birthDate.compareTo(boomEnd) < 0;
}
}
Public class Person {
private final Date birthDate;
// 다른 필드와 메서드, 생성자는 생략
/** 베이비 붐 시대의 시작과 끝 **/
private static final Date BOOM_START;
private static final Date BOOM_END;
static {
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone(“GMT”));
gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
BOOM_START = gmtCal.getTime();
gmtCal_set(1965, Calendar.JANUARY, 1, 0, 0, 0);
BOOM_END = gmtCal.getTime();
}
public boolean isBabyBoomer() {
return birthDate.compareTo(BOOM_START) >= 0 &&
birthDate.compareTo(BOOM_END) < 0;
}
}
이렇게 개선된 Person 클래스는 Calendar, TimeZone 그리고 Date객체를 클래스가 초기화 될 때 한 번만 만든다.
- 만일 개선된 Person 클래스가 초기화 된 다음에 한번도 호출되지 않는다면, BOOM_START, BOOM_END는 쓸데없이 초기화 된것이다.
- 이는 초기화 지연 ( lazy initialization ) 기법을 사용하면 피할 수 있다.
- 하지만 초기화 지연 기법은 구현이 복잡해지고 성능 개선도 어렵다.
JDK 1.5부터는 쓸데없이 객체를 만들 새로운 방법이 더 생겼다. 자동 객체화 ( Autoboxing )이다.
- 프로그래머들이 자바의 기본 자료형과 그 객체 표현형을 섞어 사용할 수 있도록 해 준다.
- 이 둘간의 변환은 자동으로 이루어 진다.
- 오토박싱 덕분에 기본 자료형과 그 객체 표현형 사이의 차이가 희미해지긴 했지만, 아주 없어진것은 아니다. 의미적인(semantic) 차이는 미미하지만, 성능 차이는 무시하기 어렵다.
// 무시무시할 정도로 느린 프로그램, 어디서 객체가 생성되는지 살펴보자
Public static void main(String[] args) {
Long sum = 0L;
for ( long i = 0; i < Integer.MAX_VALUE; I++) {
sum = sum + i ;
}
System.out.println(sum);
}
- sum이 long이 아니라 Long으로 쓰여진 덕분에 쓸데없는 객체가 2의 31승 만큼 만들어진다.
객체 표현형 대신 기본 자료형을 사용하고, 생각지도 못한 자동 객체화가 발생하지 않도록 유의하라
객체를 만들어서 코드의 명확성과 단순성을 높이고 프로그램의 능력을 향상시킬 수 있다면, 일반적으로는 만드는 것이 좋다.
마찬가지로, 직접 관리하는 객체 풀(object pool)을 만들어 객체 생성을 피하는 기법은 객체 생성 비용이 극단적으로 높ㅈ 않다면 사용하지 않는것이 좋다.
객체 풀을 만드는 비용이 정당화 될 만한 객체의 예로 가장 고전적인 것은 데이터베이스 연결이다.
데이터베이스에 접속하는 비용이 충분히 높으므로 이런 객체들은 재사용하는것이 이치에 맞다.
또한 데이터베이스의 라이센스 정책에 따라서 데이터베이스와 맺는 연결의 수가 제한될 수도 있다.
출처 : 조슈아 블로크, 『 Effective Java 2/E』, 이병준 옮김, 인사이트(2014.9.1), 규칙5 인용.