자바 정리
포스트
취소

자바 정리

삼항연산자는 경우가 두 가지일때. if else를 나타내기 싫다면.

지역 변수는 선언된 지역을 벗어나면 메모리 공간에서 소멸된다.

new 연산자는 정확히, 생성된 인스턴스의 주솟값을 반환하는 것이다.

a 참조 변수에 인스턴스 주솟값을 던지고 b = a 로 하나의 인스턴스 주소를 두 참조 변수에 할당시킬 수 있다.

환경 변수의 classpath는 특정한 상황이 아니면 되도록 사용하지 말야아 한다?

정보 은닉: 자바에서 말하는 ‘정보’는 클래스의 ‘인스턴스 변수’를 의미한다. 따라서 정보를 은닉한다는 것은 인스턴스 변수를 숨긴다는 뜻이다. (private 선언)

캡슐화: 하나의 목적을 이루기 위해 관련 있는 모든 것을 하나의 클래스에 담아 두는것

JVM상에서 클래스 로딩이 완료되어야 인스턴스 생성이 가능한데 클래스 로딩이 되는 시점에 정적 멤버는 이미 메모리 영역에 할당되어있다.

참조를 목적으로 정적 멤버를 선언할 때, 특히 필드 멤버에겐 final 선언에 대한 여부를 고민해볼 필요가 있다.

main 메소드의 호출이 이뤄지는 영역은 클래스 외부이다. 따라서 public으로 선언하는 것이 타당하다.

클래스 로딩과 동시에 정적 멤버를 초기화 시키기 위해서 블록 스코프로 감싸줄 수 있다. 패키지 import를 static으로 선언하면 클래스 이름 없이도 정적 멤버를 사용할 수 있다.

StringBuilder: 메소드에 따라 불필요한 인스턴스를 계속 생성하여 메모리 낭비를 가하는 String의 일부 기능의 대안. StringBuilder는 인스턴스 내부에 문자열 관리를 위한 메모리 공간이 존재한다. 이 메모리 공간은 말 그대로 문자를 얼마나 지정할 수 있느냐이며 input된 양에 맞게 직접 조절한다. 하지만 이러한 공간 변화는 소모가 많은 작업이므로 애초에 적절한 크기의 공간을 생성자 호출 시에 적용해주면 된다. 기본 값: 16개의 문자를 저장, 정수: N개의 문자를 저장할 수 있음, 문자열: 매개값과 16개의 문자를 추가로 저장할 수 있음. 주요 메소드 append, delete, insert, replace, reverse, substring, toString StringBuilder는 기존의 인스턴스를 계속 수정하며 그 주소를 반환하는 것이다.

1
2
StringBuilder stb1 = new StringBuilder("123");
stb1.append(45).append(67) . . .

주소를 반환하고 주솟값에 해당하는 인스턴스를 다시 찾아감.

StringBuilder 클래스(자바5) 이전에 사용 되던 StringBuffer 클래스 둘의 공통점 : 생성자를 포함한 메소드의 수, 메소드의 기능, 메소드의 이름과 매개변수의 선언 세 가지가 일치하는 건 사실상 같은 클래스지만 차이가 있다. 쓰레드 안정성. 이전에 나온 Buffer는 멀티 스레드 환경에서의 안정성을 갖췄지만 Builder는 속도를 우선시하여 싱글 스레드를 위해 새로이 탄생했다.

콘솔은 컴퓨터를 대상으로 데이터를 입력 및 출력하는 장치를 총칭.

syso에서 참조 변수의 주솟값을 출력해줬던 것도 매개 값의 인스턴스를 대상으로 toString 메소드를 호출하여 문자열로 반환된 주소를 출력해주는 것.

printf (와 format은 완전 동일한 메소드다?) d 10진수 정수 o 8진수 정수 x 16진수 정수 f 실수 e e표기법 기반의 실수 g 출력의 대상에 따라 %e 또는 %f s 문자열 c 문자

Scanner(자바5) 생성자로 전달되는 대상으로부터 데이터를 추출하는 기능을 제공한다. 생성자 매개 변수로는 FileSource, StringSource, InputStreamSource 등이 있다

배열의 fill 메소드는 배열과 특정 인자를 받아 전달된 값으로 한번에 초기화할 수 있다. enhanced-for = for-each = 향상된 for문

상속: 연관된 일련의 클래스들에 대해 공통적인 규약을 정의할 수 있다. 상속은 코드 재활용을 위한 문법이 아니다.

하위 클래스의 인스턴스 생성시 상위 클래스의 생성자까지 호출되며 상위 클래스 생성자가 먼저 호출된다. ?정적 멤버는 상속되지 않는다. 하지만 하위 클래스에서 상위 클래스의 정적 멤버를 선언 없이 다룰 수 있다. ?다 상속되어 하위 클래스와 인스턴스로 상위 클래스의 정적 멤버를 사용할 수 있다. (버전에 따른 차이인지 확인해야함)

상속 관계에서 필드 선언을 할 땐 동일한 변수를 사용하지 않도록 주의한다. main 메소드에서 타입 변환을 하며 각각의 객체에 동일한 이름을 가진 필드에 접근할 때 혼란을 준다. 정적 멤버도 현재의 타입에 알맞는 멤버에만 접근하므로 오버라이딩의 대상이 아니라고 볼 수 있다. 메소드 오버라이딩을 통해서 상속 범위를 넓힐 수 있다. (제한할 수는 없음) 클래스의 필드를 조회할 필요가 있다면 contains, equals, hashcode 등의 메소드를 오버라이딩하는 경우가 많다. toString 메소드의 경우 자바 공식 문서에서도 오버라이딩하는 것을 권장한다.

추상 클래스와 일반적인 Super 클래스는 비슷하지만 생성자를 통한 객체 생성의 여부가 다르다. 추상 클래스는 하나의 규격이라 보면 되고 추상적인 것으로 치면 인터페이스가 정점이다. 실체 클래스가 공통적으로 가져야할 필드와 메소드들을 정의해놓았으며 멤버를 통일하는 데 목적이 있다.

어노테이션: 자바 컴파일러에게 메시지를 전달하는 목적의 메모

인터페이스의 디폴트 메소드(자바8) (인터페이스의 이름은 형용사로 지어주자) 이미 많은 인터페이스와 구현 클래스들이 존재할때 새로운 인터페이스를 생성해 구조를 수정하기는 힘들다. 개발진들도 람다를 추가하기위해 인터페이스 내부에 추상메서드가 아닌 디폴트메서드 기능을 함께 도입했다. 디폴트 메서드는 오버라이딩을 하지 않아도 되는 완전한 형태이다. 또한 정적 메소드(자바8)도 도입했다. 인터페이스만으로도 메소드 사용이 가능해진 것이다.

인터페이스 참조 변수에 타입변환하여 구현 객체를 삽입하지 않더라도 구현 클래스를 통해 나온 객체는 (A insanceof B) 를 따졌을 때 참으로 나타난다.

자바에서는 하드웨어의 오동작으로 발생한 건 에러 이외의 프로그램 자체 오류는 예외라고 부른다 자바7부터 catch문에서 (AException | BException e) 이런식으로 두 개의 예외를 받을 수 있다. Exception 클래스를 상속하는 예외 중 RuntimeExceptio을 제외한 예외에 대해서는 예외 처리가 필수적이다. 메인 메소드로 예외를 넘기면, 이 예외는 메인을 호출한 가상머신에게 넘어간다. = 프로그램 종료 직접 Exception을 상속하여 프로그래머가 Exception을 정의할 수도 있다. 종료가 반드시 필요할 경우 try(resource, resource2) 처럼 내부에 그 문장을 try문이 종료될 때 리소스도 함께 종료된다. 과도한 예외처리는 성능 저하로 이어진다.

자바에서의 API 자바 시스템을 제어하기 위해 자바에서 제공하는 명령어들을 의미한다. SE(JDK)를 설치하면 자바 시스템을 제어하기 위한 API를 제공한다. 패키지 내의 클래스들도 자바에서 제공하는 API 중의 하나라고 할 수 있다.

자바 가상머신의 메모리 모델 프로그램의 실행에 필요한 메모리 공간을 가리켜 메인 메모리라 하며, 이는 물리적으로 램을 의미한다. 메모리의 효율적인 사용을 위해서 운영체제가 메모리를 관리한다. JVM은 운영체제에서 메모리를 할당 받고 효율적으로 사용한다.

JVM의 메모리 공간은 크게 메소드, 스택, 힙 영역으로 나뉜다. 메소드 영역: 메소드의 바이트코드(class 확장자), 정적 변수 스택 영역: 지역 변수, 매개 변수 CPU가 직접 메모리에 접근하고 관리해서 메모리 누수 문제가 없다. 힙 영역: 인스턴스 메모리 크기의 제한이 없으며, Stack에 비해 느리다. 메모리 관리를 직접하며 GC의 활동 영역이다.

지역 변수: 중괄호로 구분되는 지역 내에서만 유효한 변수 스택 영역 - 중괄호 내에 할당된 이후에 해당 중괄호를 벗어나면 바로 소멸되는 특성의 데이터 저장을 위한 영역

인스턴스를 스택 영역이 아닌 힙 영역으로 분리해 보관하는 건 지역 변수와의 생성 소멸 패턴이 달라서이다. 스택 영역에 보관되는 참조 변수에 힙 영역의 인스턴스의 주소를 저장하여 교류하는데 인스턴스의 소멸은 JVM에서 직접 결정하므로 인스턴스와 연결된 참조 변수에 null을 대입함으로써 소멸 판단에 도움을 줄 수 있다. 해당 인스턴스의 주소를 참조하는 변수가 없기 때문이다. 다르게 말하면 해당 인스턴스에 더이상 접근 할 수도 없다.

Object 클래스의 equals 메소드는 내용 비교가 이뤄지도록 오버라이딩 하라고 존재하는 메소드이다.

clone 메소드를 직접 작성한 클래스에 정의하고 싶다면 Cloneable(마커) 인터페이스를 상속해야 한다. 이 메소드는 인스턴스의 복사본을 만들어 참조 값을 반환한다. (얕은 복사) 깊은 복사를 위해선 clone 메소드를 오버라이딩하고 인스턴스 내부의 참조 변수에까지 clone 메소드를 사용해준다. 이때 String 클래스는 Cloneable 인터페이스를 구현하지 않기 때문에 new 연산자를 이용한다. String은 문자열의 수정이 불가능하므로, 깊은 복사의 대상에서 제외해도 된다. 당연한 말이다. clone 오버라이딩: 메소드 내부에 인스턴스 복사본 생성 -> 필드 복사 -> 인스턴스 반환

자바5부터 오버라이딩 과정에서 반환형의 수정을 허용한다. 단 클래스 이름과 관련한 조건이 있다.

Wrapper 클래스도 toString이 오버라이딩 되어있기에 출력시 주소가 아닌 값이 나온다. 박싱된 객체를 언박싱 (Object).(primitive type)value 인자를 해당 자료형으로 변환 (wrapper class).valueOf(Object) 자바5부터 자동 박싱, 언박싱이 지원된다. 모든 Wrapper 클래스는 Number 클래스를 상속한다.

BigInteger: long 범위를 벗어난 정수를 표현 BigDecimal: double 범위를 벗어난 실수를 표현 인스턴스를 생성할 때 인자를 문자열로 받고 연산을 위한 메소드가 존재함.

Random 클래스의 인스턴스를 생성할 때 씨드 값을 주지 않으면 인자를 currentTimeMills() 으로 갖는 생성자를 다시 호출한다.

StringTokenizer: 특정 기준을 가지고 문자열을 작게 나누어야 할 때 사용 토큰: 구분자를 기준으로 나뉜 문자열 조각 이 클래스는 생성할 때 추출할 문자열과 구분자에 대한 정보를 인자로 받는다. 다수의 구분자가 있을 땐 공백없이 문자열 하나로 작성하여 인자로 준다. hasMoreTokens, nextToken 메소드와 while문을 활용하여 토큰을 받는다. 구분자도 토큰으로 받고 싶다면 생성자의 인자를 그대로 주되 true 값을 마지막에 추가로 준다.

Arrays 클래스 Arrays.copyOf: 배열을 복사하여 새로 생성할 때 System.arraycopy: 존재하는 배열에 복사를 하려는 경우 Arrays.equals: 배열의 내용 비교 하지만 인스턴스 배열이 전달된다면 참조 값이 비교되기 때문에 해당 인스턴스의 클래스에서 오버라이딩 필요

Arrays.sort 인스턴스가 저장된 배열은 주소값만이 존재할 것이다. 이러한 배열에서 sort 메소드를 호출하면 인스턴스들의 순서를 compareTo 메소드를 이용해 결정한다. compareTo는 인스턴스가 속한 클래스 내부에 오버라이딩하여 기준을 정한 뒤 3가지 값을 반환하면 된다. “ 양의 정수, 음의 정수, 0 “ 이 반환 값을 이용하여 sort 메소드가 순서를 결정해준다. if else로 처리할 수도 있지만 양의 정수, 음의 정수처럼 값이 고정된 것이 아니므로 return에 this.age - p.age; 처럼 연산을 바로 작성해도 된다. 매개변수는 Object형으로 받으므로 명시적 타입 변환이 필요하다. ?제네릭이 도입되면서 Comparable 인터페이스가 제네릭 타입으로 지정되었다. 둘의 차이를 알아야 한다.

제네릭(자바5) 제네릭 타입이 없다면 Object 객체를 생성하여 이용할 수 있다. 이땐 명시적 형 변환이 필요한 상황이 반드시 나타난다. 하지만 반드시 실수하는 경우가 생길 것이다. (RuntimeException) 제네릭이 등장하면서 자료형에 의존적이지 않은 클래스를 정의할 수 있게 되었다. 인스턴스 생성 시 타입 인자를 통해 자료형을 결정하는 것이 제네릭이다. 보편적으로 자주 사용하는 타입 매개변수 E(elements), K(key), N(number), T(type), V(value) 매개변수화 타입도 타입 인자로 줄 수 있다. (다중 박싱) 타입 인자를 제한했을 때 클래스 내부에서 동일한 타입 매개변수로 선언된 필드에 제한의 기준이 되는 클래스의 메소드를 사용하여 접근할 수 있다. 하나의 클래스와 하나 이상의 인터페이스에 대해 동시에 제한할 수 있다. 정적 메소드의 제네릭은 직접 선언해줘야 한다. (compile err) 제네릭 메소드 = 제네릭 정적 메소드 제네릭 클래스는 인스턴스 생성 시 자료형이 결정되는 반면 제네릭 메소드는 메소드 호출시에 자료형이 결정된다. 제네릭 메소드를 이용할 때 컴파일러는 전달되는 인자를 보고 타입을 유추할 수 있다. 이러한 자료형의 유추는 오토 박싱까지 감안하여 이뤄진다. 제네릭 메소드도 타입 인자의 제한과 메소드 활용이 똑같이 이루어진다. 자바7부터 컴파일러의 자료형 유추 범위가 넓어져서 제네릭 메소드의 인자가 없더라도 메소드 부분의 제네릭 선언은 생략할 수 있다.

제네릭 - 와일드카드 제네릭 선언된 정적 메소드의 기본형과 선언 없이 인자에 <?>(와일드카드)를 작성하는 것은 상호 대체 가능한 동일한 메소드이다. Box를 매개변수로 작성하면 Box도 받을 수 있지 않을까? 싶지만 Object와 Integer가 상속 관계라 할 지 언정 Box와 Box는 상속 관계가 아니다. Box<? extends Number> = Number 클래스와 그를 상속한 클래스만 가능한 와일드 카드의 선언 extends: 상한 제한, super: 하한 제한 매개변수화 타입에서 (Box box) T의 범위를 제한한다. 제한이 없을 때의 안정성을 생각해보면 와일드카드는 어렵지만 절대 필요없지 않다. "필요한 만큼만 기능을 허용하여, 코드의 오류가 컴파일 과정에서 최대한 발견되도록 한다."

제네릭 메소드의 매개변수화 타입에 와일드 카드의 상한 제한을 선언하면 매개변수화 타입을 참조하는 변수를 대상으로 저장하는 기능(setter)의 메소드 호출은 불가능하다. Box<? extend Toy> 의 경우 Toy 클래스를 상속하는 다른 클래스의 인스턴스가 형 변환 되어 저장되었을 수도 있다. 그러므로 자바에서는 사전에 setter 메소드를 막아버렸다. 컴파일 에러는 자바에서 막아버리는 것

반대의 겨우인 하한 제한에서는 getter 후 특정 클래스 타입의 참조 변수에 대입하는 것을 막는다.

따라서 와일드 카드에서의 타입 제한은 box가 참조하는 인스턴스를 대상으로 꺼내거나 저장하는 기능의 메소드 호출을 불가능하게 만든다.

1
2
public static void outBox(Box<? extend Toy> box) {};
public static void outBox(Box<? extend Robot> box) {};

제네릭 메소드의 정의로 Type Erasure를 피해간다.

제네릭 클래스를 상속할 때 슈퍼 클래스의 타입 인자에 대한 지정을 서브 클래스에서 해줘야한다. 하지만 extends Class<String> 과 같이 상속과 동시에 지정해주면 서브 클래스를 제네릭으로 정의할 필요 없다. 인터페이스를 상속할 때도 모든 게 동일하게 적용된다.

컬렉션 프레임워크 자료구조는 데이터의 저장 관련 학문으로 데이터의 탐색, 삭제 등 다양한 측면을 고려한 데이터의 효율적인 저장 방법을 연구하는 학문이다. 자료구조에서 정형화하고 있는 데이터의 저장 방식 중 대표적인 몇가지를 정리하면 리스트, 스택, 큐, 트리, 해쉬 등이 있다. 위 자료구조들을 대상으로 하는 비교적 간단한 알고리즘 몇 가지를 보면 버블 정렬, 퀵 정렬, 이진 탐색 등이 있다.

컬렉션 클래스들을 기반으로 생성되는 컬렉션 인스턴스들은 인스턴스의 저장을 목적으로 한다. 컬렉션 관련 클래스들과 인터페이스들은 대부분 java.util 패키지로 묶여있다.

1
List<String> list = Arrays.asList("toy","box");

문자열이 두 개 있는 배열을 list에 담고 // public ArrayList(Collection<? extends E> c>) // List는 Collection 인터페이스를 상속하고 타입 제한으로 컬렉션 인스턴스에서는 참조만 가능하다.

1
list = new ArrayList<>(list);

생성된 인스턴스에 그대로 복사한다.

hashcode와 equals를 오버라이딩하여 Set 인터페이스 중복 판단에 도움이될 수 있다. 필드 값이 완전히 동일한 인스턴스라도 본래의 메소드는 주소값을 판별하기 떄문이다. 하지만 매번 두 메소드를 오버라이딩하는 것은 귀찮은 일이므로 특별한 경우가 아니라면 Objects의 hash 메소드를 이용할 수 있다. (가변 인자로 선언되어 있음) public int hashCode(){ return Objects.hash(model, color);} 인스턴스의 주소가 아닌 내부 필드를 인자에 넣어 비교한다.

TreeSet은 Tree라는 자료구조를 기반으로 인스턴스를 저장한다. (Red-Black Tree) 인스턴스들의 참조 순서는 오름차순으로 정렬된다. 하지만 비교군이 명확하지 않을땐 Comparable 인터페이스의 유일한 추상메소드인 int CompareTo 메소드의 구현을 통해서 그 기준을 정해주어야 한다. Tree 구조를 알아야 this가 누군지, 어떤 방식으로 정렬되는지 알 수 있다.

Comparable을 오버라이딩하여 기준을 정했지만 일시적으로 기준을 변경하고 싶을 때가 있다. Comparator 인터페이스가 이를 해결해준다. 사용방법은 Comparable과 비슷하지만 인자를 두 개 받는다. 기존의 구조가

1
public int compare(Person p1, Person p2){ return p1.age - p2.age; }

라면 반환값의 연산 순서를 바꿔주면 된다. 이를 이용하기 위해선 TreeSet 인스턴스를 생성할 때 인자 값으로 구현 객체를 대입하면 된다. List의 컬렉션 프레임워크에서 중복된 인스턴스를 제거하고 싶다면 Set - HashSet 인자에 대입 후 다시 List로 되돌리면 중복된 인스턴스를 제거할 수 있다.

Stack 동기화된 클래스로 쓰레드에 안전하지만 그만큼의 성능 저하도 발생함. 자바 6에서 스택을 대신할 수 있는 Deque 자료구조가 포함됐음. 양방향 구조이므로 Stack과 Queue를 대신할 수 있다. Deque<E> val = new ArrayDeque<>(); LinkedList를 기반으로 덱을 구성할 수도 있다. LinkedList는 Deque, List 의 구현 클래스다.

Map key는 실질적 데이터가 아니라 데이터 Value를 찾는 지표이다. HashMap 클래스는 Iterable 인터페이스를 구현하지 않으니 for-each문을 통해서 혹은 반복자를 얻어서 순차적 접근을 진행할 수 없다. 대신 Map에는 Set keySet 메소드가 존재한다. key 묶음을 담아 Set 인터페이스를 구현하여 모든 Key에 대해 순차적으로 접근할 수 있다. Set은 Iterable 인터페이스를 상속하므로 가능한 방식이다. 이를 이용하여 value를 출력하려면 HashMap 참조 변수에 get 메소드의 인자로 key 값을 전달하면 된다. TreeMap에서는 key 값을 기준으로 정렬한다. Collections.sort 를 사용하기 위해선 아래 메소드의 문장을 설명할 수 있어야 한다. public static <T extends Comparable<? super T>> void sort(List<T> list){}; Comparable<Car> 인터페이스를 구현한 Car 클래스를 상속한 SubCar 클래스를 생각해보면 List<SubCar>가 대입 됐을때 <SubCar extends Comparable<SubCar(or Car or Object)>> void sort(List<SubCar> list){}; 로 변경되어 Super 클래스가 Comparable을 구현한 상태일 때 SubCar로도 이 메소드를 사용할 수 있다. ?Comparator 인터페이스 구현 클래스는 꼭 따로 작성해줘야 되나?

Tree는 자체적으로 정렬을 고려하며 인스턴스를 받는다. List의 경우엔 정렬 없이 순차적으로 저장한다. 이진 탐색을 하려면 반드시 정렬해줘야하며, 혹 정상적인 결과를 얻는다 해도 우연의 일치일 뿐이다.

열거 열거형 값은 Enum.ABC 같이 표현하지만 case 문에서는 ABC 같이 열거형 값의 이름만 명시해도 된다. 특정 클래스 내에서만 사용하고자 하는 열거형 값이 있다면, 해당 클래스 내에 정의하면 된다. 열거형 값엔 private 생성자가 숨어있다. private으로 정의하여 인자를 받을 수도 있는데. 인자 값을 전달하려면 enum 필드에 직접 인자를 작성해야된다. 구조가 특이하지만 쓸 일은 없어보인다. 다른 참조 변수에 동일한 enum 필드의 주소값을 전달하면 두 참조 변수가 가르키는 주소는 같다. 열거 타입은 카테고리가 명확할 때 버그를 줄이며 활용할 수 있어 유용하다.

메소드의 가변 인자 선언. 가변 인자 선언을 하면 전달되는 인자의 수에 제한을 두지 않을 수 있다. 가변 인자는 컴파일러가 배열로 해석한다.

터미널 컴파일 시 -Xlint(enable recommended warnings)을 함께 작성하면 경고를 확인할 수 있다.

중첩 클래스에서 정적 네스티드 클래스나 이너 클래스에서 아우터 클래스의 필드가 private이어도 참조 가능. 멤버 클래스는 클래스의 정의를 감추어야 할 때 유용하게 사용이 된다. 컬렉션 프레임워크의 iterator 메소드도 Iterator 인터페이스를 구현한 중첩 클래스를 이용하는 것이다. 일반적으로 아우터 클래스 외부에서 네스티드 또는 이너 클래스의 객체를 생성하는 일은 거의 없다. 대부분 아우터 클래스 내부에 이들의 객체를 생성하여 사용한다. 이너 클래스의 경우 클래스를 감싸는 게 메소드라면, 이 메소드 내에 객체를 생성하고 사용한다. 주로 비동기 처리를 위해 스레드 객체를 만들 때 사용된다. 중첩 인터페이스의 경우 주로 정적 네스티드 인터페이스로만 선언하여 UI 프로그래밍에서 이벤트를 처리할 목적으로 많이 활용되며 네스티드로만 선언할 수 있다.

익명 클래스의 가장 단순 형태는 new Printable() { 실체 메소드(){} }; 의 형태이다. 인터페이스는 원래 자신이 구현 객체가 될 수 없지만 이러한 방식으로는 가능하다. 왜 익명 클래스라 부르는가? new Printable() <<<익명 클래스명>>> { 실체 메소드(){} }; 이걸 활용해서 익명 Comparator를 실행문 내에 작성할 수 있다. (클래스 파일은 생성됨) 자식 클래스 또는 구현 클래스가 재사용되지 않고 오로지 특정 위치에서 사용할 경우라면 자식 클래스를 명시적으로 선언하는 것은 귀찮은 작업이므로 익명 클래스를 사용하자. ? 익명 자식 객체에 대한 이해 필요

람다(자바8) 람다와 익명 클래스의 내부적인 동작 원리는 다르다. 람다식은 단순히 생각하면 익명 클래스를 좀더 간결하게 표현한다고 말할 수도 있다. 매개 변수, 실체 메소드, 반환값, 화살표가 필요하다. 추상메소드가 딱 하나만 존재하는 인터페이스를 람다식으로 활용할 때 이 인터페이스를 함수형 인터페이스라고 한다. 네 가지 대표적인 함수형 인터페이스 Predicate<T>, Supplier<T>, Consumer<T>, Function<T, R> ?함수형 인터페이스를 사용할만 기능들을 미리 구현해둔 건가?

메소드 참조(자바8) 메소드 참조는 람다식으로 줄어든 코드의 양을 조금 더 줄일 수 있게 한다. ‘::’ 연산자로 나타내며 클래스, 인스턴스로 참조할 수 있다. ?메소드가 접근하는 인스턴스가 인자로 들어가는 원리..

Optional 클래스(자바8) NPE 대응한 if 방어로직을 대신하여 코드를 축약할 수 있음. ?자바 8에 관한 건 무조건 다시 봐야겠다. ?축약되는 모든 것이 헷갈린다.

Stream(자바8) 배열 또는 컬렉션 프레임워크 형식의 데이터를 추상화하고 순차적으로 처리하는데 쓰인다. 최종 연산과 관련된 메소드를 호출하지 않으면 filter메소드의 반환이 이루어지지 않는다. 이를 지연 처리 방식이라고 한다. 스트림의 대략적인 구조 배열 및 컬렉션 인스턴스 대상으로 스트림 생성 stream stream.of 필터링 및 맵핑 관련 연산 filter(Predicate) map(Functional) | peek 리덕션 관련 연산 sum forEach reduce | sum, count, average, min, max 참고 - https://sejoung.github.io/2019/01/2019-01-31-java8_stream_tutorial_examples/#%EB%8B%A4%EB%A5%B8-%EC%A2%85%EB%A5%98%EC%9D%98-%EC%8A%A4%ED%8A%B8%EB%A6%BC

멀티 코어 CPU가 대중화돼서 자바는 언어 차원에서 병렬 처리를 지원한다. ParallelStream 은 병렬 처리 방식으로 속도 측면에서 장점이 있다. 병렬 처리는 연산의 횟수를 줄이는데 있지 않고 연산의 단계를 줄이는데 있다. 4개의 데이터를 BinaryOperator를 통해 처리한다면 최초에 기준값과 4개의 데이터를 동시에 비교한다. 물론 CPU의 코어 개수가 그에 맞게 필요하다.

stream.of 메소드를 통해 생성된 stream은 문자열을 동시에 3개 넣는다 하더라도 배열에 저장된 요소로 이뤄진 하나의 인스턴스만 존재하게 된다. 함수형 인터페이스부터 스트림까지 리터럴 타입을 넣어 자동 언박싱 연산이 적용되게 했는데 최적화를 위해선 DoubleStream 같은 메소드를 사용해주는 게 좋다. 스트림 인스턴스에 parallel 메소드를 사용하면 병렬 스트림으로의 변경도 가능하다. concat 메소드는 문자열 연결처럼 스트림을 연결하는 메소드이다. flatMap은 스트림을 생성하고 이를 반환한다 반면 map은 전달할 람다식에서 스트림을 구성할 데이터만 반환하면 된다.

루핑 스트림을 이루는 모든 데이터 각각을 대상으로 특정 연산을 진행하는 행위 대표적인 루핑 연산으로 forEach가 있고 중간 연산에도 루핑을 위한 메소드가 존재한다. 예를 들어 Consumer 인터페이스를 인자로 받는 peek 메소드가 있다. 중간 연산이므로 지연 처리 방식이 적용되어 있다. forEach와 peek 둘 다 매개변수로 Consumer를 받기 때문에 거의 동일한 메소드이다. 스트림 종료 여부에따라 사용하면 된다.

최종 연산에는 sum, count, average, min, max 등이 있는데 average , min , max 는 Optional형 인스턴스를 반환하기 때문에 Optional 클래스의 메소드를 사용하여 다음 연산을 진행한다.

스트림은 최종 연산을 하는 순간 파이프라인의 마지막을 통과해버린다. 이미 흘러가버린 스트림을 대상으로는 그 어떤 연산도 추가로 진행할 수 없다. 이러한 스트림의 특성 때문에 실제 코드에서 스트림형 참조변수를 선언할 일은 거의 없다.

(all,any,none)Match 최종 연산의 sorted.

1
2
3
.collect(() -> new ArrayList<>(),
	(c,s) -> c.add(s),
	(lst1,lst2) -> lst1.addAll(lst2));

첫 번째 연산은 새로운 데이터 저장소 생성 두 번째 연산의 첫 번째 매개변수는 생성된 데이터 저장소 (컬렉션 인스턴스) 두 번째 매개변수는 스트림을 이루는 데이터 순차 스트림의 경우 세 번째 연산이 의미가 없지만 그렇다고 해서 null을 전달하면 NPE 발생 병렬 스트림의 경우 여기서 addAll은 ArrayList의 인스턴스 메소드로써 메소드의 호출 결과로 lst2가 저장하고 있는 모든 데이터가 lst1에 담기게 된다. 병렬 처리가 능사는 아니다. 병렬 처리를 했을 때 오히려 속도가 느려지는 경우도 의외로 많다. 병렬 처리를 결정했을 때는 테스트를 통해서 병렬 처리의 적합성을 판단해야 한다.

시각과 날짜 관련 코드의 작성 java.util.Date(자바0) 구식 SimpleDateFormat 클래스로 형식 변환 java.util.Calendar(자바1) 구식 /* (자바8) + DateTimeFormatter(Instant는 포맷 적용이 까다롭다) java.time.Instant 1970 - 01 - 01 00:00:00 부터 현재까지 흐르는 시간 중 특정 지점을 인스턴스로 생성 (UTC 기준 시간) java.time.Duration 소요 시간을 계산하는 데 이용 java.time.LocalDate 시각 정보가 생략된 날짜 정보를 표현 java.time.LocalTime 시각 정보를 표현 java.time.period 날짜 기간을 계산하는 데 이용 java.time.LocalDateTime java.time.ZoneId 협정 세계시에 따른 시각 계산을 위해 지역 지정 java.time.ZonedDateTime 지역에 따른 시각을 표현할 수 있음 */

LocalDateTime에서 날짜의 차이와 시각의 차이를 각각 구하기 위해선 toLocalDate, toLocalTime 메소드를 이용하면 된다. 단순히 Duration만 한다면 날짜계산까지 시각의 관점에서 나타낼 수 있다.

날짜 시각 관련 인스턴스들은 메소드로 그 인스턴스를 수정하는 게 아니라 수정한 정보를 토대로 새로운 인스턴스를 반환한다. 한국의 시간대는 UTC+9이다. 협정 세계시보다 9시간이 빠름을 의미한다. Local 또는 Zone의 기본 값은 사용자 컴퓨터에 맞춰져있다. 참고 - https://jaimemin.tistory.com/1537

I/O 스트림(I/O 스트림은 java.io 소속 / 배열 스트림은 java.util.stream 소속 배열, 컬렉션 인스턴스의 데이터를 목적에 맞게 가공하는 앞의 스트림과는 달리 데이터를 꺼내는 방법에 대한 것까지도, 어떻게 데이터를 입력하고 출력할 것인가에 대한 내용이다. 입출력 대상은 파일, 키보드, 모니터, 그래픽카드, 프린터, 연결된 서버 또는 클라이언트 등 다양하다. 입출력 대상이 달라지면 코드상에서의 입출력 방법도 달라지는것이 일반적인데 자바에서는 입출력 대상에 상관없이 동일한 방법으로 입출력을 진행할 수 있도록 I/O 스트림 모델 이라는 것을 정의하였다. 스트림이란 ‘데이터의 흐름’을 의미한다. 이를 ‘데이터의 이동 통로’로 의역하면 표현이 부드러워진다. close 메소드로 생성했던 스트림을 소멸시키면 열려 있던 파일은 닫히고 할당되었던 메모리 자원은 다시 사용할 수 있도록 반환된다. try-with-resources문을 이용하면 안정적이고 간결한 코드를 작성할 수 있다.

1
try(InputStream in = new FileInputStream("data.dat") ; . . .){ . . . } catch (IOException e) { . . . }

예외 발생시 resource 영역의 스트림의 종료가 보장된다.

기본적인 데이터의 입출력 단위는 바이트이다. read 메소드는 1바이트의 유효한 데이터에 3바이트의 0을 채워서 4바이트 int형 데이터로 반환한다. 그래서 이 메소드가 반환하는 정상적인 값의 범위는 0~255이다. 역으로 write 메소드는 int형 데이터의 첫 번째 바이트만을 파일에 저장한다. 이와 같이 파일의 크기에 상관없이 1바이트씩 읽거나 쓰면 크기가 어느정도 되는 파일을 복사할 경우 제법 오랜 시간이 걸린다.

바이트 스트림이라 하여 1바이트씩만 읽고 써야 하는 것은 아니다. byte 배열을 생성해서 이를 기반으로 많은 양의 데이터를 한 번에 읽고 쓰는 것도 가능하다. read 의 인자로 바이트 배열을 전달하고 메소드는 배열의 길이를 반환한다. write 의 인자로 배열, 시작 인덱스, 길이를 전달하면 값에 맞게 데이터를 이동시킨다. read 는 읽어 들인 바이트의 수를 반환하는데, 스트림의 끝에 도달해서 더 이상 읽어 들일 데이터가 없는 경우 -1을 반환한다. 이는 배열의 길이가 초과된 게 아니라 읽어들이는 데이터에 모두 접근했다는 말이다. 출력 스트림에서 파일이 존재하지 않다면 경로에 따른 디렉토리가 존재하는 경우 파일을 자동 생성한다. createNewFile 메소드는 필요가 없다.

만약 1바이트의 데이터 4개를 모아 int형 데이터를 얻고 싶다면? 읽어 들인 1바이트 데이터 4개를 하나의 int형 데이터로 조합하는 과정이 필요하다. 필터 스트림을 통해 읽어 들이는 데이터는 합쳐지고 전달하는 데이터는 분해된다. 장치를 튜닝한 것과 비슷한 느낌으로, 바이트 단위 스트림을 필터 스트림의 인자로 전달하면 된다.

1
(DataFilter(I/O)Stream) instance::write(read)Int(Double) Method

Buffered(I/O)Stream 필터 스트림 중에서 상대적으료 사용 빈도수가 높다. 입출력에 버퍼링 기능을 제공한다. 버퍼 : 동작 속도가 크게 다른 두 장치 사이에 접속되어 속도 차를 조정하기 위하여 이용되는 일시적인 저장 장치. 저속의 단말기와 고속의 중앙 처리 장치 사이에 설치된다. 즉 버퍼 스트림은 내부에 버퍼(메모리 공간)을 갖는다. 입력 스트림을 기준으로 이야기해보자면 입력 스트림을 통해 많은 양의 데이터를 가져다 해당 버퍼를 채운다. read 메소드를 호출하면 버퍼에 저장된 데이터를 반환한다. 이것이 성능 향상의 핵심인데, 파일 복사에서 메소드 호출의 빈도수보다 더 문제가 되는 것은 파일에 빈번히 접근하는 행위이다. 파일은 물리적으로 떨어져 있다. 파일에서 1바이트를 읽는 것은 메인 메모리에서 1바이트를 읽는 것보다 훨씬 오랜 시간이 걸린다. 따라서 파일에서 데이터를 읽어 들일 때는 한 번에 많은 양을 읽어 들여야 파일 접근에 따른 성능의 저하를 최소화할 수 있다. 버피의 디폴트 용량은 8192개의 문자를 저장할 수 있는 수준이고 readLine을 통해 한 줄씩 읽는다면 아무런 문제 없이 사용할 수 있다.

char 자료형은 2바이트로 유니코드를 표현한다. UTF-8은 UTF-16이 7bit로 표현가능한 아스키코드 문자를 16bit로 표현하자, 메모리 낭비를 막기 위해 탄생한 인코딩 방식이다. UTF-8은 16bit 유니코드 문자를 지원함에도 아스키 코드 문자들을 7bit로 표현 가능하다. 한글은 유니코드에 속하므로 1바이트로 표현할 수 없다. 7bit 만으로 아스키코드의 문자를 표현하다보니 유니코드 문자가 들어오면 문자를 인식하는 과정에서 오류가 발생할 수 있다. UTF-8은 이러한 오류를 피하고자 헤더가 추가되어 3바이트를 차지하게 되었다. 아스키 코드가 주로 사용되는 환경에서는 UTF-8이 유리하지만 다양한 문자가 사용되는 공간이라면 메모리 효율과 임의 접근 효율에서 떨어진다. 그러므로 JVM은 문자나 문자열을 메모리에 저장할 때 UTF-16 인코딩 방식을 사용하여 저장한다. (JVM 외부와 데이터를 주고 받을때 영어 말고도 다양한 언어를 접하므로 합리적인 방법이다)

그러면 txt파일의 한글로된 문자는 언제 3바이트로 변하게 되는 걸까? JVM이 txt 파일에 접근하고 문자를 가져온 뒤 UTF-16(2byte) 인코딩을 끝내고 이클립스 설정이 charset이 UTF-8로 되어있다면 아스키코드를 제외한 문자는 헤더까지 부착되어 3바이트 상태로 스트림 최종 연산에 순차적으로 나타난다. 즉 JVM 내부에서 파일에 접근할 때, 해당 파일의 문자셋이 UTF-8로 되어있다면

일반적으로 아스키코드를 제외한 유니코드는 UTF-16 -> UTF-8 로 전환될 때 아래의 이진수 x 자리에 순차적으로 입력된다. 1110 xxxx, 10xx xxxx, 10xx xxxx

버퍼링 기능에 대한 대책 버퍼링은 성능 향상에 도움을 주지만 저장된 데이터가 파일에 저장되지 않은 상태에서 컴퓨터가 다운 된다면 실제 파일로 데이터를 보낼 수 없는 상황이 발생할 수 있다. flush 메소드 호출을 통해 명시적으로 버퍼를 비우라고 명령할 수 있다. 이 메소드를 번번히 호출하는 것은 또 바람직하지 않다. 스트림이 종료되면 버퍼는 자동으로 비워지기 때문에 상황에 맞게 사용해야 한다.

버퍼링 기능과 기본 자료형 데이터 저장을 동시에 사용하려면 Data(I/O)Stream의 인자로 Buffered(I/O)Stream 인스턴스를 전달하면 된다. Data 스트림은 내부에서 바이트 형태로 변환하고 Buffered 스트림은 1바이트를 모아서 보내기 때문이다.

문자 I/O 스트림은 파일에 저장된 데이터를 입출력할 때 자바의 문자 표현 방식과 상대측 문자 표현 방식을 비교해서 문자의 인코딩을 변경하는 스트림이다.

Q. 문자만 저장되어 있는 파일을 복사하려고 한다. 이때 필요한 스트림은? 문자 스트림을 통해서도 복사를 진행할 수 있다. 그러나 기본적으로 파일 복사는 파일의 내용에 상관없이 있는 그대로의 바이트 정보가 저장된 파일을 하나 더 만드는 일이다. 따라서 바이트 스트림을 생성해서 복사를 진행하는 것이 원칙이다.

Q. 자바 프로그램에서 문자 하나를 파일에 저장했다가 다시 읽어 들이려 한다. 이때 필요한 스트림은? 이 경우 파일에 문자를 저장하는 주체도, 저장된 문자를 읽는 주체도 자바 프로그램이다. 따라서 문자를 유니코드로 저장하고 읽어 들이면 충분하므로 바이트 스트림을 생성하는 것이 옳다. 물론 문자 스트림을 생성해서 이 일을 처리할 수도 있다. 그러나 그 과정에서 불필요하게 문자의 인코딩을 변경하는 일만 생기게 된다.

Q. 운영체제상에서 만든 텍스트 파일의 내용을 자바 프로그램에서 읽어서 출력할때 필요한 스트림은? 메모장과 같은 텍스트 파일에 저장된 문자들은 해당 운영체제의 기본 문자 인코딩 방식을 따른다.

운영체제상에서 만든 텍스트 파일은 메모장과 같은 프로그램을 실행해서 원하는 내용을 담은 파일을 의미한다. 이렇게 만들어진 파일에 저장된 문자들은 해당 운영체제의 기본 문자 인코딩 방식을 따른다. 메모장의 인코딩 방식은 UTF-8이다. 교재의 내용과 달리 요즘은 UTF-8 인코딩 방식으로 통일되어있다. 질문에 스스로 답하는 시간을 갖자.

문자 기반 스트림에는 BufferedReader(Writer) 가 있다. 문자열을 한 번에 쓰고 일는 일은 버퍼를 필요로 한다. newLine 메소드는 줄바꿈 기능을 한다. 버퍼 리더는 readLine 메소드로 한 문장씩 읽어 들일 수 있다.

IO 스트림 기반의 인스턴스 저장 바이트 스트림을 통해서 인스턴스를 통째로 저장하고 꺼내는 것도 가능하다. 이렇듯 인스턴스를 통째로 저장하는 것을 가리켜 객체 직렬화라 하고 (Object Serialization) 역으로 저장된 인스턴스를 꺼내는 것을 객체 역 직렬화라고 한다. (Object Deserialization) Object(I/O)Stream은 필터 스트림과 유사하지만 기존에 알던 필터스트림은 Filter(I/O)Stream 을 상속하지만 Object Stream은 저들을 상속하지 않는다. 입출력의 대상이 되는 인스턴스의 클래스는 java.io.Serializable을 구현해야한다. (마커 인터페이스) 출력의 대상은 Object.bin 파일에 함께 보관된다. writeObject 메소드로 저장하고, readObject와 명시적 타입 변환으로 인스턴스를 복원한다. 인스턴스를 저장하면 인스턴스 변수가 참조하는 인스턴스까지 함께 저장이 된다. 하지만 이 과정이 진행되려면 참조하는 인스턴스의 클래스도 마커 인터페이스가 적용되어야 한다. 만약 참조 변수가 참조하는 인스턴스의 저장을 원치 않는다면 transient 선언을 추가하면 된다. 이는 인스턴스가 아닌 기본 자료형 변수에도 해당 된다. 꺼내올 때 참조 변수들을 null로 하고자한다면 사용하자.

Java는 String 을 처리할 때 내부(메모리 상에서)에서는 UTF-16 BE 인코딩으로 문자열을 저장하고, 송수신에서는 직렬화가 필요한 경우 변형된 UTF-8 (modified UTF-8) 을 사용하며 문자열을 입/출력할 때에만 사용자가 지정한 인코딩 값 또는 운영체제의 기본 인코딩 값으로 문자열을 인코딩한다.

인코딩에 대한 자세한 내용 https://st-lab.tistory.com/41

nio 패키지는 new io 라 말할 수 있으며 기존 io 패키지의 보강을 위해 자바 4에서 NIO API가 java.nio 패키지로, 자바7에서 NIO.2 API는 java.nio.file 패키지로 추가되었다. java.nio.file.path는 기존 File 클래스의 결함 대체를 위해 정의된 인터페이스이다. Path는 File이 하는 간단한 일들을 대체할 수 있다. (아예 대체가능한 지는 모름) Path a = Path.get(URI); 로 경로에 관한 인스턴스를 만들고 Files 클래스의 정적 메소드, StandardOpenOption Enum을 활용하여 디렉토리, 파일 생성 및 제거, 바이트 단위의 read, write이 스트림 생성 없이 가능하다. 문자열 단위 송수신도 가능하다. readAllLines 와 write(path, collection, …)가 있다.

쓰레드 하나에 CPU의 코어 하나가 할당된다. Runnable 인터페이스에 람다식으로 작성하고 쓰레드 인스턴스에 인자를 넣어 사용 동기화가 필요한 메소드 내의 실행문만 따로 동기화 처리 하려면 synchronized(this){ . . . } 블록으로 감싸준다. 쓰레드의 생성과 소멸은 그 자체로 시스템에 부담을 주는 일이다. 이러한 성능 저하를 피하기 위해 미리 제한된 수의 쓰레드를 생성해두고 이를 재활용하는, 쓰레드 풀을 구성하여 사용한다. ExecutorService exr = Executors.newSingleThreadExecutor(); ExecutorService 인터페이스에 Executors의 쓰레드풀을 이용한다. submit 메소드와 shutdown 메소드로 스레드 작업을 추가하고 종료할 수 있다. submit 인자로 Runnable 구현 객체를 넣으면 반환 값을 얻을 수 없지만 Callable 구현 객체를 넣으면 반환 값을 얻을 수 있다. Callable은 반환값이 있는 함수형 인터페이스인데 쓰레드풀에서의 장치다. 반환값을 저장할 때는 Future 인터페이스를 사용하여 Callable과 Future을 특수한 경우의 세트로 사용한다. shutdown 메소드는 쓰레드 풀에 전달된 작업이 마무리되면 풀을 폐쇄하라고 명령할 뿐 기다려주지 않는다. 그래서 쓰레드 풀에 전달된 작업의 최종 결과를 확인하기 위해서 awaitTermination 메소드가 필요하다. 쓰레드 풀 전달된 모든 작업이 완료되거나 인자로 정한 시간이 지난 경우 쓰레드 풀을 폐쇄한다. 자바5에서는 ReentrantLock 클래스를 제공한다. 기존의 Synchronized를 대신할 수 있다. 객체를 생성하고 동기화가 필요한 메소드내에 lock 메소드, try-finally 블록, unlock 메소드를 장석해주면 된다. 컬렉션 프레임워크의 Vector 클래스는 기본적으로 동기화가 되어있다. 따라서 동기화가 불필요한 상황에서 사용하면 아무런 의미 없이 성능만 저하 된다. 그래서 이를 대신하기 위해 자바2에서는 동기화 처리가 되어 있지 않은 ArrayList와 LinkedList를 추가하였다.

운영체제에서는 실행중인 하나의 애플리케이션을 프로세스라고 부른다. 사용자가 애플리케이션을 실행하면 운영체제로부터 실행에 필요한 메모리를 할당받아 애플리케이션의 코드를 실행하는데 이것이 프로세스이다.

하나의 애플리케이션은 멀티 프로세스를 만들기도 한다. 예를 들어 메모장 애플리케이션을 2개 실행했다면 2개의 메모장 프로세스가 생성된 것이다.

운영체제는 두 가지 이상의 작업을 동시에 처리하는 멀티태스킹을 할 수 있도록 CPU 및 메모리 자원을 프로세스마다 적절히 할당해주고, 병렬로 실행시킨다. 예를 들어, 워드로 문서 작업을 하면서 동시에 카톡도 할 수 있다.

멀티 태스킹은 꼭 멀티 프로세스를 뜻하는 것은 아니다. 한 프로세스 내에서 멀티 태스킹을 할 수 있도록 만들어진 애플리케이션도 있다. 대표적인 것이 미디어 플레이어와 메신저이다. 미디어 플레이어의 경우 동영상 재생과 음악 재생이라는 두 가지 작업을 동시에 처리한다. 멀티 프로세스는 자신의 메모리를 가지고 실행하므로 서로 독립적이지만, 멀티 스레드는 하나의 프로세스 내부에 생성되므로 스레드 하나가 예외를 발생시키면 다른 스레드도 영향을 받는다.

어떻게 하나의 프로세스가 두 가지 이상의 작업을 처리할까? –> 멀티 스레드. 멀티 스레도로 동작하는 메신저의 경우 파일을 전송하는 스레드에서 예외가 발생하면 메신저 프로세스 자체가 종료되므로 채팅 스레드도 같이 종료된다. 그렇기 때문에 멀티 스레드에서는 예외 처리에 만전을 기해야 한다.

자바의 모든 애플리케이션은 메인 스레드가 메인 메소드를 실행하면서 시작한다. 이때 메인 스레드는 필요에 따라 작업 스레드들을 만들어서 병렬로 코드를 실행할 수 있다. 즉 멀티 스레드를 생성해서 멀티 태스킹을 수행한다. 멀티 스레드 애플레키에션에서는 실행중인 스레드가 하나라도 있다면, 메인 스레드가 종료되더라도 프로세스가 종료되지 않는다.

자바에서는 작업 스레드도 객체로 생성되기 떄문에 클래스가 필요하다. java.lang.Thread 클래스를 직접 객체화해서 생성해도 되지만, Thread 클래스를 상속해서 하위 클래스를 만들어 생성할 수도 있다.

멀티 스레드 프로그램에서 단 하나의 스레드만 실행할 수 있는 코드 영역을 임계 영역이라고 한다. 자바는 임계 영역을 지정하기 위해 동기화 메소드를 제공한다. –> synchronized

stop 메소드는 스레드를 비정상적으로 종료시켜 안에 있던 자원들이 불안정한 상태로 남게 된다. interrupted 메소드로 예외를 발생시켜 루프에서 빠져나오게 만들 수 있다. interrupted 메소드는 타겟 스레드가 미래에 일시 정지 상태가 됐을때 예외를 발생시킨다. 예로, Thead.sleep() 혹은 공유 객체 동기화 메소드로 인한 일시 정지 상태. 혹은 조건문을 이용하여 interrupted 메소드가 true를 리턴했는가. 를 확인하여 break를 걸 수도 있다.

주 스레드의 작업을 돕는 보조적인 역할을 수행하는 데몬 스레드도 있다. 예로, 워드프로세서의 자동 저장, 미디어 플레이어의 동영상 및 음악 재생, 쓰레기 수집기 등이 있다. 주 스레드가 종료되면 함께 종료된다.

컬렉션 프레임워크의 contain 메소드는 indexOf에 의한 boolean 값을 제공하고 indexOf 내부는 오버라이딩되지 않은 equals의 동등 연산자에 의한 주소 비교가 정의되어 있다. API는 동작 원리에 대한 이해만 하고 비교군들을 정리하다 보면 자연스레 외워진다.

메소드에서 반환값 없이 기존 참조 변수에 새로운 참조값을 할당할 수 없다. 새로운 참조값을 return문에 작성하고 대입 연산자를 거쳐야 한다.

객체의 해시 코드는 알고리즘과 데이터 구조가 객체를 구획에 넣을 수 있도록 한다. 이 간단한 시스템을 사용하면 분류되지 않은 서랍에서 검색하는 것보다 훨씬 빠르게 유형을 찾을 수 있다. 해시코드 계약. “동일한 객체는 실행 중인 프로세스 내에서 동일한 해시 코드를 가져야 합니다.” 아래와 같은 일반적인 오해를 의미하진 않는다. “동일하지 않은 객체는 다른 해시 코드를 가져야 한다” <= false “동일한 해시 코드를 가진 객체는 동일해야 한다” <= false 계약에서는 동일하지 않은 객체가 동일한 해시코드를 공유하는 것을 허용한다. 이것은 가능한 구별 객체의 수가 일반적으로 가능한 해시코드의 수(4byte)보다 많기 때문에 명백하다. equals를 구현할 때마다 hashCode도 구현해야 한다. 그렇게 하지 않으면 깨진 물건으로 끝날 것이다. equals 메소드를 재정의하여 일부 객체를 다른 객체와 동일하게 선언하지만 원래 hashCode 메소드는 모든 객체를 다른 것으로 취급한다. 따라서 해시코드가 다른 동일한 객체를 갖게 된다. 두 개의 서로 다른 객체가 동일한 해시코드를 가질 때마다 이를 충돌이라고 한다. 충돌은 중요하지 않으며, 단일 버킷에 둘 이상의 객체가 있음을 의미하므로 HashMap 조회는 올바른 객체를 찾기 위해 다시 찾아야한다. 충돌은 시스템 성능을 저하시키지만 잘못된 결과로 이어지지 않는다. 해시코드를 객체에 대한 고유 핸들로 착각하면, 예를 들어 이를 Map에서 Key로 사용하면 땔때로 잘못된 객체를 얻게 된다. 충돌은 드물지만 피할 수 없기 때문이다. (System 클래스의 해시코드 메소드를 사용하여 얻은 정수를 16진수로 표현한 게 Object 클래스의 메소드)

parsing이 필요하고 짧은 입력 값이면 Scanner 단순히 한 줄씩 읽거나 입력 값이 많다면 BufferReader

System.arraycopy(arr1, 0, arr2, 0, 3(arr1.length)) arr1[0]부터 시작해서 순서대로 3개의 인덱스를 arr2[0]에서부터 순서대로 삽입한다.

클래스 내 생성자는 메소드와 비슷하게 생겼지만 엄연히 다르다. 객체 생성시 초기화 역할을 담당하며 클래스 이름으로 되어있고 리턴 타입이 없다. 필드를 초기화하거나 메소드를 호출해서 객체를 사용할 준비를 한다.

refactor -> export 실행문 간단히 method 화

상수는 static final 하지만 정적 필드로 정의하지 않았을 땐 생성자를 통해 값이 변경될 수 있으므로 상수라 부를 수 없다.

프로젝트가 다르면 외부 클래스를 호출할 수 없다. 하려면 프로젝트를 외부 라이브러리 형태로 넣어줘야 한다.

웬만하면 대부분 변수에 할당하는 게 좋다. 뭐가 뭔지 다른 사람도 알아야하니깐.

매개 변수와 로컬 변수를 로컬 클래스 내부에서 사용할 때 매개 변수와 로컬 변수가 상수특성을 가지게 된다. “주로 정적 멤버 인터페이스를 많이 사용하는데 UI 프로그래밍에서 이벤트를 처리할 목적으로 많이 활용됩니다.” 다행인 건 중첩 인터페이스는 클래스의 멤버로만 선언한다.

배열의 길이가 길어질수록 향상된 for 문이 유리함 정적 메소드는 재정의할 수 없다.

Comparable과 Comparator의 차이. 두 개 모두 인터페이스이므로 추상 메소드의 실체화가 필요하며, 리터럴에 포함되지 않는 자료형인 객체를 비교할 수 있는 수단이다. Comparable : compareTo (T o) : 자신과 매개 변수 객체를 비교 : 익명 구현 객체 의미없음. : 구현 클래스 오버라이딩 Comparator : compare(T o1, T o2) : 두 매개 변수 객체를 비교 : main문에 람다식 정의 가능 Comparable은 lang 패키지에 존재하기에 import 해줄 필요가 없지만 Comparator는 util 패키지에 존재한다.

정렬을 구현해보면 알겠지만 Counting Sort 같은 특수한 경우를 제외하고 Insertion, Quick, Merge 등 다양한 정렬 알고리즘은 ‘두 데이터(요소)의 비교’를 통해 두 원소를 교환할지 말지를 정하게 된다.

Comparable 은 구현 클래스에 직접 오버라이딩. Comparator 는 람다식을 이용한 익명 구현 객체 생성 후 정렬 메소드의 인자로 전달. 이러한 특성으로 Comparator는 다중 생성 가능하지만 Comparable의 경우 단 하나의 방식 밖에 못 쓴다. flow를 주의하여 사용하자.

필드는 필드 답게, 객체의 속성과 관련 없는 변수는 최대한 로컬로

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.