불필요한 객체 생성을 피하라

똑같은 기능의 객체를 매번 생성하기보다는 객체하나를 재사용하는 편이 낫다.
특히 불변 객체는 언제든 재사용할 수 있다.

  • 대표적인 안티패턴
    String str = new String("string");

    • 실행될 때마다 새로운 String 인스턴스를 생성한다. 이 문장이 반복문 안이나 자주 호출되는 메소드 안에 있다면 쓸데없는 낭비다.
  • 개선된 버전
    String str = "string"

    • 이 코드는 하나의 String 인스턴스를 사용한다.
    • 자바스펙에 따르면 이와 똑같은 문자열 리터럴을 사용하는 모든 코드는 같은 객체를 재사용함을 보장한다.
    • 생성자는 호출할 때마다 새로운 인스턴스를 만들지만, 아이템1에 나온 정적팩터리 메서드를 제공하는 불변클래스에서는 불필요한 객체 생성을 피할 수 있다.
      • Boolean(String) -> Boolean.valueOf(String)
  • 오토박싱

    private static long sum() {
      Long sum = 0L;
      for(long i =0; i <= Integer.MAX_VALUE; i++) {
          sum += i;
      }
      return sum;
    }
    • long인 i가 Long타입인 sum에 더해질 때마다 새로운 Long타입의 인스턴스가 만들어진다.
    • 기본형을 사용하자.

정리

  • 재사용을 추천하지만, 위험할 수 있다. 다른 곳에서 참조해서 사용하고 있을 수도 있기 떄문. 따라서 불변객체라면 재사용에 안전하지만, 그게 아니라면 위험할 수도있으니 조심해서 사용해야한다.

자원을 직접 명시하지 말고 의존객체주입을 사용하라.

  • 많은 클래스가 하나 이상의 자원에 의존

  • 인스턴스를 직접 사용하면 유연성이 떨어진다.

    public class SpellChecker {
      private static final Lexicon dictionary = new ...();
    }
  • 클래스가 여러자원 인스턴스를 지원해야한다.

    • 인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주는 방식.

    • 팩터리 메서드 패턴을 구현하는 방식

      public class SpellChecker {
      private final Lexicon dictionary;
      
      public SpellChecker(Lexicon dictionary) {
        this.dictionary = Objects.requireNonNull(dictionary);
      }
      }

정리

하드코딩하지말고 유연하게 코딩할 것. 스프링프레임워크가 좋은 이유 또한 DI를 지원하기 때문이다.

인스턴스화를 막으려거든 private 생성자를 사용하라.

Util 클래스를 만들 때 보통 정적 메서드와 정적 필드만을 담은 클래스를 작성한다. 이런 클래스는 인스턴스로 만드려고 설계한 클래스가 아니다. 그렇다고 생성자를 안만들면 컴파일러가 알아서 public 생성자를 만들어준다.

따라서 명시적으로 private 생성자를 만들어서 클래스의 인스턴스화를 막자.

public class UtilClass {
    private UtilClass() {
        throw new AssertionError();
    }
    ...
}
  • 클래스 내부에서라도 생성자를 호출하지 않기 위해 private 생성자 안에서 AssertionError를 던진다.

private 생성자나 열거 타입으로 싱글턴임을 보증하라.

싱글턴?

  • 인스턴스를 오직 하나만 생성할 수 있는 클래스.
  • 무상태 객체나, 설계상 유일해야 하는 시스템컴포넌트 등.

    만드는 방식

  1. public static final 필드 방식의 싱글턴

    public class Singleton {
     public static final Singleton INSTANCE = new Singleton();
     private Singleton() { ... }
    }
    • private 생성자는 Singleton.INSTANCE를 초기화할 때 딱 한 번만 호출된다.
    • private 생성자이므로 인스턴스가 전체 시스템에서 하나 뿐임을 보장한다. ( 예외로는 리플렉션을 이용한 생성자 호출)
  2. 정적 팩터리 방식의 싱글턴

    public class Singleton {
     **private** static final Singleton INSTANCE = new Singleton();
     private Singleton() { ... }
     public static Singleton getInstance() { return INSTANCE; }
    }
    • Singleton.getInstance()는 항상 같은 객체의 참조를 반환하므로 또 다른 인스턴스는 만들어지지않는다.
  1. 원소가 하나인 열거타입을 선언하는 것.
    public enum Singleton {
     INSTANCE;
    }
    • 앞선 두가지 방법 모두 리플렉션을 공격에 취약한데, 이 방식은 리플렉션 공격에도 안전하다.
    • 대부분 상황에서는 이 방식이 가장 좋다. 단! 만드려는 싱글턴이 Enum외의 클래스를 상속해야한다면 이 방식은 쓸 수 없다. ( 다른 인터페이스를 구현하도록 선언은 가능 )

+ Recent posts