-x: 당장 중요하진 않은 내용
3장
Object의 클래스는 재정의 해서 사용하는것이 많음.
equals, hashcode, toString, clone, finalize
- Comparable.compareTo 는 Object는 아님
아이템 10
equals는 필요할 때만 재정의.
안필요하면 그냥 두는게 더 안전.
필요한 경우:
1. 논리적 비교를 해야하고
2. 상위 클래스에서 equals 를 알맞게 재정의 한적 없음
재정의 규약:
1. 반사성
- 자기 자신과 비교는 항상 참.
- x.equals(x) == true
2. 대칭성
- 거꾸로 해도 참
- x.equals(y) == true 면 y.equals(x) == true
3. 추이성
- 삼단논법 만족
4. 일관성
- 멱등성과 비슷
5. null 아님
- x.equals(null) == false
좋은 예시
@Override
public boolean equals(Object o){
if(o == this)
return true;
if(!(o instanceof PhoneNumber))
return false;
PhoneNumber pn = (PhoneNumber) o;
return pn.num == this.num && pn.num2 == this.num2;
}
아이템 11
항상 equals 정의시 hashCode도 재정의 해야함.
그래야 hashMap, hashSet 사용시 문제 안됨.
주의점:
멱등성
equlas 가 같다 판단하면 같은 hashCode 반환해야함
hashCode가 너무 많이 겹치면 안됨.
@Override
public int hashCode(){
return Objects.hashCode(a,b,c);
}
기본 제공하는 해쉬코드 써도 되는데, 속도는 느림
아이템 12
toString 은 항상 재정의해야함.
이유:
단순히 printf할때 뿐만아니라
assert, 디버거의 객체 출력 등에서 나도 모르게 쓰이기 때문.
어떻게?:
- 필요한 주요 정보를 모두 반환/ 거대한 클래스나 동작 위주면 요약 정보
- 문서화 고려. 안해도 주석은 달아주기
안해도 되는경우: 정적 유틸리티 클래스, 열거형
자동생성되는 toString 이라도 사용하기.
아이템 13
clone 재정의
요약:
1. coneable은 잘못 설계되서 cloneable 구현 안하는게 좋음
2. 복사하고싶으면 복사하는 생성자/정적 팩토리 메소드 생성 - 변환 생성자,변환 팩토리라 부름
3. 배열은 arr.clone 방식이 좋음. 객체는 주소값을 다 복사해버림
clonenable 구현시 주의점:
1. clone을 써야하면 public으로 변환 후 대상 클래스 타입을 반환
2. super.clone 호출 후 필요한 필드 수정 - deepcopy shallowcopy 생각
cloneable 안쓰는게 좋은이유:
1. 생성자를 호출 하지 않고도 객체를 생성하는게 모순적임
2. 가변객체를 참조하는 필드는 final로 선언하라는 용법과 충돌함 - deepcopy할때 생각
아이템 14
comparable<>
순서를 비교할 일 있으면 사용한다.
equals 비교와 구현시 지킬 일반 규약이 비슷하다(반사성,추이성,대칭성...)
equals 가 true 면 x.compareTo(y) == 0 이도록 하는게 좋다. - 컬랙션 정렬에서 둘이 다른 결과를 낼 수 있기 때문.
compareTo()구현시 <, > 등은 안쓰는게 좋다.
같이 박싱된 기본 타입 클래스들의 정적메소드 - ex( Integer.compare(a,b))쓰는게 좋다 - 실수 확률 낮춰줌
구현안된 클래스, 표준 아닌 순서로 비교할 클래스는 비교자 - Comperator를 이용하면 된다.
compareTo 구현할때 비교자 쓰면 간결하지만 성능저하가 조금 있다.
4장
클래스와 인터페이스
아이템 15 - 다시
클래스와 멤버의 접근 권한을 최소화하라
아이템 16
public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라
public으로 열면 안되는
이유
1. 캡슐화 이점 제공 안됨
2. 불변식 보장 불가
3. 부수 작업 수행 불가 - 클래스 만든 사람이 의도하지 않은 결과 발생 가능.
ex) 항상 getX할때 +1된값을 반환하고 싶었으면?
4. API 수정하지 않으면 표현 변경 불가
필드에 직접 접근 가능하게 하면,
그 클래스의 필드를 직접 사용하고 있는 코드 (클라이언트)가 생겨버림.
그러면 그걸 직접 못쓰게 하고 싶을때 난감함. java.awt.Dimension,Point 생각하기
아이템 17
변경 가능성을 최소화 하라
불견 객체로 만드는
방법
1. 상태 변경 메소드 제공 금지
2. 확장 불가하게 설계
3. 모든 필드 private final로 선언
4. 가변 객체의 참조값 그대로 반환하면 안됨
이유
- 단순하여 사용시 고려사항이 적음
1. 이 인스턴스를 지금 변경해도 되는가?
2. Thread Safe 한가?
등등 생각 안해도 됨.
함수형 프로그래밍
- 피연산자에 함수를 적용해서 결과를 반환해도 피연산자는 그대로인것.
- 어떤 객체 반환시 새로운 인스턴스 생성해 반환하면 됨.
아이템 18
상속보다는 컴포지션을 사용하라
클래스가 클래스를 상속하는 경우에 한정함.
확장할 목적있고 문서화 잘 한 클래스는 ㄱㅊ
class MyHashSet extends HashSet
이런게 있다하면 문제점 - 캡슐화를 깨뜨림
1. 상위 클래스에서 의도하지 않은 동작 가능 - 남이 만든 코드 내부 구현이 가려져있으면 그걸 생각하면서 코딩할수 없기때문
컴포지션과 전달을 이용하면 해결 가능.
컴포지션(구성) : 새로운 클래스를 만들고, 그 클래스의 필드로 기존 클래스의 인스턴스 참조하게 하는것
전달 : 새로운 클래스에서 기존 클래스의 메서드를 호출하는것.
전달을 이용하여 기존 클래스를 위험 없이 사용가능.
public class InstrumentedSet<E> extends ForwardingSet<E> {}
public class ForwardingSet<E> implements Set<E> {}
InstrumentedSet - 래퍼클래스
Set에 다른 기능을 추가했으니 데코레이터 패턴.
주의점
1. 콜백 프레임워크랑 쓰면 안됨 - 원래 객체는 래퍼 객체를 몰라서 문제 발생
2. 상속은 is - a 관계일때만(상속이 진짜 맞을때만) 사용. 컴포지션 사용하면 내부 구현 노출 방지 가능
아이템 19
상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라
상속용 클래스 만들려면 지켜야할게 많음.
1. 내부 처리 로직을 문서화 해야함. 캡슐화 위배
2. 1때문에 한번 문서화 한건 영원히 지켜야함
3. protected로 일부 메서드를 공개해서 하위 클래스 만들때 도와줘야함. 이걸 남발하면 걍 다 public으로 열어버린거랑 다른게 없음
4. 상속용으로 만들었으면 하위클래스 직접 몇개 만들어서 시험해 봐야함. 꼭. 유일한 방법임.
그래서 해야할건
1. 진짜 상속이 필요한지 판단 (아이템 18 참고)
2. 확장할 일 없으면 클래스를 상속 금지시키기
3. 정 해야겠으면 조심하기
아이템 20 - x 다시
추상 클래스보다는 인터페이스를 우선하라
다중 구현시에는 인터페이스가 적합함 (추상 클래스 말고)
1. 다중 상속때문에 클래스에 속성 추가 시 문제
2. 상속은 하위 클래스에서 예상하지 못한 동작 가능
인터페이스가 복잡하면 골격 구현을 제공하기.
아이템 21
인터페이스는 구현하는 쪽을 생각해 설계하라
옛날에는 인터페이스는 불변하다 가정했다.
그래서 인터페이스에 메소드를 추가하면, 구현 안한 클래스들이 전부 컴파일 에러났다.
자바 8 부터는 인터페이스에 메소드를 추가할수 있다.
디폴트 메소드를 이용한다.
하지만 이미 있는 구체 클래스들이 원치 않게 그 메소드를 삽입 당한다.
그래서 오류가 발생할수 있다 - ex) 동시성을 고려 안한 default method 가 동시성 관련 메소드와 같이 쓰이면 에러.
결론
1. 인터페이스는 애초에 잘 설계한다.
2. 디폴트 메서드는 아이템 20의 골격 구현에 도움된다. 이때 쓰는 디폴트 메서드는 좋다.
3. 디폴트 메서드를 추가해야 한다면, 테스트를 잘 해야한다
아이템 22
인터페이스는 타입을 정의하는 용도로만 사용하라
인터페이스는 구현체가 이런이런 타입이다 하는 용도임.
상수 공개용 수단으로 쓰기도 하는데, 안티패턴임. 쓰면 ㄴㄴ
이유 - 하위 클래스가 전부 쓸데없이 그 상수를 알게 되버리니까
상수 공개용이면,
1. 구체 클래스에 직접 넣거나,
2. 열거 타입 만들거나
3. 인스턴스 불가능한 유틸 클래스 만들기 (생성자가 private인 클래스 )
아이템 23
태그 달린 클래스보다는 클래스 계층구조를 활용하라
이상한거 만들지 말고 객체 지향적으로 코딩하자.
아이템 24 - x 중첩 클래스 자세히 보고 다시 보기
멤버 클래스는 되도록 static 으로 만들라
중첩 클래스 종류 4가지
아이템 25
탑 레벨 클래스는 한 파일에 하나만 담으라
문제점
1. 클래스 또 선언해서 컴파일 에러
2. 컴파일 순서 따라 다른 동작
5장
제네릭
아이템 26
로 타입은 사용하지 말라
타입 파라미터 사용된 클래스, 인터페이스 - 제네릭 클래스 / 인터페이스
제네릭 타입: 제네릭 클래스, 인터페이스 통칭
제네릭 : 지금은 이 타입을 모르지만, 이 타입이 정해지면 그 타입 특성에 맞게 사용하겠다!
와일드 카드 : 지금도 이 타입을 모르고, 앞으로도 모를 것이다!
List<?>은 ?가 뭔지 계속 몰라서, list.add("STRING") 불가함
List<T>는 가능
List<Object> 와 List<String> 은 상속관계 아님. 파라미터화 타입은 다른거로 취급하기 떄문.
로 타입은 하위 호환성을 위해 존재함.
로 타입을 쓰면 컴파일 타임에 되서야 예외 발생함.
하지만 와일드카드/제네릭 메서드 사용하기
아이템 27
비검사 경고를 제거하라
비검사 경고
- ClassCastException 발생 가능성에 대해 경고
이를 전부 없애주는게 안전함.
예외 생길일이 없는데 비검사 경고 뜨면
좁은 범위에 @SupressWarnings 달아줘야함.
- 진짜 예외가 발생할때 찾기쉬워짐
아이템 28
배열보다는 리스트를 사용하라
List<Object> 와 List<String> 은 상속관계 아님
- 제네릭은 불공변
Object[] 는 String[] 상위 타입임
- 배열은 공변
배열
공변, 실체화 됨 - 타입 소거 안되고 런타임에도 알고있다는 말
런타임에 타입 세이프
컴파일타임에 타입 세이프 아님
제네릭
불공변, 컴파일 후에 타입 소거 - 중요
런타임에 타입 세이프 아님
컴파일타임에 타입 세이프
컴파일타임에 잡는 예외가 더 좋으니까, 배열보다는 리스트 쓰기
List<String>[]
혹은 책의 예시처럼
배열 제네릭 섞어 쓰지 말기
아이템 29
이왕이면 제네릭 타입으로 만들라
그래야 더 편하다.
기존 사용자도 영향 안받음
아이템 30 - x
이왕이면 제네릭 메서드로 만들라
아이템 31
한정적 와일드카드를 사용해 API 유연성을 높이라
아이템 32
ㅇ
아이템 33
ㅇ
7장
람다와 스트림
아이템 42
익명 클래스보다는 람다를 사용하라
대부분의 경우에 편하니까.
그럼 익명클래스 사용은 언제?
- 람다식이 너무 길때
- 추상 클래스의 인스턴스 생성
- this는 람다에서 못씀
아이템 43
람다보다는 메서드 참조를 사용하라
무조건 그런건 아니다.
람다로 작성하고 바꾸면서 코딩하기
아이템 44
ㅇ
아이템 45
ㅇ
아이템 46
ㅇ
아이템 47
ㅇ
아이템 48
ㅇ
8장
일반적인 프로그래밍 원칙
아이템 49
매개변수가 유효한지 검사하라
메서드 초반에 파라미터를 검사하고 예외 던져야함.
- 예외는 최대한 빨리 잡는게 좋기때문.
- 실패 원자성을 지킬수 있음: 아이템 76 - 메서드가 실패해도 객체는 호출 전 상태를 유지해야한다
너무 과하게 검사할 필요는 없음 - 암묵적 유효성 검사 일어나기 때문 - 정도가 중요
그리고 문서화 해놓기
아이템 50
적시에 방어적 복사본을 만들라
생성자/ getter/setter는 인스턴스 원본을 복사 후 그 인스턴스를 이용해서 만들기 고려하라는 말
이유
1. 악의적으로 내부 요소 수정하는거 피하려고
ex)인스턴스 필드가 final List<T> 가져도, 새로운 리스트 인스턴스를 할당할수는 없지만 ins.getList().clear()이런거는 가능하다
클래스가 불변객체를 가지면 방어적 복사본 필요없지마, 어쩔수없이 못그러는 경우가 많다 (레거시 때문에)
복사비용 너무큼 || 클라이언트가 믿을만 하다고 생각
이면, 문서에다가 이 클래스는 방어적 복사 안한다고 알리기
아이템 51
메서드 시그니처를 신중히 설계하라
메서드 이름 잘 짓기
편의 메서드 너무 많이 만들지 말기 - 외우기 힘드니까
파라미터 적게 넘겨주기 -외우기 힘들고, 파라미터 순서 틀릴 가능성도 높아짐
방법
1. 메서드 쪼개기
- 부분리스트에서의 인덱스 찾는 메서드 필요하면 그걸 밑바닥부터 만들지 말고 부분리스트 만드는 메서드, 인덱스 찾는 메서드 이용하기
2. 도우미 클래스를 정적 멤버 클래스로 두고 파라미터로 넘겨주기
- 예를들어 Coordinate{int x, int y}
아이템 52
ㅇ
아이템 53
ㅇ
아이템 54
ㅇ
아이템 55
ㅇ
아이템 56
ㅇ
9장
일반적인 프로그래밍 원칙
아이템 57
지역변수의 범위를 최소화 해라
이유:
1. 가독성,유지보수성,오류가능성
방법:
1. 처음 쓰일때 선언하고, 동시에 초기화하기 - try/catch 같이 중괄호 바깥에서도 사용해야하면 제외
2. while 보다 for문이 이 관점에서는 더 좋음 - for문 지역변수는 소괄호,중괄호 내부이기 때문. for는 기본,for each 포함. 근데 iterator써야하면 기본 for 사용
3. 메소드를 작게 유지하기
++
for(int i = 0, n = expensiveComputation(); i < n; i++)
이렇게 하면 루프마다 계산하는 비용 하낌
아이템 58
for문 보다는 for-each문을 사용하라
이유:
1. 가독성,유지보수성,오류가능성
2. 속도 손해도 없다
안되는 상황:
1. 컬랙션 순회하면서 원소 제거, 변형할때 - Collection의 removeIf 쓰면 피할수 있음
아이템 59
라이브러리를 익히고 사용하라.
원하는 기능이 있으면, 표준 라이브러리를 이용하여 구현하는게 좋음. 모르면 찾아서 사용하고, 없으면 좋은 서드파티 찾고, 그래도 없을때는 직접 구현하기.
이유:
1. 품질이 더 좋음
2. 버그나 성능 문제가 있으면 개선될것임
3. 필요 기능이 점점 추가될것임
4. 개발자들 입장에서도 익숙해서 읽기 좋음
java.lang, java.util, java.io 등은 공부 열심히 하기.
특히 컬랙션 프레임워크, 스트림, java.util.concurrent
아이템 60
정확한 답이 필요하면 float, double은 피하라.
이유: 부동소수점 표현의 한계가 있기때문
해결: int, long 쓰거나
BigDecmimal 사용하기 - 쓰기 불편하고 느림.
아이템 61
박싱된 기본 타입보다는 기본 타입을 사용하라
이유:
1. == 비교 하면 식별성 비교함.
ex)
Integer == Integer -> 값 말고 주소값 비교함
2. 박싱된 기본 타입은 null 가질수 있음
ex)
Integer == int -> 오토 언박싱 해서 Integer null이였으면 npe
3. 시간, 메모리를 비효율적으로 사용할 수 있음
ex)
박싱된 기본 타입은 기본 타입과 연산시 거의 항상 언박싱 되서, 비효율적임
for문에서 (Integer 인 sum) sum += 1 이러면 느림
아이템 62 - 쓰레드 로컬 공부하고 다시 보기
다른 타입이 적절하다면 문자열 사용을 피하라
기본타입, 열거타입, 혼합 타입 등을 문자열로 안쓰는게 좋음
이유: 번거롭, 덜 유연, 느림,오류가능성
아이템 63
문자열 연결은 느리니 주의해야한다
+로 연결하면 O(n^2)임
아이템 64
객체는 인터페이스를 사용해 참조하라
매개변수,반환값,변수,필드 전부 다 하라
되도록 생성자로 생성할때만 실제 클래스 쓰는게 바람직함
이유:
1. 유연한 교체 - 교체이유: 성능이 더 좋거나/ 신기능 제공
주의점:
1. 새 클래스가 원래 클래스로 할때처럼 잘 돌아가도록 기능을 가지고 있어야한다.
2. 적합한 인터페이스 없으면 최대한 덜 구체적인 클래스를 타입으로 선언
선언 타입이랑 구현타입 동시에 바꾸면?:
컴파일이 안되는 문제 발생 가능하므로 안됨
아이템 65 - x
리플렉션보다는 인터페이스를 사용하라
리플렉션 사용하는 상황: DI 프레임워크, 코드 분석 도구 등의 개발
아이템 66
네이티브 메서드는 신중히 사용하라
자바 네이티브 인터페이스: JNI
- 네이티브 언어(C,C++) 등으로 작성된 네이티브 메서드를 호출하는 기술.
언제 사용?
1. 플랫폼 특화 기능 사용시(레지스트리)
2. 레거시 라이브러리를 사용할 일이 있을때
3. 성능 개선
성능 개선 목적:
신중히 사용하기. 왜냐면 요즘은 자바도 발전해서 진짜 써야할 일이 잘 없음
부작용:
복잡함, 디버깅 어려움, gc가 메모리 회수 못함, 낮은 이식성
아이템 67
최적화는 신중히 하라
성능 최적화 하겠다고 아키텍처, 가독성을 해치면 안된다.
1. 딱히 성능이 올라가지 않을수 있고
2. 유지보수가 어렵다.
일단 아키텍처, 컨벤션을 잘 따르고 나서, 성능이 느리다 싶으면
그때 가서 최적화 하고, 전후로 성능 측정하기
변경이 어려운 API, 네트워크 프로토콜, 영속 데이터 포맷은 애초에 잘 결정해야함.
아이템 68
일반적으로 통용되는 명명 규칙을 따르라
상식을 따르면 됨.
객체 생성 불가능한 클래스는 복수형 명사로 짓기
- Collections, Arrays
정적 팩터리
- valueOf, newType
타입 파라미터의 경우 알아보기
10장
일반적인 프로그래밍 원칙
아이템 69
예외는 진짜 예외 상황에만 사용하라
1. 예외는 흐름 제어에 사용해서는 안됨.
2. 그러도록 설계해서도 안됨.
- if Exception 발생 : 다른일 이런식으로 어떤 api를 사용해도, 만들어도 안됨.
- Iterator 순회시 hasNext() 같은 상태검사 메서드로 예외인지 아닌지 검사하기
- 동시성, 성능 등의 문제 있을때는 Optional | 특정 값 사용. 그러나 신중하기
아이템 70
복구할수 있는 상황에는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라
Exception - 검사 예외, checked Exception
RuntimeException - 비검사 예외, unchecked Exception
Error - 에러
구현시 선택 기준
Exception(checked)
- 복구할수 있는 상황. 클라 잘못이 아님.
- 복구에 필요한 정보 제공 메서드도 만들기(아이템 75)
- 이거 말고 그냥 Optional 반환할지 고민해보기. 그러나 복구에 필요한 추가 정보 필요하면 이거 발생시키기.
RuntimeException (unchecked)
- 프로그래밍 오류 (클라이언트가 전제조건을 못지킴)
- 복구할수 있는지, 프로그래밍 오류인지 햇갈리면 RuntimeException 상속받기
Error
- 상속 하지 말기. 업계 표준임. unchecked 는 전부 RuntimeException으로 하기
- throw도 하지말기. AssertionError 제외
Exception, RuntimeException, Error 전부 아닌 Throwable 객체
- 만들지 말기. 쓸데없이 햇갈리기만 함.
아이템 71
필요 없는 검사 예외 사용은 피하라
checked exception은 처리하기 불편하기 때문.
아이템 70의 구현시 선택 기준 잘 지키기.
아이템 72
표준 예외를 사용하라
표준예외 장점
- 사람들이 익숙함
- 가독성, 유지보수성 좋음
어떤 예외 쓸지 햇갈리면, 가장 구체적으로 적합한 예외 사용
아이템 73
추상화 수준에 맞는 예외를 던지라
너무 저수준 예외가 뜬금없이 발생하면
1. 원인 파악 힘듦
2. 내부 구현이 드러나버림
예외 번역
- 저수준 예외 잡아서 자기 추상화 수준에 맞는 예외로 바꾸는것.
- 위의 문제 방지 효과.
- 예외 연쇄 사용하여 근본 원인 확인 가능. 표준 외는 거의 예외 연쇄용 생성자 갖고있음.
- 남용하진 말기. 애초에 예외 발생 시키지 말거나, 발생해도 던지지말고 자기가 처리하기.
아이템 74
메서드가 던지는 모든 예외를 문서화하라.
예외 처리에 대한 부가 정보가 잘 드러나도록.
아이템 75
예외의 상세 메시지에 실패 관련 정보를 담으라.
toString() 오버라이딩 할때 주의점과 거의 같음.
1. 필요한 정보 모두 담기
2. 필요없는 정보, 민감한 정보(pw등) 제외하기
아이템 76
가능한 실패 원자적으로 만들라.
실패 원자적
- 어떤 객체의 메서드 호출이 실패해도(예외 발생해도) 객체는 메서드 호출 전 상태를 유지해야 한다.
예시)
stack.pop() 에서
메서드 초반에 size == 0이면 throw EmptyStackException 던짐.
그렇게 말고 후반에 하면 size -1 되서 (메서드 호출 전과 상태 다름) 계속 예외 발생함.
실패 원자성 달성 방법
1. 작업 수행 전 매개변수의 유효성 검사 (바로 위 예시)
2. 메서드 호출 성공했을때만 상태 변경하기(값을 대입하기)
실패 원자적으로 못만드는경우
- 비용, 복잡도가 너무 큰 경우
- 동기화 문제도 있을때
이때 문서에 잘 설명해야함
아이템 77
예외를 무시하지 말라
예외를
처리하거나
밖으로 던지거나
로그를 남기거나
전부 안하고 그냥 캐치하고 아무것도 안하면 나중에 큰일날수 있음