2장은 자바에서 사용하는 객체의 생성자, 싱글톤 객체, GC와 관련한 내용을 다룹니다.
1. 생성자를 직접 사용하기보단 정적 팩토리 메소드
- 팩토리 메소드에 이름을 붙임으로써 객체를 더 가독성 있게 생성
- 새로 생성될 필요가 없는 객체를 기존에 존재하는 객체로 반환 ex.싱글톤
- 반환 객체 타입의 하위 클래스 객체들도 반환할 수 있는 다형적인 동작
2. 생성자에 매개 변수가 많다면 Builder
- 객체 생성에 필수적이지 않은 필드까지 고려한 많은 생성자 오버로딩은 코드의 양을 늘리는 주범
- 사용자의 필드값 설정이 가능한 필드만 빌더로 열 수 있음
3. 싱글톤 객체 : private 생성자와 Enum 사용
- 싱글톤 객체를 만들기 위해선 생성자를 private으로 둬 객체 생성을 막아야함
- private static final 키워드로 변수를 할당하여 스태틱 영역에 싱글톤 객체를 생성하는 것도 방법 -> 직렬화-역직렬화 때마다 인스턴스가 생길 가능성
- Enum 정적 클래스 타입은 자바진영 자체에서 싱글턴 객체를 보장하기 위한 가장 좋은 방법
4. private 생성자로 인스턴스 생성 막음
5. 의존성이 있는 객체엔 DI를 사용하자
- 클래스 내부에서 직접 의존성 모듈을 생성하면 의존 객체와 결합도 상승
- 생성자에 사용할 의존성 객체를 넘겨주는 방식으로 사용하여 코드를 다형적으로 동작시킬 수 있도록 함
- 테스트, 재사용성, 유연성 상승
6. 불필요한 객체 생성 최소화
- 하기 쉬운 실수 : auto boxing, 원시 타입을 참조 타입으로 박싱하여 새로운 참조타입 객체를 만들어내는 실수
- 지연 초기화는 성능면에서 그렇게 긍정적이지 못하다
- 재사용 할 수 있는 객체라면 최대한 재사용
7. 다 쓴 객체의 참조 해제
- 객체 참조를 올바르게 해제하지 않으면 필요없는 객체가 참조하는 모든 객체들도 GC 대상에서 벗어남 -> 주의!
- GC 매커니즘이 존재하는 언어에선 메모리 누수를 찾기 까다로움(직접 free()하지 않기 때문)
- 애초에 지역 변수의 범위를 최소화(57. 지역 변수는 선언과 동시에 초기화)하는 것이 최선
8. GC 동작을 건드리지 말자
- finalize(), clean() 등은 호출했을 때 실행되지 않을뿐더러 실행 여부도 확실치 않음
- 기존 GC의 동작을 방해하기까지 함
9. try-with-resource 사용
- 기본적인 try-catch는 하나의 에러에 대해 close()작업이 수행되는 finally 블록까지 오류를 전파시킬 가능성
- AutoClosable 인터페이스를 구현한 IO 자원을 try의 괄호 안에서 선언 및 할당하여 try 블록 안에서 사용하자
- 더 짧은 코드, 더 유용한 예외 정보
독후감
메소드 내에서 new 키워드를 직접 사용하는 것보다 정적 팩토리 메소드를 사용하는 것이 낫다는 것은 확실한 것 같습니다. 사과 3개의 객체를 만들때 new Apple(3)보다 Apple.amountOf(3)이 조금 더 직관적이기 때문이죠. 또 객체를 생성하는 정적 팩토리 메소드 자체를 퍼블릭 인터페이스로 두고 그것을 다형적으로 동작시킬 수 있다는 점이 좋았습니다. OAuth로 소셜 로그인을 구현했을 때 of() 메소드를 사용하고 인자로 GOOGLE, NAVER 등의 열거 타입에 따라 일종의 전략 패턴으로 코드를 동작시켰는데요. 반환하는 객체는 각 provider에서 사용될 수 있는 UserInfo 타입이었습니다. 알게 모르게 이 전략을 사용한 것 같습니다.
그리고 스프링 프로젝트를 할 때, 필드값이 많은 @Entity 클래스 생성에 롬복의 @Builder를 이용했는데 이게 맞는 방법이었다는 생각이 들었습니다. 나 잘하고 있었잖아 사실 빌더를 이용한 코드를 먼저 본 뒤에 코드를 짠거라 과정과 결과가 뒤바뀌었다고 할 수 있지만..
싱글톤 객체의 생성과 관련해선, GoF 디자인패턴 책을 보며 멀티 스레드 환경에서 안전한 객체를 만드는 방법으로 1. static을 사용해 JVM에게 객체/변수 초기화를 위임하기 2. Enum 사용하기 두 가지 방법을 봤던 것으로 기억하는데, 이펙티브 자바에서도 같은 내용이 나와 반가웠습니다.
코드 실습
마을에서 농작물을 키우고 그것을 통해 수익을 올리는 코드를 만들 계획입니다.
private 생성자를 통해 객체의 명시적 new 호출을 막고, amountOf() 정적 팩토리 메소드로 객체를 생성할 수 있도록 했습니다.
public class Apple extends Crop {
private static final int APPLE_PRICE = 10;
private static final int APPLE_PROFIT = 20;
// ITEM 4 : 인스턴스화를 막기 위한 private 생성자
private Apple(int amount) {
this.price = APPLE_PRICE;
this.profit = APPLE_PROFIT;
this.name = "사과";
this.amount = amount;
}
// ITEM 1 : 정적 팩토리 메소드
public static Apple amountOf(int amount) {
return new Apple(amount);
}
}
그리고 작물을 기르는 CropField 클래스는 싱글톤으로 동작시키기로 했습니다. 내부에 static final 필드와 getInstance() 메소드를 두었습니다.
public class CropField {
private final ArrayList<Crop> crops = new ArrayList<>();
private static final CropField cropField = new CropField();
// ITEM 4 : 인스턴스 생성을 원치 않을 때 private 생성자
private CropField() {}
public static CropField getInstance() {
return cropField;
}
}
밭이 있는 마을 클래스 Village에는 CropField를 내부 의존성으로 갖는데, 이 때 DI 패턴을 사용했습니다. 롬복의 AllArgsConstrurctor와 final 필드를 봐주세요.
@AllArgsConstructor
@Slf4j
public class Village {
// ITEM 5 : 의존 객체 주입
// ITEM 2 : Builder (lombok)
private final CropField cropField;
private int money;
}
2장은 자바에서 사용하는 객체의 생성자, 싱글톤 객체, GC와 관련한 내용을 다룹니다.
1. 생성자를 직접 사용하기보단 정적 팩토리 메소드
- 팩토리 메소드에 이름을 붙임으로써 객체를 더 가독성 있게 생성
- 새로 생성될 필요가 없는 객체를 기존에 존재하는 객체로 반환 ex.싱글톤
- 반환 객체 타입의 하위 클래스 객체들도 반환할 수 있는 다형적인 동작
2. 생성자에 매개 변수가 많다면 Builder
- 객체 생성에 필수적이지 않은 필드까지 고려한 많은 생성자 오버로딩은 코드의 양을 늘리는 주범
- 사용자의 필드값 설정이 가능한 필드만 빌더로 열 수 있음
3. 싱글톤 객체 : private 생성자와 Enum 사용
- 싱글톤 객체를 만들기 위해선 생성자를 private으로 둬 객체 생성을 막아야함
- private static final 키워드로 변수를 할당하여 스태틱 영역에 싱글톤 객체를 생성하는 것도 방법 -> 직렬화-역직렬화 때마다 인스턴스가 생길 가능성
- Enum 정적 클래스 타입은 자바진영 자체에서 싱글턴 객체를 보장하기 위한 가장 좋은 방법
4. private 생성자로 인스턴스 생성 막음
5. 의존성이 있는 객체엔 DI를 사용하자
- 클래스 내부에서 직접 의존성 모듈을 생성하면 의존 객체와 결합도 상승
- 생성자에 사용할 의존성 객체를 넘겨주는 방식으로 사용하여 코드를 다형적으로 동작시킬 수 있도록 함
- 테스트, 재사용성, 유연성 상승
6. 불필요한 객체 생성 최소화
- 하기 쉬운 실수 : auto boxing, 원시 타입을 참조 타입으로 박싱하여 새로운 참조타입 객체를 만들어내는 실수
- 지연 초기화는 성능면에서 그렇게 긍정적이지 못하다
- 재사용 할 수 있는 객체라면 최대한 재사용
7. 다 쓴 객체의 참조 해제
- 객체 참조를 올바르게 해제하지 않으면 필요없는 객체가 참조하는 모든 객체들도 GC 대상에서 벗어남 -> 주의!
- GC 매커니즘이 존재하는 언어에선 메모리 누수를 찾기 까다로움(직접 free()하지 않기 때문)
- 애초에 지역 변수의 범위를 최소화(57. 지역 변수는 선언과 동시에 초기화)하는 것이 최선
8. GC 동작을 건드리지 말자
- finalize(), clean() 등은 호출했을 때 실행되지 않을뿐더러 실행 여부도 확실치 않음
- 기존 GC의 동작을 방해하기까지 함
9. try-with-resource 사용
- 기본적인 try-catch는 하나의 에러에 대해 close()작업이 수행되는 finally 블록까지 오류를 전파시킬 가능성
- AutoClosable 인터페이스를 구현한 IO 자원을 try의 괄호 안에서 선언 및 할당하여 try 블록 안에서 사용하자
- 더 짧은 코드, 더 유용한 예외 정보
독후감
메소드 내에서 new 키워드를 직접 사용하는 것보다 정적 팩토리 메소드를 사용하는 것이 낫다는 것은 확실한 것 같습니다. 사과 3개의 객체를 만들때 new Apple(3)보다 Apple.amountOf(3)이 조금 더 직관적이기 때문이죠. 또 객체를 생성하는 정적 팩토리 메소드 자체를 퍼블릭 인터페이스로 두고 그것을 다형적으로 동작시킬 수 있다는 점이 좋았습니다. OAuth로 소셜 로그인을 구현했을 때 of() 메소드를 사용하고 인자로 GOOGLE, NAVER 등의 열거 타입에 따라 일종의 전략 패턴으로 코드를 동작시켰는데요. 반환하는 객체는 각 provider에서 사용될 수 있는 UserInfo 타입이었습니다. 알게 모르게 이 전략을 사용한 것 같습니다.
그리고 스프링 프로젝트를 할 때, 필드값이 많은 @Entity 클래스 생성에 롬복의 @Builder를 이용했는데 이게 맞는 방법이었다는 생각이 들었습니다. 나 잘하고 있었잖아 사실 빌더를 이용한 코드를 먼저 본 뒤에 코드를 짠거라 과정과 결과가 뒤바뀌었다고 할 수 있지만..
싱글톤 객체의 생성과 관련해선, GoF 디자인패턴 책을 보며 멀티 스레드 환경에서 안전한 객체를 만드는 방법으로 1. static을 사용해 JVM에게 객체/변수 초기화를 위임하기 2. Enum 사용하기 두 가지 방법을 봤던 것으로 기억하는데, 이펙티브 자바에서도 같은 내용이 나와 반가웠습니다.
코드 실습
마을에서 농작물을 키우고 그것을 통해 수익을 올리는 코드를 만들 계획입니다.
private 생성자를 통해 객체의 명시적 new 호출을 막고, amountOf() 정적 팩토리 메소드로 객체를 생성할 수 있도록 했습니다.
public class Apple extends Crop {
private static final int APPLE_PRICE = 10;
private static final int APPLE_PROFIT = 20;
// ITEM 4 : 인스턴스화를 막기 위한 private 생성자
private Apple(int amount) {
this.price = APPLE_PRICE;
this.profit = APPLE_PROFIT;
this.name = "사과";
this.amount = amount;
}
// ITEM 1 : 정적 팩토리 메소드
public static Apple amountOf(int amount) {
return new Apple(amount);
}
}
그리고 작물을 기르는 CropField 클래스는 싱글톤으로 동작시키기로 했습니다. 내부에 static final 필드와 getInstance() 메소드를 두었습니다.
public class CropField {
private final ArrayList<Crop> crops = new ArrayList<>();
private static final CropField cropField = new CropField();
// ITEM 4 : 인스턴스 생성을 원치 않을 때 private 생성자
private CropField() {}
public static CropField getInstance() {
return cropField;
}
}
밭이 있는 마을 클래스 Village에는 CropField를 내부 의존성으로 갖는데, 이 때 DI 패턴을 사용했습니다. 롬복의 AllArgsConstrurctor와 final 필드를 봐주세요.
@AllArgsConstructor
@Slf4j
public class Village {
// ITEM 5 : 의존 객체 주입
// ITEM 2 : Builder (lombok)
private final CropField cropField;
private int money;
}