반응형

1. 서론

이번 포스팅에서는 Effective-Java 3에 대해 공부한 내용을 공유하고자 합니다.

 

'효율적으로 자바 프로그램 작성하는 방법은 이러한 것들이 있구나' 라는 전제로 포스팅을 보시면 되겠습니다.

 

2. EffectiveJava

 

1. 박싱타입을 사용 X, 기본 타입을 사용

 

박싱타입이 아닌 기본타입을 사용해야 하는 이유는 아래와 같습니다.

 

  • 기본 타입은 유효한 값을 가지는 반면 박싱타입은 null을 가질 수 있어 위험도 증가.
  • 기본 타입은 박싱타입에 비해, byte가 적어 메모리, 시간 높은 성능.
  • 값 == 비교시 박싱타입의 경우에는 정확한 비교가 되지 않음.

 

2. 자원 해제가 필요한 클래스의 경우 AutoCloseable 인터페이스를 구현

 

자원 해제가 필요한 경우 AutoCloseable 인터페이스를 구현해야 합니다.

 

AutoCloseable 은 아래와 같이 close 메소드가 선언되어 있습니다.

 

void close() throws Exception;

 

 

AutoCloseable 을 구현해야 하는 이유는 간단합니다.

java 7에서 제공되는 try-with-resources를 사용하기 위해서는 이 AutoCloseable 인터페이스를 구현해야 하기 때문입니다.

 

3. util 클래스의 경우 private한 생성자를 정의하여 객체 생성의 방어로직을 넣자.

 

util 클래스는 public static 함수들을 가진 클래스를 의미합니다.

static 함수들만을 가지고 있기 때문에 객체를 생성할 이유가 없는 클래스입니다.

 

그렇기 때문에, 개발자의 실수를 방지하기 위해 util 클래스는 private 생성자를 두어 

instance 생성이 되지 않도록 합니다.

 

4. 상속 클래스의 경우 문서화 하라.

 

상속 클래스의 경우에는 조그만 변경에도 영향이 많이 갈 수 있어, 영향도를 문서화해야 합니다.

 

문서화는 class 혹은 각 method 위에 주석으로 설명을 해두는것을 추천합니다.

 

 

5. 추상클래스로 만든것은 인터페이스로 대체하는게 나을지 고려!!

 

추상클래스의 자식클래스는 상속 개념으로 동등이 아닌 하위 계층이 되며, 인터페이스는 동일한 계층으로 분류가 됩니다.

 

java의 경우 다중상속이 지원되지 않습니다. 이것은, 추상클래스는 최대 한개까지만 상속이 가능합니다.

 

대신, 인터페이스의 경우에는 계층구조를 가지게되는 것이 아니기 때문에 다중구현이 되며

java 내에서도 명시적으로 implements로 되어 있는 이유입니다.

 

6. inner 클래스의 경우 바깥 인스턴스에 접근할일이 없다면 무조건 static으로 만들어서 사용.

 

inner 클래스를 static으로 사용하지 않을 시 위험한 점은 아래와 같습니다.

 

  • 바깥 인스턴스로의 숨은 외부 참조를 갖게 됨.
  • 참조의 저장은 시간과 공간이 소비되며 gc가 바깥 인스턴스를 수거하지 못할 수 있음
    • 이는 메모리 누수로 이어지는 길.

 

7. 제네릭의 경우 로타입을 없애고 비한정적 와일드카드 타입을 사용하라.

 

우선, 로 타입의 정의는 아래와 같습니다.

 

매개변수화 타입이 지정이 안된 것 -> ex) List

로 타입은 java 에서 제네릭이 지원되기 이전의 호환성으로인해 서비스 중이지만, 권장 부분이 아닙니다.

 

그 이유는, 로타입의 경우 컴파일에서는 정상적으로 수행되나, 실행 시 런타임 에러를 내뱉기 때문입니다.

 

 

8. 제네릭의 경고를 제거할 수는 없지만 타입안전하다고 확신할수 있다면 @SuppressWarning("unchecked")를 사용하자.

 

이것은 다른 개발자에게 알림을 위해 사용하는것이므로, 가능한한 좁은범위로 사용해야합니다.

또한 경고를 무시해도 안전한 이유를 항상 주석으로 남겨야 합니다.

 

 

 

 

 

반응형

 

 

 

 

 

 

 

9. 배열보다는 리스트를 사용.

 

array는 공변(covariant)이며, List는 불변입니다.

공변은 계층구조를 가지며, super의 변화에 sub도 영향이 가는것을 의미합니다.
불변은 공변과 반대로 super의 변화에 영향이 없다는 것을 의미합니다.

 

공변인 array의 경우에는 type mistmatch경우 컴파일 시점에서 잡아내지 못합니다.

아래는 그 예입니다.

 

Object[] array = new Long[1];
array[0] = "Young's blog";

 

하지만, 불변인 list의 경우에는 컴파일 시점에서 잘못됨을 알 수 있습니다.

아래는 그 예입니다.

 

List<Object> list = new ArrayList<Long>();

 

배열은 이러한 타입 안전성이 떨어져 제네릭과도 어우러지지 않습니다.

 

그로인해, 배열 사용보다는 리스트를 추천합니다.

 

10. Object를 사용하는 코드는 되도록이면 제네릭을 활용하자.

 

Object의 경우 형변환을 하는 비용을 감수해야한다.

하지만 제네릭으로 변환 시 형변환의 비용이 들지 않아 성능면에서 이득을 볼 수 있습니다.

 

11. 가변인수보다는 List 사용

 

가변인수는 인자로 받은 사이즈만큼 배열을 생성하여 값을 세팅하여 사용하게 됩니다.

 

이 경우, size가 0인 가변인수를 받는 경우에는 컴파일 시점이 아닌 런타임시점에 실패하게 됩니다.

또한,  jdk8의 stream 함수를 사용하기 위해서는 list로 타입 변환을 해야하는 비용도 들게 됩니다.

 

이로인해, 가변인수를 사용하기 보다는 애초에 List를 사용하는 것을 추천합니다.

 

12. 정의하려는게 타입이라면 마커 인터페이스를 사용하자.

 

마커 인터페이스는 마커 어노테이션과 다르게 2개의 이점을 가지고 있습니다.

 

  • 구현 클래스의 인스턴스들을 구분하는 타입으로 사용 가능.
  • 어노테이션의 경우 모든 클래스에 적용이 가능한 반면, 인터페이스는 자신을 구현한 클래스만을 구현 클래스임을 보장하는 점입니다.

 

13. 익명클래스 -> 람다 -> 메서드 참조

 

익명클래스가 있다면 람다로 대체하는것을 고려해보자.

사실상 수행에 있어 다른점은 없다, 그 이유는 모두 class 파일로의 변환시에는 같은 byte 코드로 되어 있기 때문이다.

 

하지만, 람다로 대체하는 이유는 좀 더 쉽게 표현이 가능하다는 점입니다.

이것은, 개발자에게 매우 큰 이점을 가져다 줍니다.

 

또한, 타입 추론을 지원하여 더욱 개발의 편의성을 제공합니다.

 

여기서, 람다의 편의성을 능가하는것이 메서드 참조입니다.

 

대표적으로 아래와 같은 예가 있습니다.

 

map.merge(key, 1, (count, incr) -> count + incr); // -> 람다
map.merge(key, 1, Integer::sum); // -> 메서드 참조

 

코드만 보기에도 간결해진것을 볼 수 있습니다.

 

14. null 검사는 자바의 Objects.requireNonNull 사용하기

 

Objects.requireNonNull의 경우 null 체크와 null인 경우 npe의 message도 정의할 수 있어,

매우 간편하며 명시적인 메소드입니다.

 

15. 컬렉션의 반환은 null이 아닌 빈 컬렉션을 반환해라.

 

null은 위에서 말한것처럼 프로그램에서 매우 치명적입니다.

그로인해, 컬렉션은 빈 컬렉션을 반환하게하여 null을 참조하는것을 막아야 합니다.

 

16. int, long, double을 반환하는 optional은 OptionalInt, OptionalLong, OptionalDouble을 사용하자.

 

기본타입의 Optional 반환을 위해 박싱타입으로 변환하지 말고.

Optional에서 제공하는 OptionalInt, OptionalLong, OptionalDouble을 사용하자.

 

조금이나마, 성능저하가 덜하다.

 

17. 정확한 계산이 필요한 경우에는 double과 float는 버리고 int, long, BigDecimal을 사용해야 한다.

 

float, double은 과학과 공학 계산용으로 설계되었다.

이는, 넓은 범위의 수를 빠르게 정밀한 근사치로 계산되어진다.

 

그렇기 때문에, 정확한 계산 시에는 int, long, BigDecimal를 사용해야 합니다.

 

18. 문자열 연결의 경우 StringBuilder를 사용하자.

 

문자열 연결의 경우, 아래와 같이 하는 경우가 있습니다.

 

String sample = "1" + "-" + "2";

이 방법은 계속 다른 String을 생성하여 만들기 때문에 성능면에서 매우 좋지 않습니다.

 

이를 해결하기 위해 java에서는 StringBuilder가 있습니다.

 

사용 예시는 아래와 같습니다.

 

String sample = new StringBuilder().append("1").append("-").append("2").toString();

 

StringBuilder는 '+' 사용보다 일반적으로 5.5배가 성능면에서 유리합니다.

 

19. 객체는 인터페이스를 사용해서 참조하자

 

객체 생성 시 인터페이스가 있다면 인터페이스를 이용한 객체를 생성해야 합니다.

 

예로는 아래와 같습니다.

 

List<String> list = new ArrayList<>(); // - 좋은 예
ArrayList<String> list = new ArrayList<>(); // - 나쁜 예

 

 

 

인터페이스 참조를 한다면, 다른 구현 클래스의 참조가 필요 시에 코드가 유연하게 됩니다.

 

만약, 인터페이스가 존재하지 않는다면 필요한 기능을 만족하는 가장 덜 구체적인 클래스를 참조하도록 합니다.

 

20. 리플렉션 코드는 인터페이스로 가능하면 대체해라

 

리플렉션의 단점은 아래와 같습니다.

 

  • 컴파일 타임 시 타입 검사가 주는 이점을 하나도 누릴 수 없습니다.
  • 코드가 지저분해지며 장황해집니다.
  • 성능에 매우 악영향을 끼칩니다.

이러한 이유로 리플렉션 코드는 최대한 배제하는것을 추천합니다.

 

21. Exception, RuntimeException, Throwable, Error는 직접사용하지 말고 이를 구현한 자식 예외 클래스 혹은 커스텀 예외를 사용하자.

 

Exception, RuntimeException, Throwable, Error들은 여러 Exception들의 상위 클래스로서

정확한 어떠한 예외인지 판단하기에는 모호합니다.

 

그렇기 때문에, 상위 클래스를 구현한 예외 클래스 사용 혹은 custom 예외 클래스 사용을 권장합니다.

 

 

22. 시간 간격을 잴때는 System.currentTimeMillis 보다는 System.nanoTime을 사용하자

 

System.nanoTime은 더 정밀하고, 시스템의 실시간 클락의 변동에도 영향을 받지 않는 장점을 가지고 있습니다.

 

24. 쓰레드 보다는 실행자, 태스크, 스트림을 애용하자.

 

java.util.concurrent 패키지에는 실행자 프레임워크라고 하는 인터페이스 기반의 유연한 태스크 실행 기능을 가지고 있습니다.

 

장점으로는 아래와 같습니다.

 

  • 특정 태스크가 완료되기를 기다릴 수 있습니다.
    • get 메서드
  • 태스크 모음 중 하나 혹은 모든 태스크가 완료되기를 기다릴 수 있습니다.
    • invokeAny, invokeAll 메서드
  • 실행자 서비스가 종료하기를 기다릴 수 있습니다.
    • awaitTermination 메서드
  • 완료된 태스크들의 결과를 차례로 받을 수 있습니다.
    • ExecutorCompletionService 클래스
  • 태스크를 특정 시간에 혹은 주기적으로 실행할 수 있습니다.
    • ScheduledThreadPoolExecutor 클래스

 

25. 동기화 문제일때, Collections.synchronizedMap -> ConcurrentHashMap을 사용

 

Collections.synchronizedMap하나의 쓰레드가 전체 컬렉션의 모든 자원을 lock을 걸게됩니다.

 

하지만, ConcurrentHashMap의 경우 필요한 부분만을 lock을 걸어 안정적이며 성능의 이점을 가져다 줍니다.

 

3. 마무리

이번 포스팅에서는 Effective-Java에 대해 포스팅 하였습니다.

 

감사합니다.

반응형

+ Recent posts