변수와 타입
변수
- 데이터의 저장과 참조를 위해 ‘할당된 메모리 공간’에 붙인 이름
기본 타입
- 이진수의 음수화 : 1의 보수화 후 1을 더함. 올림 수는 버림
- 소수점 기준, float, double은 각각 6, 15자리의 정밀도를 갖는다
- long 타입 정수를 표현할 때 리터럴 끝에 ‘L’ 문자 추가
숫자 표현, 리터럴 표현 - 2진수 : 0B 8진수 : 0 16진수 : 0x - 숫자 리터럴 표현 시 ‘100_000’ 처럼 언더바 사용 가능. 컴파일 과정에서 제거됨
- 제곱수의 표현 - 3.4e3 == 3.4 x 10^3
타입 변환
- 자동 형변환 : 자료형의 크기가 큰 방향으로 형 변환이 일어남
- 자료형의 크기에 상관없이 정수 자료형보다 실수 자료형이 우선함
- 정수형 -> 실수형 변환시 오차는 존재하겠지만 데이터의 손실은 발생하지 않음
CHAPTER 3 연산자
연산자와 연산식
- 정수형 연산을 4byte int형으로 진행
- 연산을 동반하는 상황에선 byte, short 타입으로 선언하더라도 생각만큼 메모리가 절약되지 않고, 오히려 타입 변환 과정만 추가됨
- 서로 다른 타입의 피연산자가 산술 연산식에 존재한다면, 더 큰 메모리를 할당하는 피연산자의 타입이 연산식의 결과에 부여된다.
- 실수 연산은 기본적으로 오차가 존재한다고 가정해야 한다. 연산 시 소수점 이하 셋째 자리에서 오차가 발견될 수도 있다.
연산자의 종류
- 비교 연산자에서 실수 비교는 자제한다.
- 쉬프트 연산자 예시
int num = A << Z
: A의 비트 열을 Z만큼 왼쪽으로 이동 0000_0001의 경우 ‘«’ 하면 2씩 곱해지는 거와 마찬가지
조건문과 반복문
조건문: if문, switch문
- 삼항 연산자로 간단한 if-else 처리
참조 타입
참조 타입과 참조 변수
- 상수(final)는 값을 단 한번만 할당할 수 있으며 변경 불가하다
배열
- fill 메소드를 통해 특정 인자를 받아 한번에 초기화할 수 있다
- 배열 복사 관련 메소드가 존재하므로 검색 활용
- sort 메소드는 compareTo 메소드를 이용하여 정렬 기준을 설정
열거 타입
- enum 등장으로 정수 열거형을 별도 타입으로 빼낼 수 있게 됨
- 구식 방법인 정수형 열거의 대안이다
- 싱글턴, enumMap, enumSet 그리고 방어적 복사를 생략할 수 있는 강력한 불변객체로 활용할 수 있다
클래스
필드
- 필드는 필드 답게, 객체의 속성과 관련 없는 변수는 최대한 지역 변수로
인스턴스 멤버와 정적 멤버
- JVM에서 클래스 로드가 완료돼야 인스턴스 생성이 가능한데 클래스가 로드되는 시점에 정적 멤버는 이미 메모리 영역에 할당되어 있다
- 클래스 내부에 블록 스코프를 활용하여 정적 메소드를 클래스 로드 시점에 호출할 수 있다
- 정적 멤버는 인스턴스 없이 사용할 수 있고 불필요한 메모리 할당을 방지할 수 있지만 이 때문에 오히려 불필요한 메모리를 할당하는 걸로 볼 수도 있다 정적 멤버는 공유되고 반복되는 상황에서 효율적이다
상속과 추상클래스
상속
- 연관된 일련의 클래스들에 대해 공통 규약을 정의
- 자식 클래스 인스턴스 생성시 부모 클래스 생성자가 먼저, 함께 호출된다
- private 멤버는 자식 클래스에서 직접 접근할 수 없다.
- 상속 관계에서의 필드 선언 시 동일한 변수를 사용하지 않도록 주의
- 정적 클래스는 오버라이딩의 대상이 아니다
- 부모 타입의 구현 객체를 자식 객체로 했을 때 부모 타입의 멤버에만 접근 가능하고, 재정의된 메소드가 있다면 자식 메소드가 호출됨
- is-a 관계과 명확할 때
추상 클래스
- 일반 클래스의 상속과 달리 추상 클래스는 객체 생성이 불가능하다
- 추상 클래스는 하나의 규격이라 보면 되고 인터페이스가 좀더 추상적이다
- 실체 클래스가 공통적으로 가져야할 멤버를 정의해놓으며 멤버 통일에 목적
인터페이스
인터페이스
- 이미 형성된 계층 트리에서 인터페이스에 새로운 추상 메소드를 추가하긴 힘들다 이런 상황에서는 실체화가 필요 없는 디폴트 메소드를 활용하자
- 정적 메소드를 활용하여 인터페이스만으로 메소드 사용이 가능하다
- 주로 정적 멤버 인터페이스를 많이 사용하는데, UI 프로그래밍에서 이벤트를 처리할 목적으로 많이 활용된다
- 주로 has-a 관계
타입 변환과 다형성
- 자식 instanceof 부모는 참이다
중첩 클래스와 중첩 인터페이스
중첩 클래스와 중첩 인터페이스 소개
- 정적 중첩 클래스나 인스턴스 중첩 클래스에서 바깥 클래스의 필드가 private이어도 참조 가능
- 중첩 클래스는 해당 클래스 정의를 감추어야할 때 유용하게 사용 된다
- 컬렉션 프레임워크의 iterator 메소드도 Iterator 인터페이스를 구현한 중첩 클래스를 이용하는 것이다
- 일반적으로 바깥 클래스 외부에서 해당 클래스의 중첩 클래스 객체를 생성하는 일은 거의 없다
- 메소드 소속 중첩 클래스의 경우 메소드 내에서 객체를 생성하고 사용한다 주로 비동기 처리를 위해 스레드 객체를 만들 때 사용된다
- 중첩 인터페이스의 경우 주로 정적 중첩 인터페이스로만 선언하며 UI 프로그래밍에서 이벤트를 처리할 목적으로 자주 활용되며 필드 멤버로만 선언된다
익명 객체
익명 객체의 가장 단순한 형태
new Printable()<익명>{ 실체 메소드 (...) {...}; }
- 생성자에 바로 스코프와 함께 메소드 정의가 이루어진다
- 인터페이스는 본래 자신이 구현 객체가 될 수 없지만 이러한 방식으로는 가능하다
- 자식 클래스 또는 구현 클래스가 재사용되지 않고 오로지 특정 위치에서 사용할 경우라면 명시적으로 선언하는 것이 귀찮은 작업이므로 익명 클래스를 고려해볼만 하다 ex) comparator
예외 처리
예외 클래스
- 에러 : 하드웨어 오동작으로 발생
- 예외 : 프로그램 자체 오류
- Exception 클래스를 상속하는 예외 중 RuntimeException을 제외한 예외에 대해선 예외 처리가 필수적
- 직접 Exception을 상속하여 정의할 수도 있다
예외 처리
- main 메소드로 예외를 넘기면, 이 예외는 호출 주체인 가상머신에게 넘어감 즉, 프로그램이 종료됨
- 과도한 예외 처리는 성능 저하로 이어진다
- 종료해야될 리소스가 존재한다면 finally 스코프에 기입한다
- 유효성 부적합 판정을 받았을 때 return이 아닌 비검사 예외를 명시하되 표준 예외를 사용하자
- try/catch를 남발하면 오류가 발생했을 때 제어 흐름이 중단되지 않아 부차적 문제를 발생시킬 수 있고 stack trace를 보존하지 않는 로깅은 디버깅을 어렵게 만드는 일이다. 책임지지 못할 오류는 잡지 않는다 그래야 JVM이든 어딘가에서 로그를 남길 수 있기 떄문이다
기본 API 클래스
java.lang 패키지
Wrapper
- toString 오버라이딩 되어있어 출력시 주소가 아닌 값이 나온다
- 자동 박싱, 언박싱이 지원된다
- 모든 Wrapper 클래스는 Number 클래스를 상속한다 Big
- 생성자 매개변수로 문자열 형태의 숫자를 대입함
- BigInteger : long 범위를 벗어난 정수를 표현
- BigDecimal : double 범위를 벗어난 실수를 표현
java.util 패키지
- StringBuilder : 내부에 문자열 관리를 위한 메모리 공간이 존재하여 메소드 체인으로 참조 변수의 데이터를 변환하는 형식 기존 String concat 메소드와 비교하여 메모리에 유리함 StringBuffer의 대안. 스레드 안전하진 않지만 싱글 스레드상 속도 우위
- Scanner : 생성자로 전달되는 대상으로부터 데이터를 추출하는 기능을 제공
- StringTokenizer : 특정 기준을 가지고 문자열을 작게 나누어야할 때 사용 토큰 : 구분자를 기준으로 나뉜 문자열 조각
- parsing이 필요하고 짧은 입력 값이면 Scanner
- 단순히 한 줄씩 읽거나 입력 값이 많다면 BufferReader
컬렉션 프레임워크
컬렉션 프레임워크
- 인스턴스의 저장을 목적으로 하는 자료구조 중 정형화된 것
- 배열보다 컬렉션을 지향하는 이유는 제네릭 타입과 데이터 조작 시 다양한 api가 주는 편리함 때문이다
- 중복 판단과 정렬 등에 equals, hashcode, compare 등을 이용함
- 버킷의 개수가 예상된다면 생성자의 인자로 넣어 버킷의 개수를 늘리는 과정을 최대한 배제시키자
- 동기화된 클래스로 Vector, Stack, HashTable 등이 있지만 보완품이 있다
- Tree는 자체적으로 인스턴스를 정렬한다
- 이진 탐색을 하려면 올바르게 정렬해야 한다
- Arrays.asList의 반환 타입은 update 관련 메소드를 사용할 수 없다 수정이 필요하다면 new 연산자를 통한 ArrayList를 이용하면 된다 둘다 stream은 이용할 수 있다
- Map은 Iterable 인터페이스를 구현하지 않으므로 직접 반복문을 활용할 수 없다 메소드를 활용하여 타입 변환한 뒤 순차적 접근이 가능하다
- Stack 대신 Deque-ArrayDeque 를 사용하자 (Spec 권장) ArrayDeque는 스레드 안전하지 않기 때문에 멀티 스레드 환경에서는 LinkedBlockingDeque, ConcurrentLinkedDeque 를 이용하자 전자는 멀티 스레드 환경의 단일 스레드, 후자는 멀티 스레드 권장
네트워크
소켓
- 멀티 스레드 환경에서 서버 소켓은 클라이언트 소켓이 connect 하였을 때 connection 에 대한 listener 인 accept 메소드로 인지한 뒤 클라이언트 소켓과 1대1로 연결된 소켓을 반환한다 이 소켓은 서버 소켓과 동일한 포트를 갖는다.
모던 자바
람다
- 익명 클래스와 내부 동작 원리는 다르다
- Predicate
, Supplier , Consumer , Function<T, R> - 추상 메소드가 단 하나만 존재하는 함수형 인터페이스일 때 람다 사용 가능
- 람다식을 더 줄인 게 메소드 참조 (System.out::println)
- () -> obj.method 같은 건 Object::method 로 변환하자
옵셔널
- NullPointerException의 if 로직 대응 코드를 대체할 수 있음
- NPE 방어 로직은 가독성과 유지 보수성이 떨어진다
컬렉션 스트림
- 컬렉션 데이터를 추상화하고 순차적으로 처리하는 데 쓰인다
- 기존 반복문으로 처리하던 게 내부에서 동작할 뿐이다
- 지연 처리 방식 적용으로 최종 연산 메소드를 호출해야 실행 된다 Parallel Stream
- 병렬 처리의 경우 CPU 코어 개수가 그에 맞게 필요하다
- 병렬 처리는 연산의 횟수를 줄이는데 있지 않고 연산의 단계를 줄이는데 있다
- 스레드와 같이 병렬 처리를 남용하면 성능 저하만 초래한다 [참고] https://sejoung.github.io/2019/01/2019-01-31-java8_stream_tutorial_examples/