본문 바로가기
기타/이펙티브 자바

[이펙티브자바] 2. 객체 생성과 파괴

by 창이2 2022. 4. 30.

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

 

정적 팩토리메서드의 장점 5가지 

 

1.1 생성자에 대한이름을 가질 수 있다.

1.2 호출마다 인스턴스를 새로 생성하지 않아도 된다.

1.3 하위타입의 객체를 반환할 수 있다.

1.4 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.

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

  1.5.1 이런 유연함은 서비스 제공자 프레임워크(service provider framework) 의 근간이 된다.

 

정적 팩토리메서드의 단점

 

1. 정적팩토리 메소드만 제공하면 하위클래스를 생성할 수 없다.

2. 정적팩토리 메소드는 API문서에 설명이 잘 드러나있지 않는다.

 

코드

 

더보기
class EffectiveJava {

    private static EffectiveJava instance = new EffectiveJava();
    private static EffectiveJava3thEdition subInstance = new EffectiveJava3thEdition();

    //기본 생성자
    private EffectiveJava(){

    }

    //생성자에 이름을 갖음, 이미 생성된 객체(캐싱)를 반환(1,2)
    public static EffectiveJava getInstance(){
        return instance;
    }

    //하위 타입의 객체를 반환 할 수 있다.(3)
    public static EffectiveJava getSubInstance(){
        return subInstance;
    }

    //매개변수에 따라 다른 클래스의 객체를 반환(4)
    public static EffectiveJava getInstanceByParam(boolean isMain){
        if(isMain) return instance;

        return subInstance;
    }

    void printInstance(){
        System.out.println("EffectiveJava");
    }

}

class EffectiveJava3thEdition extends EffectiveJava {

    public EffectiveJava3thEdition(){
      //정적 팩토리 메소드를 사용할 때 생성자가 private이면 상속을 할수 없음
      super();
    }

    @Override
    void printInstance() {
        System.out.println("EffectiveJava3thEdition");
    }
}

 

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

 

코드

 

더보기
class EffectiveJava {

    private final String name;
    private final int edition;
    private final int price;

    private EffectiveJava(Builder builder){
        this.name = builder.name;
        this.edition = builder.edition;
        this.price = builder.price;
    }

    static class Builder{
        private final String name;
        private int edition;
        private int price;

        public Builder(String name){
            this.name = name;
        }

        public Builder setEdition(int edition){
            this.edition = edition;
            return this;
        }

        public Builder setPrice(int price){
            this.price = price;
            return this;
        }

        public EffectiveJava build(){
            return new EffectiveJava(this);
        }
    }

    public int getName(String name){
        System.out.println("getName(String name)");
        return 0;
    }

    public String getName(String name, int age){
        System.out.println("getName(String name, int age)");
        return "";
    }

}

 

 

3. Private생성자나 열거타입으로 싱글톤임을 보장하라

코드

더보기
class EffectiveJava {

    /*
    * private 생성자는 instance화를 막기에 좋은 방법이다.
    * 외부에서 생성자를 호출 할 수 없기 떄문이다.
    * 또한 기본 생성자를 막으면 상속이 안되니 상속을 막는것에도 좋다.
    *
    * */
    private EffectiveJava(){
        try {
            if(INSTANCE != null){
                throw new Exception();

            }
        }catch (Exception e){

        }
    }

/*  싱글턴 생성 첫번째 방법
    생성자는 private, 인스턴스는 public static member
    다만 reflection을 사용하면 private생성자에 접근해 호출할 수 있다.
    이에 대한 해결법으로 생성자가 2번 생성될때 예외를 던지게 한다.
*/
    public static final EffectiveJava INSTANCE = new EffectiveJava();

    /*  싱글턴 생성 두번째 방법
    정적 팩터리 메소드 패턴을 이용하여 싱글톤 객체를 생성할 수 있다.
    장점으로는 상황마다 다양한 인스턴스를 반환 할 수 있고, 제네릭 싱글톤으로도 사용 가능
*/
    private static final EffectiveJava INSTANCE2 = new EffectiveJava();
    public static EffectiveJava getInstance2(){
        return INSTANCE2;
    }
}

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

1. private 생성자는 외부에서 호출 할 수 없기 떄문 instance화를 막기에 좋다

2. 기본 생성자를 막으면 상속이 안되니 상속을 막는것에도 좋다.

 

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

1. 다양한 타입의 자원(다양한 클래스)을 사용할땐 싱글톤이나 정적 유틸리티를 사용하지 말아라

2. 생성자 의존주입을 활용해서 유연하게 자원을 사용해라

 

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

1. 어떤 연산을 수행하는 객체가 있다면 매번 생성하는것 보다 정적으로 선언해서 사용하자

2. 연산에서 auto-boxing(wrapper class) 된 객체사용을 조심하자

 - 매번 객체가 새롭게 생성되기 때문에 성능에 문제가 일어난다.

 

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

1. 다쓴 객체 참조는 null로 참조를 해지하자

2. null처리 하는 일은 예외적인 경우에 하자(객체 스스로 메모리를 관리할때)

3. 상황에 따라 WeakReference를 이용해 GC가 일어날때 참조가 해제되는것을 보장하는 것을 고려해봐라

 

8. finalizer와 cleaner사용을 피하라

1. 객체를 소멸할때 위 두 메소드는 사용을 하지 말아라

2. 위 두 메소드는 반드시 실행된다는 보장이 안된다(우선순위가 낮음)

3. 위 메소드 대신 close() 메소드를 사용하자

4. 중첩클래스는 외부에 있는 객체를 참조하여 순환참조가 발생한다. 그래서 정적으로 만들어 이를 해결

 

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

1. try-finally는 두 부분 모두 예외가 일어날 수 있는데 이때 예외는 마지막으로 발생한것만 기록된다.

2. try-with-resource는 처음 발생한 예외를 보여주고 그 뒤의 예외는 hide된 상태로 기록된다.

 

책을 보면서 제 생각을 정리한 내용이라 피드백 주시면 감사하겠습니다.

댓글