-
[Java] Effective Java 3/E 정리 - 2장 객체 생성과 파괴Study/이펙티브 자바 2021. 6. 9. 15:09반응형
이번 장은 객체의 생성과 파괴에 대해 다룬다.
객체의 생성 시점, 생성 방법, 불필요한 생성을 피하는 법 ,
객체의 소멸을 보장하는 법, 정리 작업을 관리하는 법 등을 알아 본다.
Item 1. 생성자 대신 정적 팩토리 메서드를 고려하라
클래스의 인스턴스를 얻을 때 public 생성자를 주로 사용한다.
그러나 여기서는 정적 팩토리 메서드(static factory method)를 소개하며
정적 팩토리 메서드를 사용했을 때의 장단점을 설명한다.
정적 팩토리 메서드(static factory method)란?
클래스의 인스턴스를 반환하는 정적 메서드로, public 생성자 대신 혹은 함께 사용 가능하다.
책에서는 Boolean 클래스의 정적 팩토리 메서드 valueOf를 예제로 소개하고 있다.
valueOf 메서드는 인자로 받은 boolean값에 따라 Boolean 객체 타입으로 반환해주는 메서드다.
public static Boolean valueOf(boolean b) { return b ? Boolean.TRUE : Boolean.FALSE; }
정적 팩토리 메서드의 장점
- 이름을 가질 수 있다.
생성자와는 다르게 정적 팩터리 메서드는 이름을 가질 수 있고, 이름을 통해 반환될 객체의 특성을 표현할 수 있다. - 호출될 때마다 인스턴스를 새로 생성하지는 않아도 된다.
인스턴스를 미리 만들어두거나 새로 생성한 인스턴스를 캐싱하여 재활용하여 불필요한 객체 생성을 피할 수 있다.
* 불변 클래스(immutable class) 참고 - 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다
- 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
세번째 장점과 연관되는 장점으로 반환 타입의 하위 타입이라면 매개 변수에 따라 선택하여 반환할 수 있다. - 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.
이 장점은 서비스 제공자 프레임워크(service provider framework)를 만드는 근간이 된다.
ex) JDBC(Java Database Connectivity)
정적 팩토리 메서드의 단점
- 상속을 위해서는 public, protected 생성자가 필요하여 정적 팩토리 메서드만 제공한다면 하위 클래스를 만들 수 없다.
그러나 컴포지션 사용, 불변 타입 클래스 생성 등에 적용되는 사항이기 때문에 장점으로 받아들일 수도 있다. - 정적 팩터리 메서드는 프로그래머가 찾기 힘들다.
API 설명에 명확하게 들어나지 않기 대문에 문서를 잘 써두고 네이밍을 잘 해야한다.
주로 사용하는 명명 방식은 아래와 같다.
더보기- from: 매개 변수를 하나 받아서 해당 타입의 인스턴스를 반환하는 형변환 메서드
- of: 여러 매개변수를 받아 적합한 타입의 인스턴스를 반환하는 집계메서드
- valueOf: from과 of의 더 자세한 버전
- instance, getInstance: 매개변수로 명시한 인스턴스를 반환하나 같은 인스턴스임을 보장하지는 않는다.
- create, newInstance: instancㄷ, getInstance와 같으나 매번 새로운 인스턴스를 생성해 반환함을 보장한다.
- getType: getInstance와 같으나, 생성할 클래스가 아닌 다른 클래스에 팩토리 메서드를 정의할 때 사용한다.
"Type"은 반환할 객체 타입.
- newType: newInstance와 같으나, 생성할 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할 때 사용한다.
"Type"은 반환할 객체 타입.
- type: getType과 newType의 간결한 버전
Item 2: 생성자에 매개 변수가 많다면 빌더를 고려하라
일단 빌더를 사용하기 위해 가장 적합한 상황은 "생성자나 정적 팩터리 메서드에 매개 변수가 너무 많을 때"이다.
책에서 선택적 매개변수가 많을 때 사용할 수 있는 방법으로 세가지를 소개한다.
세가지 패턴을 비교하여 빌더 패턴에 대한 장점을 알아보자.
- 점층적 생성자 패턴 매개 변수를 1개, 2개, ..., N개로 늘려가는 방식이다.
단점은 매개변수의 개수가 많아질수록 코드를 작성하거나 읽기 어려워진다. - 자바빈즈 패턴 매개변수가 없는 생성자로 객체를 만든 후 setter 메서드로 값을 설정하는 방식.
단점은 객체 하나를 만들기 위해 메서드를 여러 개 호출해야 하고,
객체가 완전히 생성되기 전까지는 일관성이 무너진 상태에 놓이게 된다. - 빌더 패턴 필요한 객체를 직접 만드는 대신, 필수 매개변수만으로 빌더 객체를 얻는다.
그리고 빌더에 필요한 선택 매개 변수를 설정하고 build 메서드를 통해 객체를 얻는다.
빌더 패턴은 매개변수가 늘어날수록 읽고 쓰기가 간결하고, 자바빈즈 패턴보다 안전하다.
Item 3: private 생성자나 열거 타입으로 싱글턴을 보증하라
싱글톤(singleton)이란 인스턴스를 오직 하나만 생성할 수 있는 클래스를 말한다.
싱글톤을 만드는 만드는 방식으로 보통 두가지 정도가 있는데 두 방식 모두 private 생성자를 만든다.
첫 번째 방식은 인스턴스에 접근할 수 있도록 public static final 필드를 두고 접근하는 반면에
두 번째 방식은 인스턴스 멤버 변수는 private로 감추고, 인스턴스를 얻을 수 있는 정적 팩토리 메서드를 제공한다.
Item 4: 인스턴스화를 막으려거든 private 생성자를 사용하라
정적 메서드와 정적 필드만을 담은 클래스를 만들고 싶을 경우 인스턴스 생성이 불필요하고 이때 private 생성자로 만들면 된다.
추상 클래스로 만들 경우 상속해서 사용할 가능성이 있고
생성자를 만들지 않을 경우 컴파일러가 기본 생성자를 만들게 된다.
따라서 private 생성자를 이용하면 클래스 바깥에서는 접근 할 수 없고,
생성자 내부에서 AssertionError를 던질 경우 내부에서 실수로라도 생성자를 호출하는 일을 방지할 수 있다.
Item 5: 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라
클래스에 자원을 명시하지 않고 인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주면,
자원에 따라 동작이 달라지는 클래스를 구현할 수 있다.
이 패턴의 변형으로는 생성자의 자원 팩토리를 넘겨주는 방식이 있다.
이러한 의존 객체 주입 방식은 클래스의 유연성, 재사용성, 테스트 용이성을 높여줄 수 있다.
Item 6: 불필요한 객체 생성을 피하라
불필요한 인스턴스의 생성은 성능상 문제가 된다. ex) 오토박싱
동일한 기능을 제공하는 객체라면 매번 생성하기 보다 하나를 재사용하는 것이 좋다.
그러나 가벼운 객체의 경우 크게 부담이 되지 않기 때문에 무조건 재사용을 할 필요는 없다.
프로그램의 명확성, 간결성, 기능을 위해서라면 추가 생성은 좋은 일이다 :)
Item 7: 다 쓴 객체 참조를 해재하라
자바에서는 가비지 컬렉터가 알아서 메모리를 회수를 하기 때문에 메모리 관리에 소홀해진다.
그러나 메모리 누수가 발생한다면 가비지 컬렉션 활동과 메모리 사용량이 늘어나 성능에 치명적이다.
메모리 누수는 어떤 객체의 다 쓴 참조(absolete reference, 앞으로 쓰이지 않을 참조)가 어딘가에 남아 있어
그 객체와 객체가 참조하는 모든 객체를 회수하지 못하는 것을 말한다.
메모리 누수를 방지하기 위해선 해당 참조를 다 썼을 때 null 처리를 하거나, 참조를 가진 변수의 유효 범위를 잘 관리하면 된다.
Item 8: finalizer와 cleaner 사용을 피하라
자바의 finalizer와 cleaner는 C++의 destructor와 다르게 수행 시점과 수행 여부를 보장하지 않는다.
그래서 자원 회수를 위해 finalizer와 cleaner를 사용한다면 자원 회수가 지연될 수 있고,
최악의 상황에서는 자원 회수가 계속 지연되어 프로그램이 다운될 수 있다.
finalizer와 cleaner는 closer 메서드를 호출하지 않는 것에 대한 안전망 역할로 사용하거나
네이티브 피어와 연결된 객체를 처리하는 것에 사용한다.
Item 9: try-finally 보다는 try-with-resources를 사용하라
자바는 close메서드를 호출해 직접 닫아야 하는 자원을 위해 일반적을 try-finally 구문을 많이 사용한다.
그러나 닫아야하는 자원이 많아질수록 코드가 지저분해 지고,
연속적인 예외가 발생한다면 디버깅을 하기 힘들어진다.
try-with-resources를 사용하면 코드도 훨씬 간결해지고 스택 추적이 명확해진다.
'Study > 이펙티브 자바' 카테고리의 다른 글
[Java] Effective Java 3/E 정리 - 9장 일반적인 프로그래밍 원칙 (0) 2021.07.28 [Java] Effective Java 3/E 정리 - 8장 메서드 (2) 2021.07.18 [Java] Effective Java 3/E 정리 - 6장 열거 타입과 어노테이션 (0) 2021.07.07 [Java] Effective Java 3/E 정리 - 4장 클래스와 인터페이스 (0) 2021.06.23 [Java] Effective Java 3/E 정리 - 3장 모든 객체의 공통 메서드 (1) 2021.06.16 - 이름을 가질 수 있다.