tonykang22 / study

0 stars 0 forks source link

[Effective Java] 아이템 8. finalizer와 cleaner 사용을 피하라. #48

Open tonykang22 opened 2 years ago

tonykang22 commented 2 years ago

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

핵심 정리


Cleaner 사용법

예시 코드

public class Room implements AutoCloseable {
    private static final Cleaner cleaner = Cleaner.create();

    // 청소가 필요한 자원. 절대 Room을 참조해서는 안 된다!
    private static class State implements Runnable {
        int numJunkPiles; // Number of junk piles in this room

        State(int numJunkPiles) {
            this.numJunkPiles = numJunkPiles;
        }

        // close 메서드나 cleaner가 호출한다.
        @Override public void run() {
            System.out.println("Cleaning room");
            numJunkPiles = 0;
        }
    }

    // 방의 상태. cleanable과 공유한다.
    private final State state;

    // cleanable 객체. 수거 대상이 되면 방을 청소한다.
    private final Cleaner.Cleanable cleanable;

    public Room(int numJunkPiles) {
        state = new State(numJunkPiles);
        cleanable = cleaner.register(this, state);
    }

    @Override public void close() {
        cleanable.clean();
    }
}
public class Adult {
    public static void main(String[] args) {
        try (Room myRoom = new Room(7)) {
            System.out.println("안녕~");
        }
    }
}
public class Teenager {

    public static void main(String[] args) {
        new Room(99);
        System.out.println("Peace out");

        // gc를 수행하면 자원을 반납하겠으나, 아래와 같이 gc를 강제로 호출 하는 방식에 의존해서는 절대 안된다.
        System.gc();
    }
}


완벽 공략


예시 코드 (정적이 아닌 중첩 클래스는 자동으로 바깥 객체의 참조를 갖는다.)

InnerClass에서 OuterClass를 참조할 때 - OuterClass.this.로 참조할 수 있다.

public class OuterClass {

    private void hi() {
    }

    class InnerClass {
        public void hello() {
            OuterClass.this.hi();
        }
    }

    public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();
        InnerClass innerClass = outerClass.new InnerClass();

        System.out.println(innerClass);

        outerClass.printFiled();
    }

    private void printFiled() {
        Field[] declaredFields = InnerClass.class.getDeclaredFields();
        for(Field field : declaredFields) {
            System.out.println("field type:" + field.getType());
            System.out.println("field name:" + field.getName());
        }
    }
}


예시 코드 (람다 역시 바깥 객체의 참조를 갖기 쉽다.)

public class LambdaExample {

    private int value = 10;

    // LambdaExample을 정리하기 위한 메소드를 이런 방식
    // (Capturing하는 방식. 즉, 바깥 객체의 참조를 갖도록 하는 방식, `value`)
    // 으로 두게 되면 바깥 객체에 대한 참조가 생기기 때문에 객체가 부활하게 된다.
    private Runnable instanceLambda = () -> {
        System.out.println(value);
    };

    public static void main(String[] args) {
        LambdaExample example = new LambdaExample();
        Field[] declaredFields = example.instanceLambda.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            System.out.println("field type: " + field.getType());
            System.out.println("field name: " + field.getName());
        }
    }
}


완벽 공략. Finalizer 공격

만들다 만 객체를 finalize 메소드에서 사용하는 방법

예시 코드

Test 도둑_계정() 코드 참조, 생성자를 호출하면, 객체를 만들다가 예외 처리로 넘어가게 되고 결국 gc가 수행될 때, transfer이 수행되게 된다.

public class Account {

    private String accountId;

    public Account(String accountId) {
        this.accountId = accountId;

        if (accountId.equals("도둑")) {
            throw new IllegalArgumentException("도둑은 계정을 막습니다.");
        }
    }

    public void transfer(BigDecimal amount, String to) {
        System.out.printf("transfer %f from %s to %s\n", amount, accountId, to);
    }

}
class AccountTest {

    @Test
    void 일반_계정() {
        Account account = new Account("tony");
        account.transfer(BigDecimal.valueOf(10.4),"hello");
    }

    @Test
    void 도둑_계정() throws InterruptedException {
        Account account = null;
        try {
            account = new BrokenAccount("도둑");
        } catch (Exception exception) {
            System.out.println("이러면???");
        }

        System.gc();
        Thread.sleep(3000L);
    }
}


-> 참고 : 테스트 실행 시 출력

image


완벽 공략. AutoClosable

try-with-resource를 지원하는 인터페이스