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

bo-yoon·2021년 7월 17일
0

effectivejava

목록 보기
1/5

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

팩토리 메서드 설명

  • 정적 팩토리 메서드 : 그 클래스의 인스턴스를 반환하는 단순한 정적 메서드

public static Boolean valueOf(boolean b) {
	return b ? Boolean.TRUE : Boolean.FALSE;
}

장점 :
1 ) 이름을 가질 수 있음
2 ) 호출될때마다 인스턴스를 새로 생성할 필요 없음
3 ) 반환 타입의 하위 타입객체를 반환할 수 있는 능력이 있음
4 ) 입력 매개변수에 다라 매번 다른 클래승의 객체 반환 가능
5 ) 정적 팩토리 메서드를 작성하는 시점에서는 반환할 객체의 클래스가 존재 하지 않아도 됨

단점 :
1) 하위클래스를 만들 수 없다
2) 프로그래머가 찾기 어렵다.

예시) jdbc
: 클래스 안에서 다른 객체를 생성하는 로직이 있다.

	// 드라이버 가져와서 디비 생성, 연결하는 아이
	public static Connection getConnection() {
		try {
			//어떤 DB 를 사용할 지 선택, 연결하려는 드라이버 명
			Class.forName("org.h2.Driver"); 
			
			//내가 사용하고자 하는 DB(URL)에 ID/PW 로 접근
			return DriverManager.getConnection("jdbc:h2:tcp://localhost/~/testsq",
									"sa","");
		} catch ( Exception e) {
			e.printStackTrace();
		}
		return null;
	}


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

  • 빌더는 점층적 생성자보다 클라이언트 코드를 읽고 쓰기가 훨씬 간결하고 자바빈즈보다 안전하다.
  • 매개 변수 개수가 많아지면 클라이언트 코드를 작성하거나 읽기 어렵다.
  • getter, setter 범벅의 소스코드를 만드는 대신에 builder로 한번에 할당하자.
public class Applepie {
	
    private final int flour;
    public final int apple;
    public final int suger;
    public final int powder;
    
    public static class Builder {
       // 필수 매개변수
       private final int flour;
       public final int apple;
    
       // 선택 매개변수 : 기본값 초기화
       public final int suger = 1;
       public final int powder = 3;
    	
       // 필수 매개변수는 생성자를 사용해 초기화
       Builder( int flour, int apple ) {
         this.flour = 5;
         this.apple = 3
       }
    	
       // 선택 매개변수는 setter 메서드를 사용함
       
       Builder amount0fsuger(int suger) {
       		this.suger =suger;
            return this;
       }
       
       Builder amount0fpowder( int powder) {
       		this.powder;
            return this;
       
      	}
    }


}


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

  • 싱글턴 : 인스턴스를 오직하나만 생성할수 있는 클래스
  • 클래스를 싱글턴으로 만들면 이를 사용하는 클라이언트를 테스트하기 어려워진다. mock(가짜) 구현으로 대체할 수 없기 때문이다.

방1) public static final 사용, 생성자는 private


public class Elvis {

    // 딱 한번만 인스턴스 호출하였다.
	public static final Elvis INSTANCE = new Elvis();
   	private Elvis() { }
    
    public void  mynameis() { }
}
  • public, protected 생성자가 없기 때문에 직관적으로 인스턴스는 한번만 호출하는 명확하게 친구이지만
    AccessibleObject.setAccessible 을 사용해 private 생성자를 호출하는 공격을 할 수 있다. 이런 공격은 두번째 객체를 생성하려고 할때 예외를 던지게 한다.

방2) private static final, 생성자는 private, public 으로 인스턴스 호출 가능


public class Elvis {
     // 변수가 private 라 외부에서 호출 불가
	private static final Elvis INSTANCE= new Elvis();
   	private Elvis() { ..}
    
    // public으로 된 인스턴스 호출 메서드가 생김
    public static Elvis getInstance() { return INSTANCE; }
    
    public void  mynameis() { }
}

  • api 를 건들이지 않고 싱글턴 아니게 변경 가능하다. 팩토리 메서드를 사용하기 때문에 하위 객체에서 여러개의 객체 선언이 가능하다.
  • 객체에 제너릭 사용가능

방3) enum 으로 원소가 하나뿐인 열거 타입의 싱글턴


public enum Elvis {
	INSTANCE; 
    public void  mynameis() { .. }
}

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



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

  • 명시적 생성자가 private라서 클래스 밖에서 접근 불가능
  • 자바에서 아무 생성자가 없으면 public 생성자가 생성되므로 외부에서 접근이 가능하기 때문에 private 생성자를 생성하면 디폴트 생성자가 자동으로 생성안되기 때문이다.
	
    private UtilityClass() {
    	throw new AssertionError();
        
   }
	


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

  • 의존성 주입
    : 인스턴스를 생성할때 생성자에게 필요한 자원을 넘겨주는 것
  • 사용하는 자원에 따라 동작이 달라지는 클래스에는 싱글턴이 어울리지 않는다. 멀티쓰래드를 사용하는 경우.
public class daliycheck() {
	
    private final Todolist todo;
    
    public daliycheck( Todolist todo) {
    	this.todo = Objects.requireNonNull(todo);
    }

}
  • 클래스가 내부적으로 하나 이상의 자원에 의존하고, 그 자원이 클래스 동적에 영향을 준다면 싱글턴과 정적 유틸리티 클래스는 사용하지 않은 것이 좋다. 대신 필요한 자원을 넘겨주어 의존 객체 주입을 활용하자. 클래스가 생성될 때 자동으로 생성되는 것이다. 이 방법이 클래스의 유연성, 재사용성, 테스트 용이성에 좋다.


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

String 클래스에 있는 matchs는 정규 표현식을 위반하는 문자열을 걸러낼수 있는 좋은 객체이지만,
한번 쓰고 버려지는 가비지 콜랙터의 주 대상이기도 한다. 아래와 같이 미리 캐싱해두고 사용하는 방법을 사용하면 성능 개선에 도움이 된다. 함수 매번 호출하는 것보다 미리 가져오는게 성능에 좋음.

public class RomanNumerals{
	private static final Pattern ROMAN = 
		Pattern.compile("^(?=[MDCLXVI])M*D?C{0,4}L?X{0,4}V?I{0,4}$");
        
	public static boolean isRomanNumeral(String s){
		return ROMAN.matcher(s).matches();
	}
}


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

  • 메모리 누수는 겉으로 잘 들어내지 않아 발견하기 어렵고 방치되는 경우가 많다. 다슨 참조를 해제하는 것은 중요하다. 간단한 해결 방법으로는 자원을 다 사용하고 null 처리를 다음과 같이 해주면 된다.
public Object pop() {
	if(size == 0) {
        throw new EmptyStackException() ;
     }
    Object res = elements[--size]'
    element[size] = null;
    return res;
}


8. finalizer와 cleaner 사용을 피해라

  • 자바에서 객체 소멸자중 finalizer는 예측할 수 없고, 상황에 따라 불필요할수 있어 일반적으로 사용하지 않는 것이 좋다. 대신에 사용하는 cleaner도 마찬가지로 위험하다.


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

  • try-finally는 많이 사용하고 익숙하지만 try-with-resources 는 무엇인가 싶다.
    try-with-resources 는 일반적으로 복수의 자원을 처리하는 로직이다.

  • why?
    : try-finally 를 사용하면 예외가 try 블록, finally 블록 두개 다 발생가능한다. 이런 경우 추적이 블가능하다.

static void copy(String src, String dst) throws IOException{
	try(InputStream in = new FileInputStream(src);
		OutputStream out = new FileOutputStream(dst)) {
		byte[] buf = new byte[BUFFER_SIZE];
		int n;
		while((n = in.read(buf)) >= 0){
			out.write(buf, 0, n);
		}
	} catch (IOException e) {
    	return defaultval;
    }
}
  • catch 절이랑도 같이 사용 가능하다.


profile
개발 로그 🍎 🍎 🍎

0개의 댓글

관련 채용 정보