쓸데없이 객체를 만들지 말자!

이펙티브 자바!

불필요한 객체는 만들지 말라.
동일한 객체는 필요할 때마다 만드는 것보다 재사용하는 편이 낫다. 변경 불가능한 객체는 언제나 재사용할 수 있다.
우리는 절대로 피해야 할 극단적 예를 들어보겠다.

String s = new String("hello string");

위의 코드는 쓸데 없는 짓이다. 만약 위의 문장이 loop나 자주 호출되는 메서드 안에 있으면 쓸데 없는 객체가 수십만개 수백만게 만들어 질것이다.

String s = "hello string";

위와 같이 하면 같은 가상머신에서 실행되는 코드는 동일한 String을 재 사용한다.
String을 객체로 생성해서 쓸데 없는 짓을 하지 말자!

다음 코드를 보자

class Person{
  private final Date birthDate;

  public Person(Date birthDate){
    this.birthDate = birthDate;
  }

  public boolean isBabyBoomer(){
    Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
    calendar.set(1946, Calendar.JANUARY,1,0,0,0);
    Date boomStart = calendar.getTime();
    calendar.set(1965, Calendar.JANUARY,1,0,0,0);
    Date boomEnd = calendar.getTime();
    return birthDate.compareTo(boomStart) >= 0 && birthDate.compareTo(boomEnd) < 0;
  }
}

해당 코드는 1946년과 1964년 사이에 태어난 사람이면 참을 반환 한다.
위에 보면 메서드가 호출 될 때마다 Calendar 한개 TimeZone한개 Date는 두개나 쓸데 없이 만들어 낸다.
효율적인 코드는 정적 초기화 블록을 통해 개선하는 것이 좋다.

class Person{
  private final Date birthDate;
  public static final Date BOOM_START;
  public static final Date BOOM_END;

  public Person(Date birthDate){
    this.birthDate = birthDate;
  }

  static{
    Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
    calendar.set(1946, Calendar.JANUARY,1,0,0,0);
    BOOM_START = calendar.getTime();
    calendar.set(1965, Calendar.JANUARY,1,0,0,0);
    BOOM_END = calendar.getTime();
  }

  public boolean isBabyBoomer(){
    return birthDate.compareTo(BOOM_START) >= 0 && birthDate.compareTo(BOOM_END) < 0;
  }
}

이렇게 개선된 클래스는 Person클래스가 생성될때 한 번만 만든다.

JDK 1.5부터는 쓸데 없이 객체를 마들 새로운 방법이 더 생겼다. 오토박싱이라는 것인데 자바의 기본형과 그 객체 표현형을 섞어 사용할 수 있도록 해준다. 둘 간의 변환은 자동으로 이뤄진다.
코드를 보자

Long startTime = System.currentTimeMillis();
Long sum = 0L;
for (long i = 0; i < Integer.MAX_VALUE; i++) {
  sum += i;
}
System.out.println(sum);
System.out.println(System.currentTimeMillis() - startTime);

위와 같이 Int의 모든 양의 정수 합을 계산하는 코드이다.
시간이 얼마나 걸릴까? 생각보다 많은 시간이 걸린다.
Long으로 선언되어 쓸데 없는 객체들이 만들어 진다. 자료형을 Long에서 long으로 바꾸자
상당한 시간이 줄었다.
객체 표현형 대신 기본 자료형을 사용하고 생각지도 못한 자동 객체화가 발생하지 않도록 유의하라는 것이다.
그러나 객체를 만드는 비용이 높으니 무조건 피하라는 것은 아니다. 생성자 안에서 하는 일이 작고 명확하면 객체 생성과 반환은 신속하게 이루워진다.
잘 판단해서 쓰라는거 같다.

쓸데 없이 객체를 생성하지 말자!

private 생성자를 사용하자

제목이 조금 이상하긴한데.. 모든 클래스에 해당되는 말은 당연히 아니다
우리가 흔히 쓰는 유틸 클래스 상태가 없는 클래스들을 말하는거다.
해당 클래스들은 모두 static 메소드를 이루어져 있다.
객체를 인스턴스할 필요가 없는 클래스들 이다.
흔히 유틸 클래스를 만들 때 인스턴스화를 막기 위해 클래스의 abstract 선언하곤 한다.
abstract 선언해도 인스턴스를 생성 할 수 있다.
예를 들어 보자


@Test public void test() { ReflectionClass reflectionClass = new ReflectionClass() { }; reflectionClass.printName("wonwoo"); } abstract class ReflectionClass { public ReflectionClass() { System.out.println("init"); } public static void printName(String name){ System.out.println(name); } }

우리는 printName를 인스턴스화 해서 호출하였다.
물론 이 방법을 써도 컬리브레이스가 자동으로 생성되기 때문에 (툴에서) 알 수는 있다.
행여나 실수를 방지 하기 위해 가능하면 생성자를 private로 하자.
그러면 컴파일시 에러가 발생하므로 더 명확하다.
하지만 private를 생성자로 선언해도 접근가능하다.
코드를 보자.

Class classes = ReflectionClass.class;
Constructor constructor = classes.getDeclaredConstructor();
constructor.setAccessible(true);
Object o = constructor.newInstance();
System.out.println(o);

class ReflectionClass {
    private ReflectionClass() {
        System.out.println("init");
    }
}

우리는 다음과 같이 init을 출력하는걸 볼 수 있다.
그래서 우리는 다음과 같이 할 수 있다.

class ReflectionClass {
    //no instance
    private ReflectionClass() {
        throw new AssertionError();
    }
}

물론 컴파일시에는 알 수는 없지만 런타임시에라도 에러를 발생 시키자.
그리고 따로 주석까지 남기는건 센스

어제 놀다가 늦게 들어갔는데 책이 와있었다.
피곤한 나머지 몇장 못보고 잤지만 대충 이런 내용인듯 싶다.
혹시나 다르다면 나중에 고쳐야지