이펙티브 자바 2장: 객체 생성과 파괴

Adam·2024년 7월 23일
0

이펙티브 자바

목록 보기
1/10
post-thumbnail

생성자 대신 정적 팩터리 메서드를 고려하라

정적 팩터리 메서드가 생성자보다 좋은 장점

  1. 이름을 가질 수 있다: 이름을 잘 지으면 반환될 객체의 특성을 쉽게 묘사할 수 있다
  2. 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다
  3. 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다: 유연성을 제공
  4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다
  5. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다

정적 팩터리 메서드의 단점

  1. 상속을 하려면 public이나 protected 생성자가 필요하니 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다
  2. 프로그래머가 찾기 어렵다

생성자에 매개변수가 많다면 빌더를 고려하라

매개변수 개수가 많아지면 클라이언트 코드를 작성하거나 읽기 어렵다

자바빈즈패턴: 객체 생성 후 세터 메서드를 호출해 원하는 매개변수의 값을 설정하는 방식

자바빈즈패턴 단점

  1. 객체 하나 만들기 위해 메서드를 여러개 호출해야 한다
  2. 객체가 완전히 생성되기 전까지 일관성이 무너진 상태에 놓인다
  3. 클래스를 불변으로 만들 수 없음

빌더 패턴: 필수 매개변수만으로 생성자를 호출해 빌더 객체를 얻은 후 빌더 객체가 제공하는 일종의 세터 메서드들로 원하는 선택 매개변수들을 설정한다. 마지막으로 매개변수가 없는 build 메서드를 호출해 필요한 객체를 얻는다

빌더패턴 장점

  1. 가변인수 매개변수를 여러 개 사용할 수 있다
  2. 메서드를 여러 번 호출하도록 하고 각 호출 때 넘겨진 매개변수들을 하나의 필드로 모을 수 있다

빌더패턴 단점

  1. 빌더 생성 비용이 크지는 않지만 성능에 민감한 상황에선 문제가 될 수 있음
  2. 생성자 패턴보다는 코드가 장황

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

싱글턴: 인스턴스를 오직 하나만 생성할 수 있는 클래스

// public static final 필드 방식의 싱글턴
public class Elvis{
	public static final Elvis INSTANCE = new Elvis();
	private Elvis(){...}
	public void leaveTheBuilding(){...}
}

// 정적 팩터리 방식의 싱글턴
public class Elvis{
	private static final Elvis INSTANCE = new Elvis();
	private Elvis(){...}
	public static Evlis getInstance(){return INSTANCE;}
	public void leaveTheBuilding(){...}
}

// 열거 타입
public enum Elvis{
	INSTANCE;
	public void leaveTheBuilding(){...}
}

싱글턴 생성법: 생성자는 private으로 감춰두고 유일한 인스턴스에 접근할 수 있는 수단으로 public static 멤버를 마련

public static final 필드 방식의 장점

  1. 해당 클래스가 싱글턴임이 명백히 드러남
  2. 간결함

정적 팩터리 방식의 장점

  1. API를 바꾸지 않고도 싱글턴이 아니게 변경할 수 있다
  2. 정적 팩터리를 제네릭 싱글턴 팩토리로 만들 수 있다
  3. 정적 팩터리의 메서드 참조를 공급자로 사용할 수 있다

열거타입

대부분 상황에서는 원소가 하나뿐인 열거 타입이 싱글턴을 만드는 가장 좋은 방법

만들려는 싱글턴이 Enum외의 클래스를 상속해야 한다면 해당 방법은 사용 불가

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

컴파일러가 기본 생성자를 만드는 경우는 오직 명시된 생성자가 없을 때뿐이니 private 생성자를 추가하면 클래스의 인스턴스화를 막을 수 있다

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

사용하는 자원에 따라 동작이 달라지는 클래스에는 정적 유틸리티 클래스나 싱글턴 방식이 적합하지 않다

객체주입: 인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주는 방식

의존 객체 주입이 유연성과 테스트 용이성을 개선해주긴 하지만, 의존성이 큰 프로젝트에서는 코드를 어지럽게 만들기도 한다

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

자주 사용되는 객체는 새로 생성하는 것이 아니라 하나의 인스턴스를 사용해 사용하는게 성능에 좋다

오토박싱: 기본 타입과 그에 대응하는 박싱된 기본타입의 구분을 흐려주지만, 완전히 없애지는 않음

오토박싱으로 불필요한 객체들이 생성됨

다 쓴 객체 참조를 해제하라

자바는 가비지 컬렉터가 있어서 다 쓴 객체를 알아서 회수해간다

메모리 누수가 있는 스택을 사용하는 프로그램을 오래 실행하면 OutOfMemoryError을 일으켜 프로그램이 종료될 수 있다

  • 다 쓴 참조를 null 처리해 가비지 컬렉터가 회수 할 수 있게 해주는 것이 한 방법
  • 자기 메모리를 직접 관리하는 클래스라면 프로그래머는 항시 메모리 누수에 주의해야 한다

캐시 역시 메모리 누수를 일으키는 주범

  • WeakHashMap을 사용해 캐시를 만들어 다 쓴 엔트리는 자동으로 제거되게 하는게 좋다

콜백을 등록하고 명확히 해지 않으면 콜백이 계속 쌓여 메모리 누수가 발생할 수 있음

  • 콜백을 약한 참조로 저장해 가비지 컬렉터가 수거하게 해야 한다

finalizer와 cleaner 사용을 피하라

자바는 finalizer, cleaner 두 가지 객체 소멸자를 제공

finalizer: 예측할 수 없고 상황에 따라 위험할 수 있어 일반적으로 불필요

cleaner: finalizer보다는 덜 위험하지만 예측할 수 없고 느리고 일반적으로 불필요

finalizer과 cleaner는 즉시 수행된다는 보장이 없어 제때 실행되어야 하는 작업을 할 수 없다

→ 지연을 발생시켜 성능저하 및 에러로 이어질 수 있다

finalizer과 cleaner은 수행 여부도 보장하지 않는다

→ 상태를 영구적으로 수정하는 작업에서는 절대 finalizer나 cleaner에 의존해서는 안된다

finalizer을 사용한 클래스는 finalizer 공격에 노출되어 보안에 문제가 있을 수 있다

대안: AutoCloseable을 구현하고 close 메서드를 호출

finalizer과 cleaner의 역할

  1. close메서드를 호출하지 않는 것에 대비한 안전망 역할
  2. 네이티브 피어와 연결된 객체

네이티브 피어: 자바 객체가 네이티브 메서드를 통해 기능을 위임한 네이티브 객체(자바 객체가 아니여서 가비지 컬렉터는 그 존재를 알지 못한다)

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

전통적으로 자원이 제대로 닫힘을 보장하는 수단으로 try-finally가 쓰임

예외는 try와 finally 블록 모두에서 발생할 수 있기 때문에 close메서드가 실패할 가능성이 있음

try-with-resources는 짧고 예외 정보도 훨씬 유용하다

try-with-resources로는 정확하게 자원을 회수할 수 있다.

profile
Keep going하는 개발자

0개의 댓글