Chapter2 객체 생성과 파괴
1. 생성자 대신 정적 팩터리 메서드를 고려하라
1.1 장점
- 이름을 가질 수 있음 → 반환될 객체의 특성을 쉽게 묘사할 수 있음
- 호출될 때마다 인스턴스를 새로 생성하지는 않아도 됨 → 객체의 생성 비용을 줄일 수 있음
- 반환 타입의 하위 타입 객체를 반환할 수 있음 → 유연성↑
- 반환 타입의 하위 타입이기만 하면, 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있음 → 유연성↑
- 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 됨
- → interface안에 static 메서드를 구현하여 가능하게 함
1.2 단점
- 상속을 하려면 public이나 protected 생성자가 필요하므로 정적 팩터리 메서드만으로 하위 클래스를 만들 수 없음.
- 정적 팩터리 메서드는 프로그래머가 찾기 어려움 → API 설명에 자세히 나오지 않으므로 정적 팩터리 메서드 명명 방식을 따라야 함
- from: 매개변수를 하나 받아서 해당 타입의 인스턴스를 반환하는 형변환 메서드
- of: 여려 매개변수를 받아 적합한 타입의 인스턴스를 반환하는 집계 메서드
2. 생성자에 매개변수가 많다면 빌더를 고려하라
2.1 장점
- 빌더 패턴은 매개변수가 많을때, 더 좋은 가독성을 제공해 준다
- 클래스 안에 내부 클래스로 구현하여 쓰인다 → 추상 클래스에서는 내부 추상클래스로 변경하여 사용
- 점층적 생성자 패턴으로 사용할 때
// 점층적 생성자 패턴 class User { //필수 private final String name; private final int age; //선택 private int weight; private String address; public User(String name, int age) { this(name, age, 0, ""); } public User(String name, int age, int weight) { this(name, age, weight, ""); } public User(String name, int age, String address) { this(name, age, 0, address); } public User(String name, int age, int weight, String address) { this.name = name; this.age = age; this.weight = weight; this.address = address; } }
→ 너무 많은 생성자가 필요하고, 생성자를 사용할 때 가독성이 좋지 않다.
User user = new User("kim", 20, 70, "서울")
- 빌더 패턴을 사용할 때
class User_Builder { private final String name; private final int age; private final int weight; private final String address; public User_Builder(Builder builder) { name = builder.name; age = builder.age; weight = builder.weight; address = builder.address } static class Builder { //필수 private final String name; private final int age; //선택 - 기본값으로 초기화 private int weight = 0; private String address = ""; public Builder(String name, int age) { this.name = name; this.age = age; } public Builder age(int val) { weight = val; return this; } public Builder address(String val) { address = val; return this; } public User_Builder build() { return new User_Builder(this); } } }
→ 코드가 더 길어졌지만, 새로운 객체를 만들때 가독성은 더 올라갔다.
User_Builder user = new User_Builder("kim", 20).weight(70).address("서울").build()
2.2 단점
- 객체를 만들려면, 빌더부터 만들어야 함
- 성능에 안좋은 영향을 미칠 수 있음
3. private 생성자나 열거 타입으로 싱글턴임을 보증하라
4. 인스턴스화를 막으려거든 private 생성자를 사용하라
- staic 메서드와 static 필드를 갖는 클래스를 만들때가 있는데(객체지향적으로 좋지 못함), 이때 생성자를 명시하지 않으면 컴파일러가 자동으로 매개변수를 받지 않는 public 생성자를 만들어주는데, 이는 다른 사용자들에게 있어서 이 생성자를 직접 만든것인지 아니면 자동으로 만든것인지 알 수 없게한다.
- 따라서 private 생성자를 추가하여 클래스의 인스턴스화를 막아야 한다.
5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라
- 많은 클래스들이 하나 이상의 자원을 사용하는데, 이때 자원들이 static final로 구현되는 것을 볼 수 있는데, 이는 확장성에서 유연하지가 않다
- 따라서 의존 객체 주입을 사용해서 자원을 사용해야 한다 → 유연성, 재사용성, 테스트 용이성 개선
- 자원에 따라서 클래스의 동작이 바뀌면 정적 유리틸리티 클래스나 싱글톤을 사용하지 않는것이 낫다.
- 정적유틸리티 사용 ex)
class FindRoutine { private final Transportation transportation = new Car; private FindRoutine() { } public static int distance() { ... } public static int routine() { ... } }
- 의존 객체 주입 사용
class FindRoutine { private final Transportation transportation ; public FindRoutine(Transportation transportation) { this.transportation = Objects.requireNonNull(transportation); } public static int distance() { ... } public static int routine() { ... } }
6. 불필요한 객체 생성을 피하라
String s = new String("kim");
보다String s = "kim";
과 같은 리터럴을 이용하여 초기화하는 것이 반복적인 객체 생성을 막아 성능이 향상되게 할 수 있다.
7. 다 쓴 객체 참조를 해제하라
- 다 사용한 객체는
null
을 이용하여 참조를 해제할 수 있다.- ex)
elements[index] = null;
- ex)
- 모든 객체에 적용하는 것은 바람직하지 않고, 메모리나 캐시를 직접 관리하는 클래스(
ex) 스택
)에서 가비지 컬렉터가 인지 못할 경우에 처리해야 한다.
8. finalizer와 cleaner 사용을 피해라
- finalizer와 cleaner는 불확실성과 성능 저하가 발생하므로 사용을 피해야 한다.
9. try-finally보다는 try-with-resource를 사용하라
- try-finally는 주로 자원을 close하는 데 많이 쓰이는데, 복수의 자원이 들어오면 코드의 가독성이 떨어지기 때문에 try-with-resource를 사용해야한다.
- try와 함께 자원을 선언하여 try 구문이 끝나고 나면 자동으로 자원이 사라지게 해준다.
-
/* try (자원) { ... } */ static void copy(String src, String dst) throws IOException { try (InputStream in = new FileInputStream(src); // 자원 두개를 try와 함께 선언 OutputStream out = new FileOutputStream(dst)) { byte[] buf = new byte[BUFFER_SIZE]; int n; while ((n = in.read(buf)) >= 0) { out.write(buf, 0, n); } } }
※ 참고
- 이펙티브 자바 - 조슈아 블로크
'BOOK > [이펙티브 자바]' 카테고리의 다른 글
[Effective Java] Chapter7 람다와 스트림 (0) | 2022.03.17 |
---|---|
[Effective Java] Chapter6 열거 타입과 애너테이션 (0) | 2022.03.15 |
[Effective Java] Chapter5 제네릭 (0) | 2022.02.15 |
[Effective Java] Chapter4 클래스와 인터페이스 (0) | 2022.02.10 |
[Effective Java] Chapter3 모든 객체의 공통 메서드 (0) | 2022.02.08 |