본문 바로가기

전체 글

(214)
[아이템 66] 네이티브 메서드는 신중히 사용하라 자바 네이티브 인터페이스(Java Native Interface, JNI)란? 자바 프로그램이 네이티브 메서드를 호출하는 기술 네이티브 메서드란 C나 C++ 같은 네이티브 프로그래밍 언어로 작성한 메서드 전통적으로 네이티브 메서드의 주요 쓰임은 다음 세 가지다. 첫 번째, 레지스트리 같은 플랫폼 특화 기능을 사용한다. 두 번째, 네이티브 코드로 작성된 기존 라이브러리를 사용한다. e.g.) 레거시 데이터를 사용하는 레거시 라이브러리 세 번째, 성능 개선을 목적으로 성능에 결정적인 영향을 주는 영역만 따로 네이티브 언어로 작성한다. [Why] 성능을 개선할 목적으로 네이티브 메서드를 사용하는 것은 거의 권장하지 않는다. 자바 초기 시절(자바 3 전)이라면 이야기가 다르겠지만, JVM은 그동안 엄청난 속도로..
[아이템 65] 리플렉션보다는 인터페이스를 사용하라 리플렉션이란? 리플렉션 기능(java.lang.reflect)을 이용하면 프로그램에서 임의의 클래스에 접근할 수 있다. Class 객체가 주어지면 그 클래스의 생성자, 메서드, 필드에 해당하는 Constructor, Method, Field 인스턴스를 가져올 수 있다. Constructor, Method, Field 인스턴스들로는 그 클래스의 멤버 이름, 필드 타입, 메서드 시그니처 등을 가져올 수 있다. Constructor, Method, Field 인스턴스를 이용해 각각에 연결된 실제 생성자, 메서드, 필드를 조작할 수도 있다. 단점 컴파일타임 타입 검사가 주는 이점을 하나도 누릴 수 없다. 예외 검사의 이점도 누릴 수 없다. 리플렉션 기능을 써서 존재하지 않는(접근할 수 없는) 메서드를 호출하려 ..
[아이템 64] 객체는 인터페이스를 사용해 참조하라 [Why] 인터페이스를 타입으로 사용하는 습관을 길러두면 프로그램이 훨씬 유연해질 것이다. 나중에 구현 클래스를 교체하고자 한다면 그저 새 클래스의 생성자(혹은 다른 정적 팩터리)를 호출해주기만 하면 된다. 변수를 구현 타입으로 선언하면 자칫 프로그램이 컴파일되지 않을 수도 있다. 클라이언트에서 교체 전 구현 클래스에서만 제공하는 메서드를 사용했거나, 교체 전 구현 클래스를 사용해야 하는 다른 메서드에 그 인스턴스를 넘겼다면 새로운 코드에서는 컴파일되지 않을 것이다. [When] 인터페이스에는 없는 특별한 메서드를 제공하는 클래스는 클래스로 참조해야 한다. e.g.) PriorityQueue 클래스는 Queue 인터페이스에는 없는 comparator 메서드를 제공한다. [How] 적합한 인터페이스만 있다..
[아이템 63] 문자열 연결은 느리니 주의하라 [Why] 문자열 연결 연산자로 문자열 n개를 잇는 시간은 n^2에 비례한다. 문자열은 불변(아이템17)이라서 두 문자열을 연결할 경우 양쪽의 내용을 모두 복사해야하므로 성능 저하는 피할 수 없는 결과다. [How] 성능을 포기하고 싶지 않다면 String 대신 StringBuilder를 사용하자. StringBuilder를 전체 결과를 담기에 충분한 크기로 초기화하면 조금 더 빠르다.
[아이템 62] 다른 타입이 적절하다면 문자열 사용을 피하라 [배경] 문자열은 워낙 흔하고 자바가 또 잘 지원해주어서 원래 의도하지 않은 용도로도 쓰이는 경향이 있다. [Why] 문자열은 다른 값 타입을 대신하기에 적합하지 않다. 많은 사림이 파일, 네트워크, 키보드 입력으로부터 데이터를 받을 때 주로 문자열을 사용한다. 하지만 입력받을 데이터가 진짜 문자열일 때만 그렇게 하는 게 좋다. 문자열은 열거 타입을 대신하기에 적합하지 않다. (아이템34) 문자열은 혼합 타입을 대신하기에 적합하지 않다. String compoundKey = className + "#" + i.next(); 이는 단점이 많은 방식이다. 두 요소를 구분해주는 문자 #이 두 요소 중 하나에서 쓰였다면 혼란스러운 결과를 초래한다. 각 요소를 개별로 접근하려면 문자열을 파싱해야 해서 느리고, 귀..
[아이템 61] 박싱된 기본 타입보다는 기본 타입을 사용하라 [배경] 기본 타입과 박싱된 기본 타입의 주된 차이는 크게 세 가지다. 첫 번째, 기본 타입은 값만 가지고 있으나, 박싱된 기본 타입은 값에 더해 식별성(identity)이란 속성을 갖는다. 두 번째, 기본 타입의 값은 언제나 유효하나, 박싱된 기본 타입은 유효하지 않은 값, 즉 null을 가질 수 있다. 세 번째, 기본 타입이 박싱된 기본 타입보다 시간과 메모리 사용면에서 더 효율적이다. [Why] (같은 객체를 비교하는 게 아니라면) 박싱된 기본 타입에 == 연산자를 사용하면 오류가 일어난다. naturalOrder.compare(new Integer(42), new Integer(42)) 첫 번째 검사인 (i < j)는 잘 작동하지만, 두 번째 검사인 (i == j)에서는 두 '객체 참조'의 식별성..
[아이템 60] 정확한 답이 필요하다면 float와 double은 피하라 [예시] 주머니에 1.03달러가 있었는데 그중 42센트를 썼다고 해보자. System.out.println(1.03 - 0.42); 안타깝게도 이 코드는 0.6100000000000001을 출력한다. 주머니에 1달러가 있었는데 10센트짜리 사탕 9개를 샀다고 해보자. System.out.println(1.00 - 9 * 0.10); 이 코드는 0.09999999999999998을 출력한다. 주머니에는 1달러가 있고, 선반에 10센트, 20센트, 30센트, ... 1달러짜리의 맛있는 사탕이 놓여 있다고 해보자. 10센트짜리부터 하나씩, 살 수 있을 때까지 사보자. 사탕을 몇 개나 살 수 있고, 잔돈은 얼마가 남을까? 결과값을 출력하기 전에 반올림하면 해결되리라 생각할지 모르지만, 반올림을 해도 틀린 답이 ..
[아이템 59] 라이브러리를 익히고 사용하라 [Why] "바퀴를 다시 발명하지 마라." static Random rnd = new Random(); static int random(int n) { return Math.abs(rnd.nextInt()) % n; } 괜찮은 듯 보여도 문제를 세 가지나 내포하고 있다. 첫 번째, n이 그리 크지 않은 2의 제곱수라면 얼마 지나지 않아 같은 수열이 반복된다. 두 번째, n이 2의 제곱수가 아니라면 몇몇 숫자가 평균적으로 더 자주 반환된다. n값이 크면 이 현상은 더 두드러진다. 세 번째, 지정한 범위 '바깥'의 수가 종종 튀어나올 수 있다. rnd.nextInt()가 반환한 값을 Math.abs를 이용해 음수가 아닌 정수로 매핑하기 때문이다. nextInt()가 Integer.MIN_VALUE를 반환하면..