기록이 힘이다.

[이팩티브 자바]아이템 8. 완벽 공략 Finalizer/ AutoCloseable 본문

JAVA

[이팩티브 자바]아이템 8. 완벽 공략 Finalizer/ AutoCloseable

dev22 2023. 3. 7. 15:44
728x90

Finalizer 공격

import java.math. BigDecimal;

public class Account {

	private String accountId;
    
    public Account(String accountId){
    	this.accountId = accountId;
        
        if(accountId.equals("푸틴")){
        	throw new IllegalArgumentException("푸틴은 계정을 막습니다."); 
            //finalizer를 통해 보낼 수 있다
         }
      }
      
      public void transfer(BigDecimal amount, String to){
      	System.out.printf("transfer %f from %s to %s\n", amount, accountId, to);
       }
   }

 

public class BrokenAccount extends Account{
	
    public BrokenAccount(String accountId){
    	super(accountId);
    }
    
    @Override
    protected void finalize() throws Throwable{
 		this.transfer(BigDecimal.valueOf(100), "keesun");
 }
}
  • 테스트 코드
  • 방어하는 방법
    • final 클래스로 만들거나
    • finalizer() 메소드를 오버라이딩 한 다음 final을 붙여서 하위 클래스에서 오버라이딩 할 수 없도록 막는다.
class AccountTest{

	@Test
    void 일반_계정(){
    	Account account = new Account("keesun");
    	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);
    }
    
 }
import java.math. BigDecimal;

public final class Account { //상속을 할 필요가 없으면 final로 막을 수 있다.

	private String accountId;
    
    public Account(String accountId){
    	this.accountId = accountId;
        
        if(accountId.equals("푸틴")){
        	throw new IllegalArgumentException("푸틴은 계정을 막습니다."); 
            //finalizer를 통해 보낼 수 있다
         }
      }
      
      public void transfer(BigDecimal amount, String to){
      	System.out.printf("transfer %f from %s to %s\n", amount, accountId, to);
       }
   }
import java.math. BigDecimal;

public class Account { //상속을 해야 한다면 메소드에 final을 붙인다.

	private String accountId;
    
    public Account(String accountId){
    	this.accountId = accountId;
        
        if(accountId.equals("푸틴")){
        	throw new IllegalArgumentException("푸틴은 계정을 막습니다."); 
            //finalizer를 통해 보낼 수 있다
         }
      }
      
      public void transfer(BigDecimal amount, String to){
      	System.out.printf("transfer %f from %s to %s\n", amount, accountId, to);
       }
       
      @Override
      protected final void finalize() throws Throwable{}
   }

 

AutoClosable

@author josh Bloch // 교재의 지은이이다.
@since1.7

public interface AutoCloseable{
	
    void close() throws Exception;

}

인터페이스를 쓰면 main에서 finaly 블럭을 안써도 된다. 

import java.io.*;

public class AutoClosableIsGood implements AutoCloseable{

	private BufferedReader reader;
    
    public AutoClosableIsGood(String path){
    	try{
        	this.reader = new BufferedReader(new FileReader(path));
         }catch(FileNotFoundException e){
         	throw new IllegalArgumentException(path);
         }
    }
   	
    @Override
    public void close() throws IOException {
    	reader.close();
    }
  }
import java.io.IOException;

public class App{
	
    public static void main(String[] args){
    	try(AutoCloseableIsGood good = new AutoCloseableIsGood("")){
        	//TODO 자원 반납 처리가 됨.
        }catch(IOException e){
        	e.printStackTrace();
        }
    }
}
import java.io.*;

public class AutoClosableIsGood implements AutoCloseable{

	private BufferedReader reader;
    
    public AutoClosableIsGood(String path){
    	try{
        	this.reader = new BufferedReader(new FileReader(path));
         }catch(FileNotFoundException e){
         	throw new IllegalArgumentException(path);
         }
    }
   	
    @Override
    public void close() { //던지지 않고 잡으면 굳이 클라이언트 코드에서 잡을 필요가 없다.
    	try{
        reader.close();
    	}catch(IOException e){
        	e.printStackTrace();
        }
    }
  }
import java.io.*;

public class AutoClosableIsGood implements AutoCloseable{

	private BufferedReader reader;
    
    public AutoClosableIsGood(String path){
    	try{
        	this.reader = new BufferedReader(new FileReader(path));
         }catch(FileNotFoundException e){
         	throw new IllegalArgumentException(path);
         }
    }
   	
    @Override
    public void close() { //던지지 않고 잡으면 굳이 클라이언트 코드에서 잡을 필요가 없다.
    	try{
       		 reader.close();
    	}catch(IOException e){
        	throw new RuntimeException(e);
        }
    }
  }

읽고 쓰거나 하면 Closeable을 쓰는 게 좋은 선택이 될 수도 있다. -->더 많은 규약이 있다.