규칙5 불필요한 객체는 만들지 말라

규칙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 인용.