LostCatBox

(책리뷰)이펙티브 자바

Word count: 4.1kReading time: 25 min
2022/12/24 Share

(책리뷰)이펙티브 자바 3/E

2022.06.05 처음으로 다읽음.(80%이해못함. 그냥 자바 느낌을 배움)

왜?

자바라는 언어에 대해 조금이나마 친숙해지기 위해서

워낙 유명한책이고, 앞으로 자바를 많이 쓸예정이라서, 이해가 되지않는 부분도 일단 읽고 넘어가는식으로 1회독을 목표로 한다.

  • 해당책의 라이브러리는
    • java.lang
    • java.util
    • java.io
    • java.util.concurrent
    • java.util.function

1장 들어가기

  • 이책은 명료성, 단순성 원칙으로한다
  • 코드는 복사되는것이 아니라 재사용되어야한다.
  • 오류는 컴파일 타임에 잡아야한다

자바의 구성요소

  • 인터페이스

  • 클래스

  • 배열

  • 기본형

  • 기본형을 제외한 모든것은 객체

멤버

  • 메서드
  • 멤버 클래스
  • 멤버 인터페이스
  • 필드

용어

  • 상속==서브클래싱
  • 클래스가 인터페이스 구현
  • 인터페이스가 다른 인터페이스 확장(extends)
  • 아무것도 명시하지않았을때는 package-private사용
  • 공개 API는 프로그래머가 접근할수있는 모든 클래스, 인터페이스 , 패키지에 모든 클래스, 생성자, 멤버, 직렬화된형태를 가르킨다>> 이들 모두 API요소라고한다.
  • API를 사용하는 프로그램 작성자를 API사용자
    API를 사용하는 클래스(코드)는 그 API의 클라이언트라 한다.
  • 공개 API는 그 API를 정의한 패키지 밖에서 접근할수있는 API요소로 이뤄진다.
  • 따라서 모든 클라이언트가 접근가능하며, API작성자가 지원하기로 약속한 API요소들이다.
  • 패키지의 공개 API는 그 패키지의 모든 public 클래스, 인터페이스의 public혹은 protected멤버와 생성자로 구성된다

2장 객체 생성과 파괴

아이템 1. 생성자 대신 정적 팩터리 메서드를 고려하라

  • 정적 팩터리 메서드== 객체 생성의 역할을 하는 클래스 메서드(return 객체)
  • 이를 활용시 다양한 장점 존재
    • 이름가짐
    • 호출할 때마다 객체생성안함
    • 하위 자료형 객체 반환가능
    • 객체생성 캡슐화가능

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

  • 자바빈즈 패턴
  • 점층적 생성자 패턴
  • build 패턴 (세터들이 자신을 리턴하므로 메서드 연쇄가능

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

  • 싱글턴 만드는법 두가지
    • private 생성자() 후 static final 인스턴스 = new 생성자() 하면, 시스템에서 유일함(static이므로 이미 인스턴스 생성전에 생성자를 통해 인스턴스 만들어져있음)
    • private 생성자() 후 static final 인스턴스 = new 생성자() 하면 >>> 이를 getInstance() 함수의 return Instance>>> 항상 같은 객체를 반환하게된다.(유일함)(위에 방법에서 함수에서 그냥 return 변수한것임)
    • 즉, 제2의 클래스의 인스턴스는 만들어지지않음

아이템 4. 인스턴스화를 막으려거든 private 생성자를 사용하라

  • 정적 메서드와 정적 필드만 만들고싶을때!
  • Math같은 것들>> 생성자 정의안하면 기본 public생성자 자동생성됨.. 방지!
  • 해당 클래스로 상속도 불가능하게함>> 기본적으로 하위 클래스는 super()를 통해 상위 생성자를 호출하거되는데.. 이게 불가하니까!

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

  • 자원이 하나이상으로 의존하는 클래스는 이 자원을 생성자에서 생성자(){this.자원 = Objects.requireNonNull(자원)} 해줘서 아예 같이 객체에 넣어주는게 좋음

아이템 6. 불필요한 객체 생성을 피하라

  • String s = new String(”sss”)는 “sss”가 이미 String객체인데 또 객체를 생성하게만들며 실행될때마다 새로운 객체를 만들게한다.>> 이는 String s =”sss”해도 충분하며, 계속실행되도 똑같은 객체를 가르킬 뿐이다..(재사용)
  • 로마 패턴을 인식해주는것에 대한 자료들도 만약 함수에만 넣는다면 로드후 가비지가 삭제, 매번반복된다. 이러지말고, static final 필드로 해당 로마 패턴을 올린후 함수에서 갖다쓰는것으로 객체를 재사용해 성능이 높아진다
  • 래퍼클래스 ←—> 기본형 간의 이동인 오토박싱, 언박싱도 주의해야한다. 래퍼클래스가 되는순간 인스턴스가 계속생성됨

아이템 7. 다 쓴 객체 참조를 해제하라

  • 자기 메머로를 직접 관리하는 클래스라면 >>다쓴 참조를 null 처리해야 GC가 제거함
  • 보통은 scope를 이용하여 유효범위 밖으로 가면 GC가 알아서 제거되니까 신경안써도된다.

아이템 8. finalizer와 cleaner 사용을 피하라

  • 두가지 객체 소멸자 finalizer와 cleaner
  • 둘다 즉시 수행되지않는다. 언제실행될지는 GC만알수잇다.
  • 내 JVM에서는 잘되도 남의 JVM에서의 순서는 바껴서 안될수도

아이템 9. try-finally보다는 try-with-resoure

  • 자바 라이브러리에는 close메서드를 호출해 직접닫아줘야하는 자원이 많다
  • 자원을 제대로 닫히는것을 보장하기위해 try-finally많이사용된다 하지만 자원이 두개이상이면 try-finally문 2번사용 복잡
  • try-with-resoure를 활용해야하며, AutoCloseable이 구현되어있는것이여야한다. 대부분의 클래스 라이브러리가 이미구현함.
  • 매우 간결해지고, close()호출직접안해도됨

3장 모든 객체의 공통 메서드

  • Object 는 모든 클래스의 조상이며, final이 아닌 Object메서드들의 재정의는 약속하에 해야한다.
  • Comparable.compareTo도 이와비슷하다.

아이템 10. equals는 일반 규약을 지켜 재정의하라

  • equals의 재정의는 객체 식별성이 아닌 논리적 동치성을 비교가 필요할때다. 주로 값 클래스!(Integer, String)
  • 즉, 객체가 같은지가 아니라 값이 같은지 물어볼때
    (값 클래스라고 해도, 값이 같은 인스턴스가 둘 이상만들어지지않을때는 재정의필요없다.어차피 틀림)
  • == 연산자를 사용해 입력이 자기자신의 참조인지확인
  • instanceof 연산자로 입력이 올바른 타입인지 확인
  • 입력을 올바른 타입으로 형변환한다.(위에과정에서검사완료)
  • 입력 객체와 자기 자신의대응되는 핵심필드가 모두 일치시 true
  • equals()를 구현했다면 3가지확인하라
    • 대칭적?
    • 추이성?
    • 일관성?
  • equals()를 재정의할 땐, hashCode도 반드시 재정의하자
  • Object외의 타입을 매개변수로 받는 equals()는 작성하지말자!>> 이렇게하면 재정의(오버라이딩)아니라 오버로딩이다.

아이템 11. equals를 재정의하려거든 hashCode도 재정의

  • equals()를 재정의한 클래스 모두 hashCode()도 재정의해야한다
  • equals가 두 객체가 같다고 판단하면 반드시 hashCode()도 같은 판단나와야한다.
  • equals가 두 객체가 다르다 판단해도, hashCode()는 같을수도있다.(해쉬값충돌)
  • HashMap이나 HachSet에서 hashCode()씀..hashCode()재정의필요
  • hashCode(){return Objects.hash(…..)} 활용해도좋다.
  • 그리고 hashCode()에 캐싱처리해놓는것도좋다

아이템 12. toString을 항상 재정의하라

  • Object의 기본 toString는 단순히 클래스 _이름@16진수로표시한해시코드를 반환한다
  • toString은 반드시 재정의해야한다. 그 객체가 가진 주요 정보를 모두 반환해주는 것 이좋다.>>읽기좋은형태로!
  • 너무 정보가 방대하다면 해당객채가 가진특징요약도좋다
    전화번호부(총 1234개)
  • 상위 클래스에서 이미 알맞게 구현해놨다면 그대로써도된다.
  • toString가 어떤형식으로반환하는지 반드시 적어놔야한다.

아이템 13. clone 재정의는 주의해서 진행하라

  • Cloneable인터페이스는 매우 널리쓰인다.
  • 하지만 final클래스가 아닌이상 다양한 상황을 고려해야한다 따라서 생성자와 팩터리를 이용하는 게 최고다
  • 단 배열은 clone활용이 깔끔하다

아이템 14. Comparable을 구현할지 고려하라

  • Comparable 인터페이스의 유일한 메서드 compareTo!!!
  • compareTo는 Object메서드가 아니다
  • compareTo는 동치성 비교+순서비교+제니릭
  • Comparable을 구현했다 == 그 클래스의 인스턴스들에는 자연적 순서있음을 의미
  • 따라서 Comparable을 구현한 객체의 배열은 Arrays.sort(a);처럼 손쉽게 정렬할수있다.

스크린샷 2022-05-31 오전 11.22.07.png

  • 자바 플랫폼 라이브러리의 모든 값 클래스와 열거타입이 Comparable을 구현했다. 알파벳, 숫자, 연대등 순서가 명확한 값 클래스를 작성시 반드시 Comparable구현하자
  • 비교를 활용하는 클래스 = TreeSet, TreeMap,
  • 검색과 정렬 알고리즘 활용하는 Collections, Arrays
  • Comparable은 타입을 인수로 받는 제네릭 인터페이스 이므로, compareTo 메서드의 인수 타입은 컴파일타입에 정해진다.
  • Comparable을 구현하지 않은 필드나 표준이 아닌 순서로 비교해야한다면 Comparator를 대신 사용하자

4장 클래스와 인터페이스

  • 추상화의 기본 단위인 클래스와 인터페이스

아이템 15. 클래스와 멤버의 접근 권한을 최소화하라

  • 오직 API를 통해서만 다른 컴포넌트와 소통하며, 서로의 내부 동작방식에 전혀 개의치 않는다.<>> 캡슐화

  • 장점은 많지만 핵심은 개별 컴포넌트 병행으로 개발가능, 동작 검증가능

  • 각 요소의 접근성은 그 요소가 선언된 위치와 접근 제한자(4가지)로 정해진다.

  • 기본원칙은 모든 클래스와 멤버의 접근성을 가능한 한 좁혀야한다.

  • 가장 바깥의 톱레벨 클래스와 인터페이스에는 접근 수준이 2가지(package-private, public)가 존재한다
    이중 public을 선언시, 공개 API가 되며, package-private선언시 , 해당 패키지 안에서만 이용가능하다.
    즉, 외부에서 안사용하면 package-private바람직>> 내부 구현이므로, 클라이언트와 상관없이 수정, 교체, 제거가능.>> 반면 public일경우 API가되고, 하위 호환을 위해 영원한 관리 필요

  • 멤버(필드,메서드, 중첩클래스,중첩 인터페이스) 에 부여할수있는 접근 수준 4가지

    • private: 멤버를 선언한 톱레벨 클래스에서만 접근가능
    • package-private(deflaut): 멤버가 소속된 패키지 안의 모든 클래스에서 접근가능
    • protected: package-private의 접근 버위 포함하고, 이 멤버를 선언한 클래스의 하위 클래스에서도 접근가능
    • public: 모든 곳에서 접근 가능
  • public클래스의 private 멤버를 package-private까지 풀어주는건 괜찮지만 그 이후까지 허용하지말자.

  • public클래스의 인스턴스 필드는 되도록 public이 아니여야 한다

아이템 16. public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라

  • 단순한 class {iv인스턴스} 는 데이터 필드에 직접 접근하기에 캡슐화 이점 제공못함>> 불변식을 보장못하고, 외부에서 필드에 접근할떄 부수 작업을 수행할수도없다. >> 따라서 private으로 모두 바꾼후, public 접근자(getter)를 추가한다
  • 밖에서 접근가능한 클래스라면 접근자를 제공함으로 클래스 내부 표현 방식을 언제든 바꿀수있는 유연성을 얻음
    public클래스가 필드를 공개하면 이를 사용하는 클라이언트들이 생겨날것이므로 내부 표현 방식을 마음대로 바꿀수없다.
  • 하지만 package-private 클래스혹은 private 중첩 클래스라면 데이터 필드를 노출해도 문제없다.
  • public클래스의 가변 필드는 직접 노출 금지

아이템 17. 변경 가능성을 최소화하라

  • 불변 클래스==그 인스턴스의 내부 값을 수정할수없는 클래스
  • 만드는법 5가지
    • 객체의 상태를 변경하는 메서드를 제공하지않는다
    • 클래스를 확장할 수 없도록 한다
    • 모든 필드를 final로 선언한다.
    • 모든 필드를 private으로 선언한다.
    • 자신 외에는 내부의 가변 컴포넌트에 접근할수없도록한다.
  • 불변 객체이므로 메서드들의 반환은 결국 결과값을 담은 새로운 객체를 반환함
  • 불변 객체는 생성된 시점부터 파괴될때까지 그대로 간직한다
  • 불변 객체는 근복적으로 스레드 안전하여 따로 동기화할 필요없다.
  • 불변 클래스는 자주 사용되는 인스턴스를 public static final 타입 변수명 = new 타입(자주쓰이는값) static으로 해주면, 캐싱!! 을 통해 , 중복해서 생성할 필요도없어진다.>>> 이런 정적 팩터리르 사용하면 여러 클라이언트가 인스턴스를 공유하여, 메모리 사용량, 가비지 컬렉션 비용줄어든다.
  • 불변 객체의 단점은 값이 다르면 반드시 독립된 객체로 만들어야한다는 점이다.
  • 따라서 getter구현한다고 setter를 무조건 만들지 말자
  • 합당한 이유가 없다면 모든 필드는 private final 이여야한다

아이템 18. 상속보다는 컴포지션을 사용하라

  • 상속은 코드를 재사용하는 강력한 수단이지만, 잘못사용하면 오류를 내기 쉽다
  • (지금부터 말하는 상속은 인터페이스 구현 X, 인터페이스 확장X) only 클래스가 클래스로 상속하는것
  • 메서드 호출과 달리 상속은 캡슐화를 깨뜨린다( 상위 클래스 건드리면 하위클래스에서 문제발생ㄱ)
  • 상속받은 애의 allAdd()메서드호출시 상속받은 애의 Add()를 호출하므로 allAdd()를 오버라이딩하지않았다면, 오류남… 상속은 즉, 상위 클래스의 내부 구현방식을 알수없다.
  • 메서드 재정의만 문제아니다. 새로운 메서드를 추가하는것도 위험이다
  • 상속의 취약점 피하려면 컴포지션과 전달을 사용하자.

아이템 19. 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라

  • 재정의 가능 메서드는 public과 protected메서드중 final이 아닌 모든 메서드를 뜻한다
  • 상속을 고려하지 않았다면 하위 클래스에도 문제가 됨에 따라 금지하자
  • 상속금지는 클래스를 final로 선언하거나, 생성자 모두를 외부 접근 금지하자

아이템 20. 추상 클래스보다는 인터페이스를 우선하라

  • 추상 클래스와 인터페이스 차이를 보면, 추상클래스는 반드시 구현하는 클래스가 하위 클래스가 되어야한다는점이다(무조건 1개)
  • 인터페이스는 믹스인 정의에 안성맞춤이다. >>믹스인이란, 대상 타입의 주된 기능에 선택적 기능을 혼합한다!는의미
  • 인터페이스로는 계층구조없는 타입 프레임워크 만들수있다.
  • 추상 골격 구현, 인터페이스 함께 제공.. 템플릿 메서드 패턴

아이템 21. 인터페이스는 구현하는 쪽을 생각해 설계하라

  • 인터페이스는 디폴트 메서드가 들어갈수있게되었다.(하위 호환성때문에)
  • 인터페이스에 함수추가나, 디폴트 메서드를 추가하는 일은 상당히 다양한 테스트 케이스 진행후 릴리스해야함

아이템 22. 인터페이스는 타입을 정의하는 용도로만 사용하라

  • 인터페이스는 자신을 구현한 클래스의 인스턴스를 참조할 수있는 타입 역할을 한다.! 용도는 이것하나!!
  • 상수 인스터페이스 안티패턴은 인터페이스를 잘못 사용한 예이다. 마치 내부 구현인 클래스의 상수를 API로 노출시키는 것과 같은 효과다
  • 상수가 특정 클래스에 강하게 연관된 상수라면, 인스턴스화 할수없는 유틸리티 클래스에 담아서 공개하자(생성자를 private처리!!)

아이템 23. 태그 달린 클래스보다는 클래스 계층구조를 활용하라

  • 상속을 이용하여, 각 상황에 맞게 동작이 달라지는 메서드를 루트 클래스의 추상 메서드로 선언 후 이를 상속받아 각각의 특성있는 클래스를 만든다. 공통인자들도 모두 루트 클래스로 보낸후, 이제 상속받아 특정 변수들은 그곳에서 정의

아이템 24. 멤버 클래스는 되도록 static으로 만들라

  • 중첨 클래스란 다른 클래스 안에 정의된 클래서이며 자신을 감싼 바깥 클래스에서만 쓰여야한다!!
  • 중첩 클래스
    • 정적 멤버 클래스
    • 멤버 클래스
    • 익명 클래스
    • 지역 클래스
  • 첫번째 제외한 나머지는 내부 클래스에 해당된다.
  • 정적 멤버 클래스는 흔히 바깐 클래스와 함께 쓰일 때만 유용한 public 도우미 클래스로 쓰인다
  • 비정적 멤버 클래스의 인스턴스는 바깥 클래스의 인스턴스와 암묵적으로 연결된다. 따라서, 비정적 멤버 인스턴스안에서 바깥클래스명.this 형태로 가져올수있다.>> 비정적 멤버 클래스는 바깥 인스턴스없이는 생성불가능
  • 따라서 개념사 ㅇ중첨 클래스의 인스턴스가 바깥 인스턴스와 독립적으로 존재가능하다면 정적 멤버 클래스로해야한다.

아이템 25. 톱레벨 클래스는 한 파일에 하나만 담으라

  • 하나의 파일에는 하나의 톱레벨 클래스만 하자

5장 제네릭

아이템 26. 로 타입은 사용하지 말라