이펙티브 자바 Chapter2. 객체 생성과 파괴

October 10, 2021 - 3 minute read -
book effective-java

이펙티브 자바 2장에서는 객체 생성, 불필요한 생성을 피하는 방법,
객체 파괴, 파괴전 필요한 작업들에 대해서 소개하고 있다.

아이템1. 생성자 대신 정적 패터리 메서드를 고려하라

  • 정적 팩토리 메서드 장점
    • 반환 객체의 특성을 고려하여 이름을 추가 가능
    • 미리 만들어서 사용하거나 캐싱하는 등 붎필요한 객체 생성 회피
    • 반환 타입의 하위 타입으로 반환 가능 (클라이언트는 인터페이스만 알도록 함)
    • 입력한 매개변수에 따라 다른 클래스 객체 반환 가능
    • 정적 팩토리 메서드를 작성은 반환 객체 클래스가 없어도 가능
  • 정적 팩토리 메서드 단점
    • 상속을 하려면 public 또는 protected 생성자가 필요, 정적 팩토리 메서드만 제공하면 하위 클래스 생성 불가능
    • 프로그래머가 찾기 어려움
  • 명명 방식
    • from : 매개변수 하나 받아 해당 타입 인스턴스 반환
    • of : 매개변수 여러개 받아 적합한 타입 인스턴스 반환
    • valueOf : from과 of 보다 자세한 메서드
    • instance or getInstance: 매개변수로 인스턴스를 반환하지만 동일한 인스턴스인지는 보장 안함
    • create or newInstance : instance, getInstance 동일, 하지만 매번 새로운 인스턴스 반환
    • get{Type} : getInstance와 동일하지만, 생성 클래스가 아닌 다른 클래스에서 팩터리 메서드를 정의
    • new{Type} : newInstance와 동일하지만, 생성 클래스가 아닌 다른 클래스에서 팩터리 메서드를 정의
    • {type} : get{Type}, new{Type} 간결 버전


아이템2. 생성자에 매개변수가 밚다면 빌더를 고려하라

  • 점층적 생성자 패턴 단점
    • 매개변 수가 많아지면 작성, 읽기가 어려움
    • 매개변수를 잘못 넣을 수도 있음
  • 자바빈즈(setter) 패턴 단점
    • 메서드 호출이 많아짐
    • 불변으로 만들 수도 없음
    • 완전한 객체 되기 전 일관성이 무너짐
    • 스레드 안전하지 않음
  • 빌더 패턴 장점
    • 불변 가능
    • 쓰거나 읽기 쉬움
    • 선택적 매개변수
    • 계층적으로 설계된 클래스와 함께 쓰기 좋음(추상 빌더)


아이템3. private 생성자나 열거 타입으로 싱글텀임을 보증하라

  • public static final 방식
    • private 생성자로 하나뿐임을 보장
    • 코드가 간결
    • 리플렉션 공격에 대비하기 위해 private 생성자에 예외를 던져야 함
  • public static Object getInstance() 방식
    • 언제든 싱글턴이 아니게 변경 가능
    • 제네릭 팩터리 로 만들기 가능
    • 메서드 참조를 supplier로 사용할 수 있음
    • 위 장점들이 필요없다면 public static 필드 방식 선호
  • 열거(enum) 타입 방식
    • public static 필드 방식보다 더 간결하고 쉽게 직렬화 가능
    • 리플렉션 공격에도 문제 없음
    • 싱글턴을 이용하려면 원소가 하나뿐인 열거타입이 가장 적합


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

정적 멤버만 있는 유틸리티 클래스는 인스턴스화가 필요 없음
→ private 생성자를 추가하고 예외를 던져 클래스 내부에서도 인스턴스화 방지


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

클래스 내부에 있는 자원이 동작에 영향을 준다면 싱글턴, 유틸리티는 안됨
→ 생성자에 자원을 넘겨주어 (의존 객체 주입 패턴) 사용해야 함
→ 테스트 용이성, 유연성, 재사용성 개선


아이템6. 불필요한 객체 생성을 피하라

  • String 생성자보다 문자열 리터럴 (new String("test") → "test")
  • 생성자 보다는 정적 팩터리 메서드 사용
  • 비싼 객체일 경우 캐싱해서 재사용
  • 의도치 않은 오토 박싱이 되지 않도록 주의(박싱 타입보다는 기본 타입 사용)
  • 무거운 객체가 아니라면 직접 만든 객체 풀(pool)은 코드가 복잡해지고 메모리 사용량도 늘어남(가비지 컬렉터가 훨씬 빠름)


아이템7. 다 쓴 객체 참조를 해제하라

  • 다 쓴 변수는 유효 범위 밖으로 밀어내어 참조 해제
  • 메모리를 직접관리하는 Stack 클래스에서 참조를 다 썼다면 null로 처리
    • NullPointerException 이 발생하겠지만 오류는 가능한 빨리 발견해야 함
    • 메모리 누수에 주의
  • 키를 참조하는 동안만 객체가 살아있는 캐시가 필요하다면 WeakHashMap을 이용
  • 리스너(listener), 콜백(callback)은 계속 쌓이지 않도록 약한 참조(weak reference) 로 저장

아이템8. finalizer 와 cleaner 사용을 피하라

try-with-resources 와 try-finally (AutoCloseable 구현) 사용하여 해결
finalizer 와 cleaner는 안전망 역할 또는 중요하지 않은 네이티브 자원 회수용으로만 사용

  • finalizer 와 cleaner 문제점
    • 즉각 수행된다는 보장이 없음
    • 동작중 발생된 예외를 무시, 작업이 남아있어도 즉시 종료
    • 성능이 떨어짐
    • 공격에 노출되어 보안 문제 (직렬화 도중 예외로 하위 클래스의 finalizer 수행 가능)


item9. try-finally보다는 try-with-resources를 사용하라

  • close 메서드 호출 놓치기 쉬움
  • 코드가 간결하고 명확
  • 예외 처리 유용
  • 쉬운 자원 회수