기능적으로 동일한 객체는 필요할 때마다 만드는 것보다 재사용하는 편이 낫다.
객체를 재사용하는 프로그램은 메모리 관리에 더 용이하다.
절대로 피해야 할 극단적 예를 하나 들어보자.
String s = new String("stringette");
만위 위의 문장이 순환문(loop)나 자주 호출되는 함수 안에 있으면 그 숫자대로 String 객체가 만들어진다.
* 가령 필자는 2년차 시절, 야구 기록 웹 API XML 반복문 안에 string 문을 통해 작성한 뒤 속도가 현저하게 떨어지는 증상을 겪은 뒤로
StringBuilder를 사용하기 시작했다.
C#이나 자바나 string 문자가 변경되면 새로 객체가 생성된다.
String s = "stringette"; 이라면 stringette2가 되면 새로운 객체가 생성되기에 StringBuilder를 사용하기를 추천한다.
다음은 변경 가능한 객체를 사용하여 사람이 베이비 붐 세대에 속하는지 아닌지를 알려주는 isBabyBoomer 함수를 사용하는 것을 보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public class Person { private final Date birthDate; //다른 필드 및 함수 생성자 생략 //이렇게 하면 안 된다 public boolean isBabyBoomer(){ //생성 비용이 높은 객체를 쓸데없이 생성한다. Calendar gmtCall = Calendar.getInstance(TimeZone.getTimeZone("GET")); gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0); Date boomStart = gmtCal.getTime(); gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0); Date boomEnd = gmtCal.getTime(); return birthDate.compareTo(boomStart) >= 0 && birthDate.compareTo(boomEnd) < 0; } } | cs |
정적 초기화 블록(static initializer)를 통한 개선
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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; } } | cs |
개선된 Person 클래스는 Calendar, TimeZone, Date 객체를 클래스가 초기화 될 때 한번만 만든다.
책 저자의 말대로라면 성능이 꽤 많이 좋아지는 것 같지만, 이 경우 Calendar가 객체 생성 비용이 높기 때문이라고 한다.
Person이 처음 생성 될 때, 느린 초기화를 사용하면 어떨까 했는데 책의 저자는 별로 추천하고 싶지 않은 것 같았다.
만약 필수인 Class라면 이 경우도 상관 없을 것 같지만, 생성 될수도 아닐 수도 있는 곳에서는 느린 초기화가 나을 것 같다.
JDK 1.5부터 자동 객체화(autoboxing)이 새로 생겼다고 한다.
기본 자료형(primitive type)과 그 객체 표현형을 섞어 사용할 수 있도록 해준다는 것인데 둘 간의 변환은 자동으로 이뤄진다고 한다.
그렇다면 어떤 부분에서 문제가 생길 지 알아보자.
1 2 3 4 5 6 7 8 9 | public static void main(String[] args) { Long sum = 0L; for(long i = 0; i < Integer.MAX_VALUE; i++){ sum += i; } System.out.println(sum); } | cs |
이 코드에서 단순히 sum을 Long으로 선언하고, i를 long으로 선언했다고 자동 캐스팅이 되면서 객체가 추가로 생성된다고 한다.
기본 자료형을 사용해야 할 때와 객체 표현형을 사용할 때가 존재할 때를 제외하고는 그다지 문제가 생길 것 같지 않다는게 개인 의견이다.
이 내용과 다른 관점으로 방어적 복사라는 것이 있다고 한다.
재사용이 가능하다면 새로운 객체를 만들지 말라는 이 얘기와 다르게, 새로운 객체를 만들어야 한다면 기존 객체는 재사용하지 말라는 이야기가 있는데
코드를 작성하다 보면, 상황에 맞게 써야하는게 답인 것 같다.
'Java' 카테고리의 다른 글
Effective Java #07 종료자 사용을 피하라 (0) | 2016.04.13 |
---|---|
Effective Java #06 유효기간이 지난 객체 참조는 폐기하라 (0) | 2016.04.13 |
Effective Java #04 객체 생성을 막을 때는 private 생성자를 사용하라 (0) | 2016.04.13 |
Effective Java #02 private 생성자나 enum 자료형은 싱글턴 패턴을 따르도록 설계하라. (0) | 2016.04.13 |
Effective Java #02 생성자 인자가 많을 때는 Builder 패턴 적용을 고려하라. (0) | 2016.04.12 |