- 무작위의 Bollean, Int, Long, Float, Double 등의 난수를 얻는 데 사용
- 값의 범위는 0~명시한 수
※ Math.random()과의 차이점 : Math는 0.0~1.0 사이의 Double형 난수를 얻는데만 사용
예시) 흔하지만 문제가 심각한 코드!
static Random rnd = new Random();
static int random(int n) {
return Math.abs(rnd.nextInt()) % n;
}
위의 예시에는 총 3가지의 문제를 가지고 있음
1) n이 그리 크지 않은 2의 제곱수라면 얼마 지나지 않아 같은 수열이 반복
2) n이 2의 제곱수가 아니라면 몇몇 숫자가 평균적으로 더 자주 반환, n값이 크면 이 현상이 더 두드러짐
public static void main(String[] args) {
int n = 2 * (Integer.MAX_VALUE / 3);
int low = 0;
for (int i = 0; i < 1000000; i++)
if (random(n) < n/2)
low++;
System.out.println(low);
}
- 해당 코드는 백만 개의 수를 무작위로 생성하고 중간 값보다 작은 값의 개수를 얻는 코드이다.
- 백만 개를 무작위로 생성하고 중간 값을 기준으로 반을 나누기 때문에 상식적으로 50만개 정도가 나온다고 생각하는 것이 일반적이지만, 필자는 666364라는 값을 얻음
> 약 2/3가 중간보다 낮음
3) 지정된 범위 '바깥'의 수가 종종 튀어나올 수 있음
- Random.nextInt()가 반환한 값을 Math.abs를 이용해서 절대 값으로 변경하는데, nextInt()가 Integer.MIN_VALUE를 반환하면 Math.abs를 거쳐도 -2147483648이라는 값을 반환하게 되고, % n은 음수를 반환함(n이 2의 제곱이 아닐 경우)
※ Math.abs를 거쳐도 음수가 나오는 이유 : Java Int형의 유형은 32비트이기 때문에 저장할 수 있는 가장 큰 정수가 2147483647이기 때문(최대 값은 최소 값에 연결)
이러한 문제를 해결하기 위해 Random.nextInt(int)를 이용할 수 있음
표준 라이브러리를 사용할 때의 이점
1. 표준 라이브러리를 사용하면 전문가의 지식과 경험을 활용할 수 있음
자바 7부터는 Random을 사용하지 말고 ThreadLocalRandom으로 대체하는 것이 좋음
- Random보다 더 고품질의 무작위 수 생성 로직, 빠른 속도
- 포크·조인 풀이나 병렬 스트림에서는 SplittableRandom을 사용
※포크·조인 풀 : 큰 업무를 작은 업무로 나누어 배분해서, 일을 처리한 후 취합
2. 핵심적인 일을 하는데 시간을 더 투자할 수 있음
프로그래머들은 Deep한 곳 보다는 애플리케이선(서비스) 기능 개발에 집중
3. 지속적인 성능 개선
- 업계 표준 벤치마크를 사용해 성능을 확인하기 때문에, 표준 라이브러리 제작자들은 꾸준히 성능을 개선
- Java 플랫폼 라이브러리의 많은 부분이 수 년에 걸쳐 지속해서 다시 작성
4. 많아지는 기능
라이브러리에 부족하거나 필요한 부분이 있다면 개발자 커뮤니티에서 의견 수렴 후 다음 릴리즈에 해당 기능이 추가하는 방향
5. 자신이 작성한 코드가 많은 사람들이 이해가능
표준 라이브러리를 사용함으로써 다른 개발자들이 더 읽기 좋고 유지보수하기도 좋아지며, 재활용하기 쉬운 코드가 됨
But, 아직도 많은 프로그래머가 라이브러리의 존재 또는 기능을 모르기 때문에 직접 구현해서 사용 중
> 라이브러리가 방대하기 때문에 모든 문서를 공부하기는 현실적으로 힘듦
> 최소 java.lang, java.util, java.io와 그 하위 패키지들에는 익숙해져야 함
추가적으로 item 45~48의 컬렉션 프레임워크와 스트림 라이브러리 등은 알아두면 좋음
해당 부분에서 가장 중요한 것은 라이브러리를 일단 사용해보고 어떠한 기능을 제공하는지 파악
item 66. 네이티브 메서드는 신중히 사용하라
※ 네이티브 인터페이스란?
- Java 프로그램이 네이티브 메서드를 호출하는 기술
※ 네이티브 메서드란?
- C, C++과 같은 네이티브 프로그래밍 언어로 구현한 메서드
네이티브 메서드의 주요 쓰임
■ 레지스트리와 같은 플랫폼 특화 기능을 사용
- Java의 변화에 따라 OS와 같은 하부 플랫폼의 기능들을 점차 흡수하고 있음
> 플랫폼 독립성↑
> 네이티브 메서드 사용 영역↓
- Java9는 process API를 추가하여 OS에 접근하도록 함
■ 네이티브 코드로 작성된 기존 라이브러리를 사용
- 레거시 데이터를 사용하는 레거시 라이브러리
* 레거시 데이터 : 프로그래밍언어, 플랫폼, 기술 등에 있어서 과거로부터 물려 내려온 데이터
- 대체할 만한 Java 라이브러리가 없는 네이티브 라이브러리를 사용할 때 네이티브 메서드 사용
■성능 개선을 목적으로 성능에 결정적인 영향을 주는 영역만 따로 네이티브 언어로 작성
네이티브 메서드는 성능 개선을 목적으로 사용하는 것을 지양
■ Java 3 전을 제외하고, JVM의 발전 속도는 나날이 증가
■대부분의 작업에서 현재의 Java는 다른 플랫폼과의 성능에서 차이가 없음
■ 네이티브 라이브러리는 GNU 다중 정밀 연산 라이브러리(GMP)를 내세우며 지속적인 개선 작업 몰두
> 고성능의 다중 정밀 연산일 경우 네이티브 메서드를 통해 GMP를 사용해도 됨
네이티브 메서드의 단점
■ 안정성이 낮으므로, 애플리케이션도 메모리 훼손 오류가 발생할 수 있음
■ Java에 비해 플랫폼의 영향을 많이 받기 때문에 이식성이 낮음
■ 디버깅이 어려움
■ 속도가 느려질 위험 존재
■GC(가비지 컬렉터)가 네이티브 메모리는 자동 회수와 추적이 불가능
■ 코드적으로 Java와 네이티브의 이동에 따라 추가 비용 발생
■ 접착 코드(glue code) 사용으로 인한 가독성 저하
* 접착 코드 : 호환되지 않는 프로그램이나 SW 구성 요소를 통합하는 코드
★ 네이티브 메서드는 성능 개선을 기대할 수 없으며, 최소한만 사용하고 크리티컬한 오류의 발생을 방지하기 위해 지속적이고 철저한 테스트와 검증이 필요
item 68. 일반적으로 통용되는 명명 규칙을 따르라
※ 자바의 명명 규칙은 철자와 문법의 두 범주로 나눌 수 있음
철자 규칙
패키지, 클래스, 인터페이스, 메서드, 필드, 타입 변수의 이름을 다룸
* 이러한 규칙을 지키지 않은 API는 사용, 유지보수가 어렵고 다른 프로그래머들이 읽기가 힘드며 오해로 인한 오류가 발생할 수 있기 때문에 규칙들은 반드시 지켜야 함
패키지(Package)
■ 패키지와 모듈 이름은 .(점)으로 구분하여 계층적으로 지음
■ 요소들은 소문자 알파벳이나 숫자로 이루어짐
■ 상용 패키지라면 인터넷 도메인 이름을 역순으로 사용
ex) edu.com / com.google
■ 예외적으로 표준 라이브러리와 선택적 패키지들은 각각 java, javax로 시작
■ 패키지를 설명하는 요소로 이루어지며, 8자 이하의 짧은 단어로 사용
ex) utilities -> util
ex) awt와 같이 여러 단어로 구성되었을 때, 각 단어의 첫 글자를 따서 사용
■ 많은 기능을 제공하는 경우, 계층을 나누어 더 많은 요소로 구성
ex) java.util은 java.util.concurrent.atomic과 같은 수많은 하위 패키지를 내제
클래스, 인터페이스
■ 하나 이상의 단어로 이루어지며, 각 단어는 대문자로 시작
ex) List, FutherTask
■ 여러 단어의 첫 글자만 딴 약자나 max와 min과 같이 통용되는 줄임말을 제외하고 단어를 줄이면 안됨
> 전체를 대문자로 할 경우 직관적이지 않음
메서드, 필드
■ 첫 글자를 소문자로 쓴다는 점을 제외하고 클래스 명명 규칙과 동일
ex) ensureCapacity
■ 첫 단어가 약자라면 단어 전체를 소문자로 작성
■ 예외적으로 상수 필드는 모두 대문자로 표현하며 단어와 단어 사이는 밑줄로 표현
ex) VALUE, NEGATIVE_INFINITY
> 상수 필드는 값이 변하지 않는 static final 필드를 뜻함
* static final 필드 : 한 번 초기 값이 지정되면 변경이 불가하고, 객체마다 저장되지 않으며 클래스에만 포함
- 인스턴드가 만들어질 때마다 새로 메모리를 잡고 초기화하지 않고, 클래스단에서 잡아서 하나의 메모리 공간 사용
■ 지역변수도 비슷한 명명 규칙이 적용되지만, 약어를 써도 문맥을 통해 유추할 수 있기 때문에 사용 가능