2장 : 객체 생성과 파괴

2023. 4. 4. 13:00스터디/이펙티브 자바 스터디

728x90

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

## 정적 팩터리 메서드 장점

1. 이름을 가질 수 있음 : 반환될 객체에 특성을 쉽게 묘사

2. 호출될 때마다 인스턴스를 새로 생성하지 않아도 됨 : 같은 객체가 자주 요청되는 상황에 성능 향상

3. 반환 타입의 하위 타입 객체를 반환할 수 있음

4. 입력 매개변수에 따라 매번 다른 클래스 객체 반환 가능

5. 정적 팩터리 메서드를 작성하는 시점에서 반환할 객체의 클래스가 존재하지 않아도 됨

 

## 정적 팩터리 메서드 단점

1. 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없음. 생성자가 private이기 때문

2. 정적 팩터리 메서드는 프로그래머가 찾기어려움. api 문서화 필수


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

- 생성자로는 선택적 매개변수가 많으면 적절히 대응하기 어려움

- 점층적 생성자 패턴도 쓸 수 있으나, 매개변수 개수가 많아지면 클라이언트 코드를 작성하거나 읽기 어려움

- 자바빈즈 패턴도 쓸 수 있으나, 객체 하나를 만들려면 여러 개의 매서드 호출해야하고, 객체 완성 전에는 일관성이 무져 있음. 심지어 불편 클래스로 만들 수도 없음

public class Sample {
    private final int a;
    private final int b;
    private final int c;
    private final int d;

    public static class Builder {
        //필수
        private final int a;
        private final int b;
        //선택
        private int c = 0;
        private int d = 0;

        public Builder(int a, int b) {
            this.a = a;
            this.b = b;
        }

        public Builder c(int val) {
            c = val;
            return this;
        }

        public Builder d(int val) {
            d = val;
            return this;
        }

        public Sample build() {
            return new Sample(this);
        }
    }
    private Sample(Builder builder) {
        a = builder.a;
        b = builder.b;
        c = builder.c;
        d = builder.d;
    }
}

 

- 불변식을 보장하려면 빌더로부터 매개변수를 복사한 수 해당 객체 필드들도 검사

- 불변식 : 프로그램이 실행하는 동안, 혹은 정해진 기간 동안 반드시 만족해야 하는 조건

- 빌더 패턴은 계층적 설계 클래스와 함께 쓰기도 좋음

- 공변 반환 타이핑 : 하위 클래스의 메서드가 상위 클래스의 메서드가 정의한 반환 타입이 아닌, 그 하위 타입을 반환하는 기능

- 빌더 패턴은 성능에 민감한 상황에서는 문제가 될 수도 


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

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

- 싱글턴은 테스트 하기가 어려움. mock 대체가 불가하기 때문

- 정적 팩터리 메서드와 응용 가능 : 싱글톤 여부 설정 가능 및 제네릭 싱글턴 팩터리로 생성 가능

- 아주 복잡한 직렬화 상황이나 리플렉션 공격에도 제 2의 인스턴스가 생기는 일을 완벽히 막아줌

- 대부분 상황에서는 Enum이 싱글턴을 만드는 가장 좋은 방법 


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

- private 생성자를 통해 클래스의 인스턴스화를 막을 수 있음

- 어떤 환경에서도 클래스가 인스턴스화 되는 것을 막아줌


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

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

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

- 팩터리 : 호출할 때마다 특정 타입의 인스턴스를 반복해서 만들어주는 객체


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

- 생성자는 호출할 때마다 새로운 객체를 생성, 팩터리 메서드는 그렇지 않음

- 인스턴스를 클래스 초기화(정적 초기화) 과정에서 직접 생성해 캐싱해두고, 나중에 호출시 재사용하는 방식으로 진행 : private static final 붙여서 생성

- 성능 뿐 아니라 코드도 명확해짐 : 이름을 지어주어 코드의 의미가 잘 드러남

- 지연 초기화로 불필요한초기화를 없앨 수 있으나, 코드가 복잡해지고, 성능은 크게 개선 안됨

- 어댑터 : 실제 작업은 뒷단 객체에 위임하고, 자신은 제 2의 인터페이스 역할을 해주는 객체

- 오토박싱 : 기본 타입과 그에 대응하는 박싱된 기본 타입을 상호 변환해줌

- 박싱 타입은 연산시 속도가 느림

- 박싱된 기본 타입 보다는 기본타입을 사용, 의도치 않은 오토박싱이 숨어들지 않도록 주의

- 단순히 객체 생성을 피하고자 객체 풀 생성 X, 자체 객체 풀은 코드를 헷갈리게 만들고, 메모리 사용량을 늘리고 성능을 떨어뜨림

- 방어적 복사가 필요한 상황에서 객체를 재사용했을 때의 피해가 필요 없는 객체를 반복 생성했을 때의 피해보다 훨씬 크다 


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

- 다 쓴 참조를 여전히 가지고 있으면 GC가 회수를 하지 않음

- 해당 참조를 다 썼을 때 null 처리 하면 됨, 허나 null로 직접 처리하는 일은 예외적인 경우여야 함

- 자기 메모리를 직접 관리하는 클래스라면 프로그래머는 메모리 누수에 주의해야

- 캐시 또한 메모리 누수를 일으키는 주범

- 리스너나 콜백 또한 마찬가지, 콜백 등록만 하고 명확히 해지하지 않으면 계속 쌓임


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

- finalizer, cleaner 둘 다 예측 불가, 느리고, 일반적으로 불필요

- 즉시 수행된다는 보장도 없음. GC에서 다른 애플리케이션 스레드보다 우선순위가 낮음.

- 성능 문제도 심각하고, 보안 문제도 있음

- 아 그냥 쓰지 말라면 쓰지 마


아이템 9. try-finally보다는 try-with-resources를 사용하라

 

728x90