클래스의 인스턴스를 얻는 전통적인 수단은 public 생성자 이다.정적 팩터리 메서드를 이용한 인스턴스 생성 방식은 다음과 같은 장점이 있다.BigInteger(int, int, Random) 과 BigInteger.probablePrime 중 어떤 것이 소수를 반환한
점층적 생성자 패턴으로 인스턴스를 만들려면 사용자가 사용시 헷갈릴 뿐만 아니라, 매개변수가 점점 많아질수록 클라이언트 코드를 읽거나 사용하기가 점점 더 어려워진다.Setter를 이용한 Java Beans 패턴으로 대체할 수 있겠지만, 객체 완성 전까지 일관성이 유지되지
해당 클래스가 싱글턴임이 API 명백히 드러난다.간결하다.API 를 바꾸지 않고도 싱글턴이 아니게 변경할 수 있다.팩터리 메서드에서 INSTANCE 를 반환할 때 새로운 객체를 생성해서 반환하는 방식으로 바꾸면 된다.정적 팩터리를 제너릭 팩터리로 바꿀 수 있다 (대부분
아주 가끔, 정적 필드와 정적 메서드로만 구성된 클래스를 만들고 싶은 경우가 있을 것이다.물론, 객체 지향적인 사고에는 맞지 않지만 나름의 쓰임새가 있다.객체 생성자를 명시하지 않으면 컴파일러가 자동으로 public 의 매개변수를 받지 않는 기본 생성자를 만들어주기 때
클래스들이 자원에 의존하는 경우가 있다.SpellCheck 가 Dictionary 라는 유틸리티 클래스를 사용한다고 가정해보자위의 두 방법 모두 확장에 유연하지 않고 테스트하기 어렵다.사전에는 다양한 종류가 있을 것이다. (영어 사전, 한국어 사전, 등등..)dicti
GC 에 너무 의존한 나머지 메모리 관리를 하지 않는 경우가 생긴다.위의 Stack 의 pop() 메서드에서는 참조 해제(null 처리)를 해주지 않아서 메모리 누수가 발생한다.각 원소의 참조가 더 이상 필요없는 시점에서는 참조 해제가 필요하다.위와 같이 null 처리
메모리 관리에 대해서 언급했으니 이번에는 객체 소멸자에 대해서 알아보자.예측할 수 없고, 상황에 따라 위험할 수 있다.finalizer의 대안이지만 여전히 예측할 수 없고, 느리고, 일반적으로 불필요하다.즉시 실행하지 않는다.실행되기까지 얼마가 걸릴지 알 수 없다.실행
try-finally 는 전통적인 자원 회수 방식이다. 예외가 발생하거나 메서드에서 반환되는 경우까지도포함되어서 사용되었다.다만, 자원이 둘 이상이 되면 지저분해 진다는 단점이 있다.예외는 try 블록과 finally 블록 모두에서 발생할 수 있다. 물리적인 문제를 이
equals 를 재정의할 때는 hashCode도 재정의 해야한다. HashMap이나 HashSet 같은 컬렉션의 원소로 인스턴스를 사용할 때 문제를 일으키게 되기 때문이다.다음은 Object 명세서에서 발최한 규약이다.equals 비교에 사용되는 정보가 변경되지 않았다
기본 toString 메서드는 클래스\_이름@16진수로\_ 표시한\_해시코드를 반환할 뿐, 우리가 필요한 정보를 반환하는 경우는 거의 없다.즉, toString 을 재정의하지 않는다면 로그에 쓸모없는 메세지만 남게된다. toString은 객체가 가진 주요 정보를 모두
x.clone() != x x.clone().getclass() == x.getclass() \-> 참이지만, 필수는 아니다.x.clone().equals(x) cloneable 를 구현한 클래스에서 clone 을 호출하면 그 객체의 필드를 하나하나 복사한 객체를 반환
compareTo는 단순 동치성 비교에 순서까지 비교할 수 있고 제네릭하다. Comparable을 구현했다는 건 클래스의 인스턴스들에게 순서가 있음을 뜻한다.compareTo는 객체와 주어진 객체의 순서를 비교한다. 객체가 주어진 객체보다 작다면 음의 정수, 크다면 양
캡슐화가 얼마나 잘되었는지노출되는 API와 실제 구현이 얼마나 잘 분리되었는지메시지를 주고받는 두 컴포넌트가 서로의 내부 동작을 신경쓰지 않는지시스템 개발 속도를 높인다. 여러 컴포넌트를 병렬로 개발할 수 있기 때문이다.시스템 관리 비용을 낮춘다. 각 컴포넌트를 더 빨
API 를 수정하지 않고 내부 표현을 바꿀 수 있다불변식을 보장할 수 있다외부에서 필드에 접근할 때 부수 작업을 수행할 수 있다public 클래스의 필드가 불변(const, final)이라면 직접 노출할 때의 단점이 조금은 줄어들지만 좋은 생각은 아니다.API를 수정하
인스턴스의 내부 값을 수정할 수 없는 클래스로 불변 인스턴스에 간직된 정보는 고정되어 객체가 파괴되는 순간까지 절대 달라지지 않는다.객체의 상태를 변경하는 메서드를 제공하지 않는다.클래스를 확장할 수 없도록 한다. 즉, 상속하지 못하도록 한다.모든 필드를 final 로
컴포지션?private 필드를 통해 기존 클래스가 새로운 클래스의 구성요소(인스턴스)로 쓰이는 것새로운 클래스에 기존 클래스의 영향이 적어 기존 클래스가 변경되어도 안전상속은 캡슐화를 깨뜨린다.상위 클래스가 어떻게 구현되느냐에 따라 하위 클래스의 동작에 이상이 생기기
상속용 클래스는 재정의할 수 있는 메서드들을 내부적으로 어떻게 이용하는지 문서로 남겨야 한다.내부 매커니즘을 문서로 남기는 것만이 상속을 위한 설계의 전부는 아니다. 효율적인 하위 클래스를 큰 어려움 없이 만들 수 있게 하려면 클래스의 내부 동작 과정 중 끼어들 수 있
기존 클래스에 새로운 인터페이스를 구현하려면 implements 구문으로 추가하고 인터페이스가 제공하는 메서드를 구현하기만 하면 된다.믹스인?클래스가 구현할 수 있는 타입으로, 믹스인을 구현한 클래스에 원래 주된 타입 외에도 특정 선택적 행위를 제공한다고 선언하는 효과
JAVA 8의 Collection 인터페이스에 추가된 removeIf 메소드는 true 를 반환하는 모든 원소를 제거한다.디폴트 메서드는 기존 구현체에 런타입 오류를 일으킬 수 있다. 그렇기 때문에 기존의 인터페이스에 디폴트 메서드를 추가하는 일은 꼭 필요한 경우가 아
인터페이스는 자신을 구현한 클래스의 인스턴스를 참조할 수 있는 타입 역할을 한다.클래스가 어떤 인터페이스를 구현한다는 것은 자신의 인스턴스로 무엇을 할 수 있는지를 클라이언트에게 이야기해주는 것이다.이 지침에 맞지 않는 예로 상수 인터페이스가 있다. 상수 인터페이스?메
태그 달린 클래스?해당 클래스가 어떤 타입인지를 나타내는 필드를 포함하는 클래스태그 달린 클래스는 단점이 한가득이다. 장황하고 오류를 내기 쉽고 비효율적이다.아래의 예시를 보자열거 타입, 태그 필드, switch 문 등 쓸데없는 코드가 너무 많다여러 구현이 한 클래스에
정적 멤버 클래스비정적 멤버 클래스익명 클래스지역 클래스이 중 첫번째를 제외한 나머지는 inner class 에 해당한다.정적 멤버 클래스는 바깥 클래스의 private 멤버에 접근할 수 있다. 이 점만 제외한다면 나머지 일반 클래스와 똑같다.정적 멤버 클래스와 비정적
당연한 이야기지만 한 소스 파일에는 하나의 톱레벨 클래스가 들어가야 한다. 톱레벨 클래스를 여러 개 선언해도 자바 컴파일러는 불평하지 않는다. 하지만 그 중 어떤 것을 사용할지는 어느 소스 파일을 먼저 컴파일 하느냐에 달려 있다. 그렇기 때문에 톱레벨 클래스들은 서로
로 타입은 제네릭이 도입되기 이전 코드와의 호환성을 위해서 제공된다. 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않을 때를 말한다.e.g) List<E> -> List로 타입에는 다른 타입의 매개변수가 컬렉션에 들어갈 때 컴파일 시점이 아닌 런타임 시점에 에러
할 수 있는 한 모든 비검사 경고를 제거하라. 모두 제거한다면 타입 안전성이 보장된다. 경고를 제거할 수 없지만 타입 안전하다고 확신할 수 있다면 @SuppressWarnings("unchecked") 어노테이션을 달아 경고를 숨기자. 단 반드시 타입 안전에 대해 검증
배열과 제네릭타입에는 중요한 차이가 있다. 첫째는 배열은 공변이고 제네릭은 비공변이라는 것이다. 즉, Sub\[] 와 Super\[] 이 존재하면 Sub\[] 는 배열 Super\[] 의 하위타입이 된다. 반면 서로 다른 타입 Type1, Type2 가 있을 때 Lis
만약 클라이언트가 위 Stack 클래스를 사용하려면 Stack 에서 꺼낸 객체를 형변환해서 사용을 해야한다. 이러면 런타임 오류가 날 위험이 있다.일반 클래스를 제네릭 클래스로 만드는 첫 단계는 클래스 선언에 타입 매개변수를 추가하는 일이다.타입 이름으로 보통 E를 사
클라이언트에서 입력 매개변수와 반환값을 명시적으로 형변환해야 하는 메소드보다 제네릭 메소드가 더 안전하고 사용하기도 쉽다. 타입과 마찬가지로, 메소드도 형변환 없이 사용할 수 있는 편이 좋으며 많은 경우 그렇게 하려면 제네릭 메소드가 되어야 한다.매개변수화 타입을 받는
매개변수화 타입은 기본적으로 불공변이다. 불공변 방식보다 유연한 무언가가 필요한 경우가 있다. Stack 클래스에 pushAll 메소드를 추가해야 한다고 가정해보자.위의 경우 Integer 는 Number의 하위 타입이기 때문에 논리적으로 잘 동작해야 할 것 같지만 오
들어가며 가변인수(varargs)는 메서드에 넘기는 인수의 개수를 클라이언트가 조절할 수 있도록 해주지만 구현 방식에 허점이 있다. 가변인수 메서드를 호출하게 되면 가변 인수를 담기 위한 배열이 자동으로 하나 만들어진다. 실체화 불가 타입은 런타임 시 타입 정보가
제네릭은 Set<E>, Map<K,V> 등의 컬렉션과 ThreadLocal<T>, AtomicReference<T> 등의 단일 원소 컨테이너에도 흔히 쓰인다. 하지만 이처럼 클래스 레벨에서 매개변수화 할 수 있는 타입의 수는 제한적이다. (e.g
위와 같은 정수 열거형 패턴(int enum pattern) 기법에는 단점이 많다. 타입 안전을 보장할 방법이 없고 표현력도 좋지 않다. 오렌지를 보내야할 메서드에 사과를 보내고, 동등 연산자(==) 로 비교하더라도 컴파일러는 아무런 경고 메세지를 출력하지 않는다.문자
열거 타입의 상수의 순서를 반환하는 Ordinal 메서드는 많은 부작용을 낳는다.만약 중간에 새로운 상수를 추가하거나 상수가 제거되면 ordinal로 반환되는 정숫값이 달라진다. 불필요한 더미 데이터를 넣거나 하는 방식 등으로 보완할 수 있겠지만 이는 불안정하고 실용성
열거한 값들이 주로 집합으로 사용되는 경우 예전에는 서로 다른 2의 거듭제곱 값을 할당한 정수 열거 패턴을 사용했다.다음과 같은 식으로 비트별 OR를 사용해 여러 상수를 하나의 집합으로 모을 수 있고, 이렇게 만들어진 집합을 비트 필드이라고 한다.비트 필드 를 사용하면
이따금 배열이나 리스트에서 원소를 꺼낼 때 ordinal 을 사용해 인덱스를 얻는 코드가 있다. 다음 코드를 보자.이들을 생애주기 별로 3개의 집합으로 만들고, 정원을 한바퀴 돌면서 각 식물을 해당 집합에 넣어보자. 이때 어떤 프로그래머는 집합들을 배열 하나에 넣고 생
대부분의 상황에서 열거 타입을 확장하는 건 좋은 생각이 아니다. 확장한 타입의 원소는 기반 타입의 원소로 취급하지만, 그 역은 성립하지 않기 때문이다. 기반 타입과 확장된 타입들의 원소를 모두 순회할 수 있는 방법도 마땅하지 않다. 또, 확장성을 높이려면 고려할 요소가
도구나 프레임워크가 특별히 다뤄야 하는 프로그램 요소에는 딱 구분되는 명명 패턴을 적용해 왔다. 예를 들어 테스트 프레임워크인 JUnit3 에서는 테스트 메서드 이름을 test로 시작하게끔 지어야 했었다.하지만 이 경우 다음과 같은 문제가 있다.오타가 나면 안된다.실수
@Override는 상위 타입의 메서드를 재정의했음을 의미한다. Bigram은 영어 알파벳 두개로 구성된 문자열을 표현하는 클래스이다. main 메서드를 보면 Set을 사용했기 중복을 허용하지 않아 s의 size로 26이 출력이 될 것 같지만, 실제로는 260이 출력된
마커 인터페이스 ? 아무 메서드도 담지 않고, 단지 자신을 구현하는 클래스가 특정 속성을 가짐을 표시해주는 인터페이스이다.마커 인터페이스의 대표격인 Serializable 은 자신을 구현한 클래스의 인스턴스는 ObjectOutputStream을 통해 write할 수 있
함수형 인터페이스 과거에는 함수 타입을 표현할 때 추상 메서드를 하나만 담은 인터페이스를 사용했다. 이런 인터페이싀 인스턴스를 함수 객체라고 해서, 특정 함수나 동작을 나타내는 데 사용했다. JDK1.1이 등장하면서 이 함수 객체를 만드는 주요 수단은 익명 클래스가
람다는 익명 클래스보다 간결하다. 그런데 람다보다 더 간결한 방법이 있으니, 바로 메서드 참조(method reference)이다. 매개변수의 수가 늘어날수록 메서드 참조로 제거할 수 있는 코드의 양도 늘어난다. 하지만, 항상 메서드 참조가 더 나은 방법은 아니다. 람
추상 메서드를 딱 하나만 갖고 있는 인터페이스를 말한다. Single Abstract Method 라고 불리기도 한다. 다음 코드를 보자.위 코드는 static, default 메서드와 상관없이 추상 메서드가 하나만 존재하기 때문에 함수형 인터페이스이다.자바는 다양한
스트림 API 는 다량의 데이터 처리 작업을 돕고자 자바 8에 추가되었다. 이 API 가 제공하는 추상 개념 중 핵심은 다음 두 가지다.스트림은 데이터 원소의 유한 혹은 무한 시퀀스를 의미스트림 파이프라인은 이 원소들로 수행하는 연산단계를 표현하는 개념스트림 안의 데이
스트림 패러다임의 핵심은 계산을 일련의 변환으로 재구성하는 부분이다. 이때 각 변환 단계는 가능한 한 이전 단계의 결과를 받아 처리하는 순수 함수여야 한다. 순수 함수란 오직 입력만이 결과에 영향을 주는 함수를 말한다.위 코드는 모은 작업이 종단 연산인 forEach(
스트림은 반복을 지원하지 않는다. 스트림 인터페이스는 Iterable 인터페이스가 정의한 추상 메서드를 전부 포함할 뿐만 아니라 Iterable 인터페이스가 정의한 방식대로 동작하지만, 스트림이 Iterable을 extends 하지 않아서 반복할 수 없다.위 코드는 컴
자바로 동시성 프로그램을 작성하는 것은 쉬워지고 있짐나 올바르고 빠르게 작성하는 일은 어렵다. 동시성 프로그래밍을 할 때는 안전성과 응답 가능 상태를 유지하기 위해 애써야 한다. 병렬 스트림 파이프라인도 다를 바 없다.병렬 스트림 파이프라인에는 주의해야할 점이 있다.
메서드와 생성자의 대부분은 입력 매개변수가 특정 조건을 만족하기를 바란다. 예를 들어 인덱스 값은 음수여서는 안되고 객체 참조는 NULL이 아니어야 한다는 식이다. 이런식의 제약은 반드시 문서화해야 하고 메서스 몸체가 시작되기 전에 검사해야 한다. 오류는 발생한 곳에서
JAVA 의 클래스는 시스템의 다른 부분에서 무슨 짓을 하던 불변식이 지켜진다. 네이티브 메서드를 사용하지 않기 때문에, 메모리 전체를 하나의 거대한 배열로 사용하는 C, C++ 같은 언어에서는 누릴 수 없는 강점이다.하지만 다른 클래스로부터 침범을 아무런 노력없이 다
항상 표준 명명 규칙을 따라야 한다. 이해할 수 있고, 같은 패키지에 속한 다른 이름들과 일관되게 짓는 게 최우선 목표다. 그 다음은 개발자 커뮤니티에서 널리 받아들여지는 이름을 사용하는 것이다. 긴 이름은 피하자. 애매하면 자바 라이브러리의 API 가이드를 참조하라메
위 코드는 "집합", "리스트", "그 외" 를 출력할 것 같지만 실제로 수행해보면 "그 외", "그 외", "그 외" 를 출력한다. 그 이유는 다중정의된 세 classfiy 중 어느 메서드를 호출할 지 컴파일 시점에 정해지기 때문이다. 런타임에는 타입이 매번 달라지지
가변인수 메서드는 명시한 타입의 인수를 0 개 이상 받을 수 있다. 가변인수 메서드를 호출하면 먼저 인수의 개수와 길이가 같은 배열을 만들고 인수들을 이 배열에 저장해 가변인수 메서드에 건네준다.예컨대 인수가 1개 이상이어야 할 때도 있다. 인수의 개수는 런타임에 배열
다음은 흔히 볼 수 있는 메서드다.사실, 재고가 없다고 해서 특별하게 취급할 필요는 없다. null 을 반환하기 위해서는 반환하는 쪽에서도 null 을 반환하는 상황을 특별하게 관리해줘야 하기 때문에 코드가 더 복잡해진다.빈 컨테이너를 할당하는데 비용이 들기 때문에 n
자바 8 이전에는 메서드가 특정 조건에서 값을 반환할 수 없을 때 취할 수 있는 선택지가 두 가지 있었다. 예외를 던지거나 null을 반환하는 것이다. 두 가지 방법 모두 허점이 있다. 예외를 던지는 경우, 스택 추적 전체를 캡처하기 때문에 비용이 만만치 않다. nul
C와 같은 역사가 오래된 프로그래밍 언어 중에는 지역변수를 코드 블록의 첫 머리에 선언하는 경우가 많고, 이 방식을 습관처럼 따르는 프로그래머가 있다. 지역변수의 범위를 줄이는 가장 강력한 기법은 가장 처음 쓰일 때 선언하기다. 미리 지역변수를 선언해두면 코드가 어수선
전통적인 for문보다 for-each문이 간결할 때가 많다. for-each문은 Iterable 인터페이스를 구현한 객체라면 무엇이든 순회할 수 있다. 다음 코드를 보자. i.next()를 계속 호출하기 때문에 결과값은 "ONE ONE"부터 "SIX SIX"가 출력된다
표준 라이브러리를 사용하면 다음과 같이 이점이 많다첫번째, 그 코드를 작성한 전문가의 지식과 앞서 사용한 다른 프로그래머들의 경험을 활용할 수 있다.두번째, 핵심적인 일과 크게 관련없는 문제를 해결하느라 시간을 허비하지 않아도 된다. 세번째, 따로 노력하지 않아도 성능
folat와 double 타입은 넓은 범위의 수를 빠르게 정밀한 근사치로 계산하도록 세심하게 설계됐다. 따라서 정확한 결과가 필요할 때는 사용하면 안된다. 특히 float와 double은 금융 관련 계산과는 맞지 않는다. 0.1 혹은 10의 음의거듭 제곱 수를 표현할
자바의 데이터 타입은 크게 두 가지로 나눌 수 있다. 바로 기본 타입과 참조 타입이다. 그리고 각각의 기본 타입에 대응하는 참조 타입이 하나씩 있고 이를 박싱된 기본 타입이라고 한다. 예를 들어 int,double,boolean에 대응하는 박싱된 기본 타입은 Integ
문자열은 다른 값 타입을 대신하기에 적합하지 않다. 입력받을 데이터가 진짜 문자열일 때만 그렇게 하는게 좋다. 데이터가 수피형이라면 int,float,BigInteger등 적당한 수치 타입으로 변환해야 한다. 예/아니오 질문의 답이라면 적절한 열거 타입이나 boolea
문자열 연결 연산자(+) 는 여러 문자열을 하나로 합쳐주는 편리한 수단이다. 하지만 문자열 연결 연산자로 문자열 n개를 잇는 시간은 n^2에 비례한다. String은 불변이기 때문에 두 문자열을 연결할 경우 양쪽의 내용을 모두 복사해야 하므로 성능 저하는 피할 수 없는
\[아이템51]에서 매개변수 타입으로 클래스가 아닌 인터페이스로 사용하라고 했었다. 이 조언은 "객체는 클래스가 아닌 인터페이스로 참조하라"고까지 확장할 수 있다. 적합한 인터페이스만 있다면 매개변수뿐 아니라 반환값, 변수, 필드를 전부 인터페이스 타입으로 선언하라.
리플렉션 리플렉션을 사용하면 프로그램,에서 임의의 클래스에 접근할 수 있다. Class 객체가 주어지면 그 클래스의 생성자, 메서드, 필드에 해당하는 Constructor, Method, Field 인스턴스를 가져올 수 있고, 이어서 이 인스턴스들로 그 클래스의 멤버
자바 네이티브 인터페이스 (JNI) 는 자바 프로그램이나 C, C++ 같은 네이티브 프로그래밍 언어로 작성된 네이티브 메서드를 호출하는 기능이다. 네이티브 메서드의 주요 기능은 다음과 같다.레지스트리 같은 플랫폼 특화 기능 사용네이티브 코드로 작성된 기존 라이브러리 사
최적화는 좋은 결과보다 해로운 결과로 이어지기 쉽다. 성능때문에 견고한 구조를 희생시키지 말자. 빠른 프로그램보다는 좋은 프로그램을 작성하라. 좋은 프로그램이지만 원하는 성능이 나오지 않은다면 그 아키텍처 자체가 최적화할 수 있는 길을 안내해줄 것이다. 좋은 프로그램은
위 코드는 잘못된 추론을 근거로 성능을 높이려고 하고 있다.JVM은 배열의 원소에 접근할 때마다 경계를 넘지 않는지 검사한다. 따라서 이 검사를 반복문에도 적용할 경우 같은 일이 중복되므로 하나를 생략한 것이다. 하지만 이 추론은 세 가지 근거에서 잘못됐다.예외는 예외
검사 예외는 호출하는 쪽에서 복구하리라 여겨지는 상황일 때 사용해야 한다. 검사 예외를 던지면 호출자가 그 예외를 catch로 잡아 처리하거나 더 바깥으로 전파하도록 강제하게 된다. 따라서 그 메서드를 호출했을 때 발생할 수 있는 오류 상황을 API 사용자에게 알릴 수
검사 예외를 제대로 활용하면 API와 프로그램의 질을 높일 수 있다. 결과를 코드로 반환하거나 비검사 예외를 던지는 것과는 달리, 검사 예외는 발생한 문제를 프로그래머가 처리해 안정성을 높이게끔 해주기 때문이다. 물론, 검사예외를 과하게 사용한다면 오히려 쓰기 불편한
자바 라이브러리는 쓰기에 충분한 수의 예외를 제공한다. 표준 예외를 사용하면 얻을 수 있는 가장 큰 이점은 다른 사람이 익히고 사용하기가 쉽다는 점이다. 낯선 예외를 사용하지 않아 읽기 쉽게 된다는 장점도 존재한다. 그리고, 예외 클래스가 적을수록 메모리 사용량이 줄고
수행하려던 일과 관련 없는 예외가 튀어나온다면 당황스러울 것이다. 메서드가 저수준 예외(구체화 단계의 예외)를 처리하지 않고 바깥으로 던져버리면 내부 구현 방식을 드러내어 윗 레벨 API 를 오염시킨다. 다음 릴리스에서 구현 방식을 바꾸면 다른 예외가 튀어나와서 기존
메서드가 던지는 예외는 그 메서드를 사용하는데 아주 중요한 정보다. 따라서 예외 하나하나를 문서화하는데 충분한 시간을 사용해야 한다. 검사 예외는 항상 따로 선언하고 각 예외가 발생하는 상황을 자바독의 @Throws 태그를 사용해 정확히 문서화 하자. 극단적인 예로 메
예외를 잡지못해 프로그램이 실패하면 자바 시스템은 그 예외의 스택 추적(stack trace) 정보를 자동으로 출력한다. 스택 추적은 예외 객체의 toString()메서드를 호출해서 얻는 문자열로, 보통은 예외의 클래스 이름 뒤에 상세 메시지가 붙는 형태다.따라서 예외
작업 도중 예외가 발생해도 그 객체는 여전히 정상적으로 사용할 수 있는 상태라면 멋질 것이다. 검사 예외를 던진 경우라면, 호출자가 오류 상태를 복구할 수 있을 테니 특히 더 유용할 것이다. 일반화해 이야기하면, 호출된 메서드가 실패하더라도 해당 객체는 메서드 호출 전
API 설계자가 메서드 선언에 예외를 명시하는 까닭은 그 메서드를 사용할 때 적절한 조치를 취해달라고 말하는 것이다. 안타깝지만 예외를 무시하는 것은 아주 쉽다. 해당 메서드 호출을 try 블록으로 감싸고 catch블록에서 아무 일도 하지 않으면 된다.예외는 문제 상황
synchronized 키워드는 해당 메서드나 블록을 한번에 한 스레드씩 수행하도록 보장한다. 많은 프로그래머들은 동기화를 배타적 실행, 즉 한 스레드가 변경하는 중이라서 상태가 일관되지않은 순간의 객체를 다른 스레드가 보지 못하게 막는 용도로만 생각한다. 먼저 이 관
과도한 동기화는 성능을 떨어뜨리고 교착상태에 빠뜨리고, 예측할 수 없는 동작을 수행한다. 응답 불가와 안전 실패를 피하려면 동기화 메서드나 동기화 블록 안에서는 제어를 절대로 클라이언트에게 양도하면 안된다.예를 들어서, 동기화된 영역 안에서는 재정의할 수 있는 메서드는
JAVA 5에서 도입된 동시성 유틸리티가 이전의 wait, notify로 하던 일들을 대신하게 되었다. wait과 notify는 사용하기가 아주 까다롭기 때문에 고수준 동시성 유틸리티를 사용하자.java.util.concurrent의 고수준 유틸리는 세 범주로 나눌 수
한 메서드를 여러 스레드가 호출했을 때 메서드가 어떻게 동작하느냐는 해당 클래스와 이를 사용하는 클라이언트 사이의 중요한 계약과도 같다. API 문서에서 언급이 없다면 사용자는 나름의 가정을 해야만 하고, 지나치게 동기화를 하거나 충분히 하지 못해 심각한 오류로 이어질
지연 초기화란, 필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 기법이다. 그래서 값이 전혀 쓰이지 않는다면 초기화도 일어나지 않는다. 이 기법은 정적 필드, 인스턴스 필드 모두에 사용할 수 있다. 지연 초기화는 주로 최적화 용도로 쓰지만, 클래스와 인스턴스
여러 스레드가 실행 중이면 OS의 스레드 스케줄러가 어떤 스레드를 얼마나 오래 실행할 지를 결정한다. 정상적인 OS라면 이 작업을 공정하게 수행하지만 구체적인 스케줄링 정책은 OS마다 다를 수 있다. 따라서 잘 작성된 프로그램이라면 이 정책에 좌지우지돼서는 안 된다.정
직렬화는 안전하지 않다. 신뢰할 수 없는 스트림을 역직렬화하면 원격 코드 실행, 서비스 거부 등의 공격으로 이어질 수 있다. 역직렬화 과정에서 호출되어서 위험한 동작을 수행하도록 하는 메서드를 가젯이라고 하는데 이런 가젯을 여러개 사용하는 것을 가젯 체인이라고 한다.