Dev/Java

[effective-java] 일반적인 프로그래밍 원칙

창문닦이 2020. 1. 14. 17:59

[9장. 일반적인 프로그래밍 원칙]


1. 지역변수의 범위를 최소화하라
- 지역변수의 범위를 줄이는 가장 강력한 기법은 가장 처음 쓰일 때 선언하기

  • 지역번수의 범위는 선언된 지점부터 그 지점을 포함한 블록이 끝날 때 까지.

- 거의 모든 지역변수는 선언과 동시에 초기화.

  • try-catch문은 이 규칙에서 예외.

  • 변수를 초기화하는 표현식이 검사예외를 던질 가능성이 있을 경우 try 블록안에서 초기화. 예외가 블록을 넘어 메소드까지 전파할 수 있으므로)

  • 변수 값을 try 블록 바깥에서도 사용해야한다면 try 블록 앞에서 선언.

  • 반복문에서는 반복변수의 범위가 반복문의 몸체 그리고 for 키워드와 몸체 사이의 괄호 안으로 제한된다.

  • 반복 변수의 값을 반복문이 종료된 이후에 써야 하는 상황이 아니라면, while문 보다 for문이 낫다.

- 메서드를 작게 유지하고 한가지 기능에 집중해라

  • 한 메서드에서 여러가지 기능을 처리한다면 그중 한 기능과만 관련된 지역변수라도 다른 기능을 수행하는코드에서 접근할 수 있다. 

  • 해결 방법은 단순히 메소드를 기능별로 쪼개기.


2. 전통적인 for문보다는 for-each문을 사용하라
- for-each문의 정식 이름은 향상된 for 문(enhanced for statement)
- for(Object ob : objects) { //반복 처리}
- 여기서 콜론(:)은 안의(in) 라고 읽으면 된다
- for-each문을 사용할 수 없는 상황 세가지

  • 파격적인 필터링(destructive filtering) : 컬렉션을 순회하면서 선택된 원소를 제거해야 한다면 반자의 remove 메소드를 호출해야 한다. java8부터는 Collection의 removeIf 메소드를 사용해 컬렉션을 명시적으로 순회하는 일을 피할 수 있다.

  • 변형(transforming) : 리스트나 배열을 순회하면서서 그 원소의 값 일부 혹은 전체를 교체해야 한다면 리스트의 반복자나 배열의 인덱스를 사용해야 한다.

  • 병렬 반복(parallel iteration) : 여러 컬렉션을 병렬로 순회해야 한다면 각각의 반복자와 덱스 변수를 사용해 엄격하고 명시적으로 제어해야 한다.

- 세 경우에는 일반적인 for문 사용하되, 문제들을 경계
- for-each문은 컬렉션과 배열, Iterable 인터페이스를 구현한 객체라면 무엇이든 순회할 수 있다. 

  • Iterable인터페이스는 메서드가 단 하나 뿐이다

  • public interface Iterable{//이 객체의 원소들을 순회하는 반복자를 반환한다. Iterator iterator(); }

  • 원소들의 묶음을 표현하는 타입을 작성해야 한다면 Iterable 구현체를 만들어보자! for-each문을 쓸 때 유용함을 느낄 수 있을 것이다.

- 전통적인 for문과 비교했을 때 for-each문은 명료, 유연, 버그예방이라는 장점과 성능 저하도 없다. 가능한 모든 곳에서 for문이 아닌 for-each문을 사용하자 


3. 라이브러리를 익히고 사용하라
- 표준 라이브러리를 사용하면 그 코드를 작성한 전문가의 지식과 여러분보다 앞서 사용한 다른 프로그래머들의 경험을 활용할 수 있다.

  • Random 클래스보단 ThreadLocalRandom 

- 핵심적인 일과 크게 관련 없는 문제를 해결하느라 시간을 허비하지 않아도 된다.
- 따로 노력하지 않아도 성능이 지속해서 개선된다.

  • 표준 라이브러리 제작자들은 더 나은 방법을 꾸준히 찾으므로 !!

- 기능이 점점 많아진다.

  • 부족한 부분이 있다면 다음 릴리스에 해당 기능이 추가되기도 한다.

- 내가 작성한 코드가 많은 사람에게 낯익은 코드가 된다.

  • 가독성이 높아지며 유지보수하기 좋고 재활용하기 쉬운 코드가 된다.

- 메이저 릴리스마다 수많은 기능들이 라이브러리에 추가된다. 모든 API 문서를 공부하지 못하더라도 자바 프로그래머라면 적어도 java.lang, java.util, java.io와 그 하위 패키지에들에는 익숙해져야 한다.

  • API를 확인해보자 : 컬렉션 프레임워크, java.util.concurrent 동시성 기능

- 라이브러리에서 어떤 영역의 기능을 제공하는지 살펴보고 원하는 기능이 없다고 판단되면 대안을 사용하자. 

  • 자바 표준 라이브러리 -> 고품질의 서드파티 라이브러리 -> 직접구현

4. 정확한 답이 필요하다면 float와 double은 피해라
- float과 double 타입은 금융 관련 계산과는 맞지 않는다.

  • 과학과 공학 계산용으로 설계. 이진 부동소수점연산에 쓰인다. 

  • 넓은 범위의 수를 빠르게 밀한 근사치로 계싼하도록 설계되었다.

- 금융 계산에는 BigDecimal, int , long 을 사용해야 한다. 
- BigDecimal 

  • 단점 : 기본 타입보다 쓰기가 훨씬 불편하고, 훨씬 느리다.  코딩 시 불편함과 성능 저하

  • 장점 : 8가지 반올림 모드를 이용해서 반올림을 완벽히 제어할 수 있다. 법으로 정해진 반올림을 수행해야 하는 비즈니스 계산에서 용이함.

- int , long 

  • 단점 : 다룰 수 있는 값의 크기가 제한되고 소수점을 직접 관리해야 한다.

  • 장점 : 성능이 중요하고 소수점을 직접 추적 가능, 숫자가 너무 크지 않다면 사용.

  • int는 9자리 10진수, long 18자리 십진수일 때 사용하고 이를 넘으면 BigDecimal을 사용한다.


5. 박싱된 기본타입보다는 기본 타입을 사용하라
- 자바의 데이터 타입은 기본형(primitive type), 참조형(reference type)

  • 기본형 : int, long, boolean,

  • 참조형 : 클래스, Integer, Double, Boolean

- 기본형와 박싱된 기본형의 차이점

  • 기본형은 값만 가지고 있으나, 박싱된 기본 타입은 값에더해 식별성이란 속성을 가진다.

  • 기본형의 값은 언제나 유효하나, 박싱된 기본 타입은 유효하지 않은 값(null)을 가질 수 있다.

  • 기본타입이 박싱된 기본타입보다 시간과 메모리 사용면에서 더 효율적이다.

- (같은 객체를 비교하는게 아니라면)박싱된 기본타입에 == 연산자를 사용하면 오류가 발생한다.
- 기본 타입과 박싱된 기본 타입을 혼용한 연산에서는 박싱된 기본 타입의 박싱이 자동으로 풀린다.
- 박싱된 기본 타입을 써야하는 경우

  • 컬렉션의 원소, 키, 값으로 사용(컬렉션은 기본 타입을 담을 수 없기 때문)

  • 매개변수화 타입이나 매개변수화 메서드의 타입 매개변수로는 박싱된 기본 타입 사용.(자바 언어가 타입 매개변수로 기본 타입을 지원하지 않기 때문)

  • 리플렉션을 통해 메서드를 호출할 때도 박싱된 기본 타입 사용

- 오토박싱이 박싱된 기본 타입을 사용할때의 번거로움을 줄여주지만 위혐을 없애주지는 않는다.
- 같은 연산에서 기본타입과 박싱된 기본타입을 혼용하면 언박싱이 이루어지며  NPE를 던질 수 있다.

6. 다른 타입이 적절하다면 문자열 사용을 피하라
- 문자열은 다른 값 타입을 대신하기에 적합하지 않다
- 문자열은 열거타입을 대신하기에 적합하지 않다
- 문자열은 혼합 타입을 대신하기에 적합하지 않다
- 문자열은 권한을 표현하기에 적합하지 않다.

7. 문자열 연결은 느리니 주의하라
- 문자열 연결 연산자로 문자열 n개를 잇는 시간은 n^2에 비례한다.

  • 문자열은 불변이라서 연결해야 할 경우 양쪽의 내용을 모두 복사해야 하므로 성능 저하로 이어지는 것

- 많은 문자를 연결할 때는 문자열 연결 연산자(+)를 피하자
- String 대신 StringBuilder를 사용하자. append 메소드 활용.
- 문자 배열을 사용하거나 문자열을 연결하지 않고 하나씩 처리하는 방법도 있다.

8. 객체는 인터페이스르 사용해 참조하라
- 적합한 인터페이스만 있다면 매개변수 뿐만 아니라 반환갑, 변수, 필드를 전부 인터페이스 타입으로 선언해라

  • 객체의 실체 클래스를 사용해야 할 경우는 오직 생성자로 생성할 때 뿐이다.

  • 인터페이스를 타입으로 사용하는 습관을 길러두면 프로그램은 훨씬 유연해질 것이다.

- 적합한 인터페이스가 없다면 당연히 클래스로 참조해야 한다.

  • String과 BigInteger 같은 값 클래스. 이런 클래스를 여러가지로 구현될 수 있다고 생각하고 설계하는 일은 거의 없다.

  • 클래스 기반으로 작성된 프레임워크가 제공하는 객체. OutputStream 등 java.io nbsp;패키지의 여러 클래스가 이 부류에 속한다.

  • 인터페이스에 없는 특별한 메서드를 제공하는 클래스. PriorityQueue클래스는 Queue 인터페이스에는 없는 comparator 메서드를 제공. 인터페이스 말고 클래스를 쓰는 경우는 이 메소드를 꼭 사용해야 하는 경우로 최소화.

- 적합한 인터페이스가 없다면 클래스의 계층 구조 중 필요한 기능을 만족하는 가장 덜 구체적인(상위의) 클래스를 타입으로 사용하자

9. 리플렉션보다는 인터페이스를 사용하라
- 리플렉션 기능(java.lang.reflect)을 이용하면 프로그램에서 임의의 클래스에 접근 가능
- 리플렉션이란? 
- 단점

  • 컴파일타임 타입 검사가 주는 이점을 하나도 누릴 수 없다.

  • 리플렉션을 이용하면 코드가 지저분하고 장황해진다.

  • 성능이 떨어진다.

- 리플렉션은 아주 제한된 형태로만 사용해야 그 단점을 피하고 이점만 취할 수 있다.
- 리플렉션은 인스턴스 생성에만 쓰고, 이렇게 만든 인스턴스는 인터페이스나 상위 클래스로 참조해 사용하자.

 

10. 네이티브 메서드는 신중히 사용하라
- 자바 네이티브 인터페이스(Java Native Interface, JNI) 는 자바 프로그램이 네이티브 메서드를 호출하는 기술.
- 네이티브 메서드 : C나 C++같은 네이티브 프로그래밍 언어로 작성한 메서드.
- 주요 쓰임 

  • 레지스트리같은 플랫폼 특화 기능을 사용한다.

  • 네이티브 코드로 작성된 기존 라이브러리를 사용한다.

  • 성능 개선을 목적으로 결정적인 영향을 주는 영역만 네이티브 언어로 작성

- 네이티브 메서드를 사용해서 성능을 개선하는 일은 많지 않다. 네이티브  코드는 최소한만 사용하고 철처히 테스트해라. 네이티브 코드 안에 있는 단하나의 버그가 애플리케이션 젅체를 훼손할 수 있다..

11. 최적화는 신중히 하라
- 최적화 격언

  • 그 어떤 핑꼐보다 효율성이라는 이름 아래 행해진 컴퓨팅 죄악이 더 많다.(심지어 효율을 높이지도 못하면서)

  • 자그마한 효율성은 모두 잊자. 섣부른 최적화가 만악의 근원이다.

  • 최적화를 할 때는 두 규칙을 따르라. 첫 번째, 하지마라. 두 번째, 아직 하지마라. 다시말해 완전히 명백하고 최적화되지 않은 해법을 찾을때까지는 하지마라.

- 빠른 프로그램 보다는 좋은 프로그램을 작성하라
- 성능을 제한하는 설계를 피하라
- API를 설계할 때 성능에 주는 영향을 고려하라 
- 성능을 위해 API를 왜곡하는 건 매우 안좋은 생각이다.

12. 일반적으로 통용되는 명명 규칙을 따르라

 

'Dev > Java' 카테고리의 다른 글

[java] jmeter 성능 테스트  (0) 2020.05.20
[effective-java] 동시성  (0) 2020.01.15
[java] CSV 파싱하기  (0) 2019.12.13
[java] Logback 과 Maven  (0) 2019.10.04
[java] JVM, JRE, JDK, 자바 메모리 구조  (0) 2019.09.28