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

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

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

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

  • 정적 팩토리 메서드 장점
    • 반환 객체의 특성을 고려하여 이름을 추가 가능
    • 미리 만들어서 사용하거나 캐싱하는 등 붎필요한 객체 생성 회피
    • 반환 타입의 하위 타입으로 반환 가능 (클라이언트는 인터페이스만 알도록 함)
    • 입력한 매개변수에 따라 다른 클래스 객체 반환 가능
    • 정적 팩토리 메서드를 작성은 반환 객체 클래스가 없어도 가능
  • 정적 팩토리 메서드 단점
    • 상속을 하려면 public 또는 protected 생성자가 필요, 정적 팩토리 메서드만 제공하면 하위 클래스 생성 불가능
    • 프로그래머가 찾기 어려움
  • 명명 방식
    • from : 매개변수 하나 받아 해당 타입 인스턴스 반환
    • of : 매개변수 여러개 받아 적합한 타입 인스턴스 반환
    • valueOf : fromof 보다 자세한 메서드
    • 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. finalizercleaner 사용을 피하라

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

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


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

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