-
57. 지역변수의 범위를 최소화하자
-
58. 전통적 for문보단 for-each문을 사용하자
-
59. 라이브러리를 익히자! (바퀴 재발명 X)
-
60. 정확한 답이 필요하다면 소수형 타입(float, double)을 피하자
-
61. 박싱된 Primitive Type < Primitive Type
-
62. 모든 것을 문자열 타입으로 사용하지 말자
-
63. 문자열 연결은 일반적으로 느리다
-
64. 객체는 인터페이스를 통해 참조하자
-
65. 인터페이스 > 리플렉션
-
66. 네이티브 메소드 사용 최소
-
67. 최적화는 신중히
-
68. 일반적인 명명 규칙을 따르자
-
독후감
이펙티브 자바 어렵다..............

실무 코드도 똑바로 봐본적 없는 제가 읽기엔 심오하고,, 추상적이고,, 큰 의미를 갖는 것도 많이 없는 것 같아 발췌독을 해보도록 하겠습니다!
57. 지역변수의 범위를 최소화하자
- 지역변수의 범위는 선언시점부터 중괄호가 끝날때까지 -> 선언과 동시에 초기화해서 사용하자
- Iterator패턴을 사용하는 for문의 경우, 반복문을 도는 동안 블록 안에서만 사용할 수 있는 변수를 만들어 범위를 최소화시킨다는 점에서 좋다
- 메소드 기능을 작게 분리하고 하나의 메소드는 하나의 기능을 하게 함으로써 변수의 범위와 기능의 비중을 줄이기도 가능
58. 전통적 for문보단 for-each문을 사용하자
- for문 안에서 사용할 지역변수를 선언하고 선언; 조건; 업데이트; 식으로 구성하면 코드가 지저분해지기 쉽다. Iterator을 사용하고, hasNext()와 next()를 직접 호출하며 필요 이상으로 코드가 길어지기도 한다. next()를 직접 호출하는 코드는 버그를 발견하기 힘들다
- "향상된 for 문", 혹은 "for-each문"을 사용하면 조금 더 깔끔, 유연한 코드가 되는데, for (E e : Iterable<E>) 형식으로 반복문 선언이 가능하다 -> Iterable을 구현한 클래스라면 for-each문을 사용할 수 있다!
- Iterable 객체를 순회하며 modify하거나 삭제를 하는 경우라면 이를 사용할 수 없는데, 이땐 removeIf()를 사용하거나 실제 index값을 통한 반복문을 구성해야한다.
59. 라이브러리를 익히자! (바퀴 재발명 X)
- 라이브러리를 만든 전문가와 이미 해당 라이브러리를 사용한 개발자의 지식을 얻을 수 있고, 핵심 비즈니스 로직에 집중할 수 있고, 라이브러리 개선에 따라 메인 어플리케이션 역시 같이 개선되는 효과를 누릴 수 있는 표준 라이브러리를 애용하자!
- 널리 사용되는 라이브러리, 혹은 코드를 사용하면 다른 개발자들에게도 익숙한 코드가 된다
- 자바랭, 유틸, io 등의 패키지와 그 라이브러리들은 자바 개발자로서 어느정돈 알아두자..
60. 정확한 답이 필요하다면 소수형 타입(float, double)을 피하자
- 이진 데이터를 다루는 컴퓨터의 특성상, 소수를 정확히 표현할 수 없기 때문에 소수 자료형은 어느정도 오차가 날 수밖에 없다 -> 금융 관련이나, 정밀한 계산이 필요한 군사 시스템엔 사용하면 안된다
- 금융 계산엔 BigDecimal을 쓰는 것이 좋은데, primitive 타입을 사용하는 것보다 훨씬 느리고 쓰기 불편하겠지만 소수를 정확하게 저장할 수 없는 컴퓨터 특성상 어쩔 수 없는 부분이다..
61. 박싱된 Primitive Type < Primitive Type
- 박싱 타입은 결국 JVM의 힙에 새로 객체가 생성된다 -> 객체 생성에 비용이 발생 & GC의 동작을 요구하여 성능을 떨어뜨림, 오토박싱 / 언박싱이 일어나는 과정에서 성능 문제가 발생할수도.
- 박싱 타입은 "==" 연산이 제대로 동작하지 않을 가능성이 있고(identity 만족 X) 일반 primitive type이 가지는 default값 없이 null로 초기화될 수도 있음
- 컬렉션의 원소나 키 값으로 사용될 때, 기본값이 null로 설정되어야 할 때(DB값 등)가 아니면 primitive type을 쓰도록 하자
62. 모든 것을 문자열 타입으로 사용하지 말자
- 문자열(String)은 편리하여 오남용되기 쉬움.. IO 결과 데이터에 무턱대로 String을 붙인다거나 하면서 다른 타입의 대체품으로 쓰는걸 경계하자 -> String이 제공하는 기능에만 의존하며 적절한 Object 메소드를 제공하기 힘들어질 확률이 크다
- 모든 type을 문자열로 하는 대신 primitive type, enum, 혼합 타입(ex. 특정 VO)이 어울릴 땐 해당 타입들을 사용하도록 하자!
63. 문자열 연결은 일반적으로 느리다
- String은 불변이기 때문에 "+"연산에 최악의 경우 O(N^2)시간이 걸린다. 성능에 신경쓴다면 문자열 "+"연산을 피하자
- 단순한 + 열거의 경우 JVM이 컴파일 과정동안 최적화를 시키며 자동으로 StringBuilder를 사용하겠지만, for문을 통한 concat일 경우 for문을 도는 횟수만큼 문자열 객체가 새로 생성된다 -> GC 과부하!
64. 객체는 인터페이스를 통해 참조하자
- 특정 객체를 객체의 실제 구현체로써 사용해야할 상황은 new 생성자를 통한 생성시 뿐, 나머지는 인터페이스로 선언하고 참조하자 (ArrayList<Integer> integerList로 선언하지 말고, List<Integer> integerList로 선언하라는 소리)
- 프로그램이 객체지향의 이점을 매우 잘 누리면서 훨씬 유연한 동작을 할 수 있도록 구성할 수 있다 -> 인터페이스를 구현한 다른 구현체로 손쉽게 프로그램의 일부를 다형적으로 동작시킬 수 있다
- 물론, 적합한 인터페이스가 없다면 구체 클래스를 직접 참조하는 수밖에 없는데(ex. VO, 특정 클래스에서만 동작하는 메소드를 사용할 경우), 그렇지 않다면 상위 인터페이스를 추가하거나 어쨌든 최상위 추상 클래스를 통해 참조하는 것이 좋다
65. 인터페이스 > 리플렉션
- 리플렉션은 런타임에 클래스 정보에 접근해서 임의의 클래스 메소드와 필드값 등을 사용할 수 있게 해주는 java api
- 그러나 리플렉션을 이용하면 성능이 떨어지고, 코드가 복잡해지고(리플렉션 api 호출과 각종 예외처리로 인해), Class<?>와 Method 등을 사용하게 되므로 타입 검사의 이점을 누릴 수 없다! 리플렉션을 통한 메소드 호출 대신 인터페이스를 이용하자!!
- IDE, DI 프레임워크, 코드 위빙이 일어나는 라이브러리 등 리플렉션이 꼭 필요한 상황이 아니면 사용 X(그마저도 객체 생성 이외에는 리플렉션 사용 최소화중)
- 굳이 유용한 상황을 꼽자면.. 버전이 여러개 존재하는 외부 패키지를 다룰 때 유용할 수도..
66. 네이티브 메소드 사용 최소
- JNI란 JVM에 있는 C/C++로 작성된 플랫폼 특화 메소드를 호출하는 기술
- 플랫폼 특화 기능을 수행(수요 줄어듦)하거나, 레거시를 다루거나, 성능 개선 목적으로 사용하는 경우가 있는데 성능 개선 목적으론 사용 X
- 이식성을 낮추고, 디버깅 난이도를 올리고, 메모리 오류로부터 자유롭지 못하니 웬만하면 쓰지 말자
67. 최적화는 신중히
- 최적화를 위해 코드 품질을 떨어뜨리는 일 하지 말자
- 애초에 아키텍처와 API를 설계할 때 객체 생성을 줄이거나, 구현체 대신 인터페이스를 참조하는 식으로 시작하자
- 최적화 후 성능 측정 했을 때 기대만큼 안나오는 경우도 많다.. 좋은 설계는 좋은 성능을 수반한다
68. 일반적인 명명 규칙을 따르자
- 패키지 이름 : "."으로 구분, 일반적으로 도메인 주소의 역순. 자바 플랫폼의 경우 java/jakarta로 시작
- 클래스 및 인터페이스, 지역변수 : 대문자로 시작하는 하나 이상의 단어, camel case. 지역 변수의 경우, 약어를 써도 문맥으로 유추 가능하면 약어 사용 가능
- 메서드 및 필드이름 : 소문자로 시작하는 하나 이상의 단어, camel case
- 상수 필드(static final) : 모두 대문자 + 언더바로 공백 구분
- 타입 매개변수 in generic : T, E 등
- getter과 setter, boolean을 return하는 isXXX 메소드, 특정 타입의 객체를 반환하는 toXXX메소드, 여러가지 정적 팩토리 메소드 명명법 등
독후감
염두에 두어야 할 컨벤션들을 조금 알아본 챕터였습니다.
일단 일반적인 명명 규칙에 대해! start.spring.io 사이트에서 스프링부트 프로젝트를 시작하려 패키지와 의존성을 다운받을 때, 패키지 이름이 자동 생성되는 것에 대해서 저건 어떤 뜻을 가지고 있을까, 왜 com.example이라는 패키지 명을 가지고 있을까? 싶었는데 그것 자체로 명명 컨벤션이었다는 것이 책에 적혀있으니 안심이 되었습니다. 생각해보면 naver.com 도메인에 CNAME을 붙일 때 www.naver.com을 사용하게 되는데, 이건 naver.com 하위의 www 도메인을 사용한다는 의미죠? 그렇기 때문에 전체 도메인의 세부사항을 정의한 하위 패키지는 도메인 이름의 역순으로 되어야한다는 것이 이해갑니다.
getter와 setter 네이밍 컨벤션에 대해선, spring mvc에서 기본으로 채택하는 json converter가 객체를 직렬화/역직렬화 할 때 네이밍 컨벤션 기반의 리플렉션을 사용하고 있기 때문에 주의해야하는 것으로 알고 있습니다. 없는 필드에 대해 getter를 작성했다가 에러 뱉은걸 몇 번 봐서ㅎㅎ
리플렉션은 불특정 객체의 메소드와 필드값을 런타임에 이용할 수 있다는 점에서 편의를 제공하지만, 느리고, 복잡하고, 타입 검사가 없어 런타임 오류가 발생할 확률이 높기 때문에 잘 사용하지 않는다고 말할 수 있게 되었습니다. 프레임워크나 라이브러리에서 객체를 생성할 때 생성자를 한정적으로 사용하거나, IDE 등에서 프로젝트 코드의 분석에 쓴다고 하네요? 리플렉션도 그렇고 JNI도 그렇고 뭔가 팬시해보이는 기술이지만 흔하게 볼 수 없는데 역시 쓰이지 않는덴 이유가 있나봅니다.
그 외 부동소수점을 사용하는 컴퓨터 특성상 소수형 타입은 정확하지 않다는 점, 검증된 라이브러리 사용법을 잘 익혀 사용하는게 좋다는 점, 특별한 경우가 아니면 객체를 새로 생성하지 않는 원시 타입을 사용하라는 점 등 확실히 해야할 컨벤션들을 다시 확실히 할 수 있었습니다!
이펙티브 자바 어렵다..............

실무 코드도 똑바로 봐본적 없는 제가 읽기엔 심오하고,, 추상적이고,, 큰 의미를 갖는 것도 많이 없는 것 같아 발췌독을 해보도록 하겠습니다!
57. 지역변수의 범위를 최소화하자
- 지역변수의 범위는 선언시점부터 중괄호가 끝날때까지 -> 선언과 동시에 초기화해서 사용하자
- Iterator패턴을 사용하는 for문의 경우, 반복문을 도는 동안 블록 안에서만 사용할 수 있는 변수를 만들어 범위를 최소화시킨다는 점에서 좋다
- 메소드 기능을 작게 분리하고 하나의 메소드는 하나의 기능을 하게 함으로써 변수의 범위와 기능의 비중을 줄이기도 가능
58. 전통적 for문보단 for-each문을 사용하자
- for문 안에서 사용할 지역변수를 선언하고 선언; 조건; 업데이트; 식으로 구성하면 코드가 지저분해지기 쉽다. Iterator을 사용하고, hasNext()와 next()를 직접 호출하며 필요 이상으로 코드가 길어지기도 한다. next()를 직접 호출하는 코드는 버그를 발견하기 힘들다
- "향상된 for 문", 혹은 "for-each문"을 사용하면 조금 더 깔끔, 유연한 코드가 되는데, for (E e : Iterable<E>) 형식으로 반복문 선언이 가능하다 -> Iterable을 구현한 클래스라면 for-each문을 사용할 수 있다!
- Iterable 객체를 순회하며 modify하거나 삭제를 하는 경우라면 이를 사용할 수 없는데, 이땐 removeIf()를 사용하거나 실제 index값을 통한 반복문을 구성해야한다.
59. 라이브러리를 익히자! (바퀴 재발명 X)
- 라이브러리를 만든 전문가와 이미 해당 라이브러리를 사용한 개발자의 지식을 얻을 수 있고, 핵심 비즈니스 로직에 집중할 수 있고, 라이브러리 개선에 따라 메인 어플리케이션 역시 같이 개선되는 효과를 누릴 수 있는 표준 라이브러리를 애용하자!
- 널리 사용되는 라이브러리, 혹은 코드를 사용하면 다른 개발자들에게도 익숙한 코드가 된다
- 자바랭, 유틸, io 등의 패키지와 그 라이브러리들은 자바 개발자로서 어느정돈 알아두자..
60. 정확한 답이 필요하다면 소수형 타입(float, double)을 피하자
- 이진 데이터를 다루는 컴퓨터의 특성상, 소수를 정확히 표현할 수 없기 때문에 소수 자료형은 어느정도 오차가 날 수밖에 없다 -> 금융 관련이나, 정밀한 계산이 필요한 군사 시스템엔 사용하면 안된다
- 금융 계산엔 BigDecimal을 쓰는 것이 좋은데, primitive 타입을 사용하는 것보다 훨씬 느리고 쓰기 불편하겠지만 소수를 정확하게 저장할 수 없는 컴퓨터 특성상 어쩔 수 없는 부분이다..
61. 박싱된 Primitive Type < Primitive Type
- 박싱 타입은 결국 JVM의 힙에 새로 객체가 생성된다 -> 객체 생성에 비용이 발생 & GC의 동작을 요구하여 성능을 떨어뜨림, 오토박싱 / 언박싱이 일어나는 과정에서 성능 문제가 발생할수도.
- 박싱 타입은 "==" 연산이 제대로 동작하지 않을 가능성이 있고(identity 만족 X) 일반 primitive type이 가지는 default값 없이 null로 초기화될 수도 있음
- 컬렉션의 원소나 키 값으로 사용될 때, 기본값이 null로 설정되어야 할 때(DB값 등)가 아니면 primitive type을 쓰도록 하자
62. 모든 것을 문자열 타입으로 사용하지 말자
- 문자열(String)은 편리하여 오남용되기 쉬움.. IO 결과 데이터에 무턱대로 String을 붙인다거나 하면서 다른 타입의 대체품으로 쓰는걸 경계하자 -> String이 제공하는 기능에만 의존하며 적절한 Object 메소드를 제공하기 힘들어질 확률이 크다
- 모든 type을 문자열로 하는 대신 primitive type, enum, 혼합 타입(ex. 특정 VO)이 어울릴 땐 해당 타입들을 사용하도록 하자!
63. 문자열 연결은 일반적으로 느리다
- String은 불변이기 때문에 "+"연산에 최악의 경우 O(N^2)시간이 걸린다. 성능에 신경쓴다면 문자열 "+"연산을 피하자
- 단순한 + 열거의 경우 JVM이 컴파일 과정동안 최적화를 시키며 자동으로 StringBuilder를 사용하겠지만, for문을 통한 concat일 경우 for문을 도는 횟수만큼 문자열 객체가 새로 생성된다 -> GC 과부하!
64. 객체는 인터페이스를 통해 참조하자
- 특정 객체를 객체의 실제 구현체로써 사용해야할 상황은 new 생성자를 통한 생성시 뿐, 나머지는 인터페이스로 선언하고 참조하자 (ArrayList<Integer> integerList로 선언하지 말고, List<Integer> integerList로 선언하라는 소리)
- 프로그램이 객체지향의 이점을 매우 잘 누리면서 훨씬 유연한 동작을 할 수 있도록 구성할 수 있다 -> 인터페이스를 구현한 다른 구현체로 손쉽게 프로그램의 일부를 다형적으로 동작시킬 수 있다
- 물론, 적합한 인터페이스가 없다면 구체 클래스를 직접 참조하는 수밖에 없는데(ex. VO, 특정 클래스에서만 동작하는 메소드를 사용할 경우), 그렇지 않다면 상위 인터페이스를 추가하거나 어쨌든 최상위 추상 클래스를 통해 참조하는 것이 좋다
65. 인터페이스 > 리플렉션
- 리플렉션은 런타임에 클래스 정보에 접근해서 임의의 클래스 메소드와 필드값 등을 사용할 수 있게 해주는 java api
- 그러나 리플렉션을 이용하면 성능이 떨어지고, 코드가 복잡해지고(리플렉션 api 호출과 각종 예외처리로 인해), Class<?>와 Method 등을 사용하게 되므로 타입 검사의 이점을 누릴 수 없다! 리플렉션을 통한 메소드 호출 대신 인터페이스를 이용하자!!
- IDE, DI 프레임워크, 코드 위빙이 일어나는 라이브러리 등 리플렉션이 꼭 필요한 상황이 아니면 사용 X(그마저도 객체 생성 이외에는 리플렉션 사용 최소화중)
- 굳이 유용한 상황을 꼽자면.. 버전이 여러개 존재하는 외부 패키지를 다룰 때 유용할 수도..
66. 네이티브 메소드 사용 최소
- JNI란 JVM에 있는 C/C++로 작성된 플랫폼 특화 메소드를 호출하는 기술
- 플랫폼 특화 기능을 수행(수요 줄어듦)하거나, 레거시를 다루거나, 성능 개선 목적으로 사용하는 경우가 있는데 성능 개선 목적으론 사용 X
- 이식성을 낮추고, 디버깅 난이도를 올리고, 메모리 오류로부터 자유롭지 못하니 웬만하면 쓰지 말자
67. 최적화는 신중히
- 최적화를 위해 코드 품질을 떨어뜨리는 일 하지 말자
- 애초에 아키텍처와 API를 설계할 때 객체 생성을 줄이거나, 구현체 대신 인터페이스를 참조하는 식으로 시작하자
- 최적화 후 성능 측정 했을 때 기대만큼 안나오는 경우도 많다.. 좋은 설계는 좋은 성능을 수반한다
68. 일반적인 명명 규칙을 따르자
- 패키지 이름 : "."으로 구분, 일반적으로 도메인 주소의 역순. 자바 플랫폼의 경우 java/jakarta로 시작
- 클래스 및 인터페이스, 지역변수 : 대문자로 시작하는 하나 이상의 단어, camel case. 지역 변수의 경우, 약어를 써도 문맥으로 유추 가능하면 약어 사용 가능
- 메서드 및 필드이름 : 소문자로 시작하는 하나 이상의 단어, camel case
- 상수 필드(static final) : 모두 대문자 + 언더바로 공백 구분
- 타입 매개변수 in generic : T, E 등
- getter과 setter, boolean을 return하는 isXXX 메소드, 특정 타입의 객체를 반환하는 toXXX메소드, 여러가지 정적 팩토리 메소드 명명법 등
독후감
염두에 두어야 할 컨벤션들을 조금 알아본 챕터였습니다.
일단 일반적인 명명 규칙에 대해! start.spring.io 사이트에서 스프링부트 프로젝트를 시작하려 패키지와 의존성을 다운받을 때, 패키지 이름이 자동 생성되는 것에 대해서 저건 어떤 뜻을 가지고 있을까, 왜 com.example이라는 패키지 명을 가지고 있을까? 싶었는데 그것 자체로 명명 컨벤션이었다는 것이 책에 적혀있으니 안심이 되었습니다. 생각해보면 naver.com 도메인에 CNAME을 붙일 때 www.naver.com을 사용하게 되는데, 이건 naver.com 하위의 www 도메인을 사용한다는 의미죠? 그렇기 때문에 전체 도메인의 세부사항을 정의한 하위 패키지는 도메인 이름의 역순으로 되어야한다는 것이 이해갑니다.
getter와 setter 네이밍 컨벤션에 대해선, spring mvc에서 기본으로 채택하는 json converter가 객체를 직렬화/역직렬화 할 때 네이밍 컨벤션 기반의 리플렉션을 사용하고 있기 때문에 주의해야하는 것으로 알고 있습니다. 없는 필드에 대해 getter를 작성했다가 에러 뱉은걸 몇 번 봐서ㅎㅎ
리플렉션은 불특정 객체의 메소드와 필드값을 런타임에 이용할 수 있다는 점에서 편의를 제공하지만, 느리고, 복잡하고, 타입 검사가 없어 런타임 오류가 발생할 확률이 높기 때문에 잘 사용하지 않는다고 말할 수 있게 되었습니다. 프레임워크나 라이브러리에서 객체를 생성할 때 생성자를 한정적으로 사용하거나, IDE 등에서 프로젝트 코드의 분석에 쓴다고 하네요? 리플렉션도 그렇고 JNI도 그렇고 뭔가 팬시해보이는 기술이지만 흔하게 볼 수 없는데 역시 쓰이지 않는덴 이유가 있나봅니다.
그 외 부동소수점을 사용하는 컴퓨터 특성상 소수형 타입은 정확하지 않다는 점, 검증된 라이브러리 사용법을 잘 익혀 사용하는게 좋다는 점, 특별한 경우가 아니면 객체를 새로 생성하지 않는 원시 타입을 사용하라는 점 등 확실히 해야할 컨벤션들을 다시 확실히 할 수 있었습니다!