1. Nested class(내포 클래스)
- 클래스 안에 존재하는 클래스
- 자바는 클래스 안에 클래스를 생성하는 문법을 지언
1) Inner Class
- 클래스 안에 존재하는 클래스
- 클래스 내부에서만 사용할 목적으로 생성
- 일반 클래스는 접근 지정자가 package(접근 지정자가 없는 경우) 와 public 만 가능하지만 inner class에서는 private와 protected도 가능
- 클래스가 컴파일 되었을 때는 외부 클래스 이름$내부 클래스 이름.class
2) Static Inner Class
- class 앞에 static을 붙이는 클래스
- 내포 클래스에 static 멤버가 있으면 일반 inner class는 에러 발생
- 내포 클래스에 static 멤버가 있는 경우 인스턴스 생성 없이 사용할 수 있도록 static을 붙여줘야 합니다.
- static이 붙은 클래스는 인스턴스 생성없이 사용 가능합니다.
3) Local Inner Class
- 메서드 안에 만들어지는 내포 클래스
- 메서드 안에서만 사용이 가능합니다.
4) Anonymous Class
- 이름 없는 클래스 또는 객체
- 인터페이스를 구현하거나 클래스를 상속 받아야 할 때 별도의 클래스를 만들지 않고 필요한 메서드만 재정의 해서 사용하는 문법
- 이벤트 처리 할 때 많이 사용됩니다.
- 이 경우에 인터페이스 안에 메서드가 1개인 경우는 람다 표현식으로 작성하는 것이 가능합니다.
5) 문법적으로 수업 도중 많이 사용하는 것
- Anonymous Class 입니다.
6) 실습
* 중복된 코드(스파게티 코드) , 똑같은 코드를 두번 쓰지 말자 - 고쳐야 할 상황이 왔을 때 두번 고치는 건 실수할 수 있는 가능성이 있기 때문에 유지 보수가 어려워진다.
* 상속은 부모(슈퍼, 베이스), 자식(서브, derived)이 아니라 하위 클래스가 상위 클래스로부터 물려 받는 것(아래에서 위로) - 상속이라고 하지 말고 is a라고 말할 것, 부모랑 자식이라고 읽지 말고 굵은 글씨로 읽을 것
* has a : 클래스 안에서 다른 클래스의 인스턴스 사용, 다른 클래스의 기능을 받아들여 사용함.
7) Anonymous Class(익명 객체 또는 익명 클래스 - 클래스 이름이 없는)
- 작성하는 방법을 알아두어야 합니다.
- 인터페이스를 구현하거나 클래스를 상속 받아서 사용을 하고자 하는 경우에 별도의 클래스를 생성하지 않고 사용하는 방법
- 상속이나 구현을 하는 클래스를 만들지 않고 활용
new 인터페이스 이름이나 상위 클래스 이름(매개변수 나열){
필요한 메서드 정의
};
8) 실습
2. Lambda(람다)
1) 개요
- jdk 1.7부터 함수형 프로그래밍을 위해서 추가
- 이름 없는 함수를 만들기 위해서 사용
- 익명 객체를 만들어서 사용하던 것을 조금 더 간결하게 함수를 대입하는 형태처럼 보이도록 하기 위해서 사용
- 메서드가 1개만 존재하는 인터페이스에서만 사용이 가능
2) 작성 방법
(매개변수 나열) → (내용을 작성)
- 매개변수를 작성할 때는 자료형과 매개변수 이름을 작성하는데 매개변수 자료형은 생략이 가능합니다.
- 자료형을 생략하면 호출할 때 자료형을 결정합니다.
- 매개변수가 1개인 경우는 ( )를 생략하는 것이 가능
- 매개변수가 없거나 2개 이상인 경우는 생략하면 안됨
- 메서드에 return type이 있다면 return 만들면 됩니다.
- 중괄호 안에 return 하는 문장만 있다면 ( )를 생략하는 것이 가능합니다.
- return도 생략이 가능합니다.
3) 실습
4) 자바가 제공하는 람다 인터페이스
1> Consumer: 매개변수는 있고 리턴 값은 없는 메서드를 소유한 인터페이스
2> Supplier: 매개변수는 없고 리턴값만 있는 메서드를 소유한 인터페이스
3> Function: 매개변수와 리턴이 모두 있는 메서드를 소유한 인터페이스
4> Operator: 매개변수가 있고 리턴도 있는 메서드를 소유한 인터페이스인데 이 인터페이스는 일반적으로 메서드 내부에서 연산 작업 수행
5> Perdicate: 매개변수가 있고 리턴 값이 boolean인 메서드를 소유한 인터페이스
3. Stream API
1) 개요
- collection 데이터를 다룰 때 내부 반복자를 만들어서 접근하기 위한 API
- 내부 반복자를 이용해서 접근하면 반복문이나 빠른 열거를 사용하는 것보다 빠르게 작업을 수행할 수 있습니다.
- How보다 What
- 작업의 과정: 생성 - 중간 작업(여러 개를 묶어도 됩니다) - 최종 작업
- 생성은 배열이나 List를 가지고 수행
- 중간 작업은 스트림의 데이터를 순회하면서 작업을 수행해서 다시 스트림을 리턴하기 때문에 다른 중각 작업을 연속해서 배치하는 것이 가능합니다.
- 최종 작업은 1번만 가능합니다.
- 집계를 수행하는 함수나 forEach처럼 하나씩 순회하면서 작업을 수행하기 위한 함수 또는 collect처럼 배열이나 List를 생성해주는 함수를 사용합니다.
- 마지막 방법이 제일 빠름.
2) 스트림 생성
1> Collection 인터페이스로부터 상속 받은 객체는 stream()이나 parallelStream() 을 호출
- parallelStream()을 호출하면 병렬 처리가 가능한 스트림
2> 배열의 경우는 Arrays.stream(배열)을 이용해서 생성
3> 일련 번호 형태의 정수 스트림은 IntStream.range(start, end)나 rangeClosed(start, end)를 이용해서 생성하는 것이 가능
3) 중간 작업 - 작업을 수행한 후 stream을 리턴
1> distinct(): 중복을 제거
2> filter(매개변수 1개를 받아서 boolean을 리턴하는 람다): 람다가 true를 리턴하는 데이터만 모아서 스트림을 생성
3> map(매개변수를 1개 받아서 리턴하는 람다): 리턴하는 데이터를 모아서 새로운 스트림을 리턴
4> sotred(비교 처리를 수행해주는 람다): 람다를 기준으로 정렬
5> skip(long n): n만큼 건너뜀
6> limit(long n): n개 만큼 추출
4) 최종 연산
- count(): 데이터 개수 리턴
- max(): 최대값 리턴
- min(): 최소값 리턴
- average(): 평균 리턴
- forEach(): 데이터를 순회하면서 작업
- collect(): 데이터를 모아서 다른 자료형으로 변경
- 계산에 의해서 나오는 max, min average는 리턴 타입이 Optional 입니다.
- Optional은 isPresent()로 데이터의 존재 여부를 확인할 수 있고 get()으로 실제 데이터를 가져올 수 있습니다.
5) 실습
NullPointerException = null이 멤버를 호출해서 발생시키는 예외
String str = "Hello";
system.out.println(str);
str = null;
< if(str != null) >
system.out.println(str);
system.out.println(str.toUpperCase());
4. Thread
- 프로그래밍에서 가장 중요한 개념 중의 하나인데 실제 생성해서 사용하는 경우는 안드로이드나 자바 애플리케이션을 만들 때이고, web Programming 에서는 직접 생성해서 사용하는 경우가 드뭅니다.
1) 작업 단위
1> Process: 실행 중인 프로그램
- 프로세서를 할당 받아서 실행되는 것
- 한 번 실행되면 자신의 작업이 종료될 때까지 제어권을 다른 프로세스에게 넘기지 않고 계속 수행됩니다.
2> Thread: Process를 작게 나누어서 작업을 수행하는 단위(양보 가능)
- Thread는 단독으로 실행될 수 없고 Process 안에서 실행되어야 합니다.
- 자신의 작업 도중 쉬는 시간이 생기거나 일정한 시간이 지나면 다른 스레드에게 제어권을 양도할 수 있습니다.
2) Thread Programming을 할 때 반드시 알아야 될 사항(개념에 대해서 꼭 숙지하기)
1> 하나의 스레드가 사용 중인 자원은 다른 스레드가 수정하면 안된다.
- Mutual Exclusion(상호 배제), Synchronus(동기화)
2> 생산자와 소비자 문제(ex. 공장)
- 소비자는 생산자가 물건을 생성해 주어야만 작업을 수행합니다.
3> Dead Lock
- 결코 발생할 수 없는 사건을 무한정 기다리는 것
3) java에서 Thread를 생성하는 기본적인 방법
1> Thread 클래스로부터 상속 받는 클래스를 만들고 public void run 이라는 메서드에 스레드로 수행할 내용을 작성한 후 인스턴스를 만들고 start 메서드를 호출
2> Runnable 인터페이스로부터 상속 받는 클래스를 만들고, public void run이라는 메서드에 스레드로 수행할 내용을 작성한 후 Thread 클래스의 인스턴스를 만들 때 생성자에 생성한 클래스의 인스턴스를 대입하고 Thread 클래스의 인스턴스가 start 메서드를 호출하면 됩니다.
3>Callable 인터페이스를 구현한 클래스를 이용해서도 생성 가능합니다.
4) Thread.sleep(long msec[,long nano])
- msec 밀리초 동안 현재 스레드를 중지
- nano를 입력하면 msec 밀리초 + nano 나노초 만큼 대기
- 이 메서드를 사용할 때는 InterrupedException을 처리해주어야 합니다.
- 실습을 할 때 이 메서드를 사용하는 이유는 일정 시간 이상 대기를 해야만 다른 스레드에게 제어권이 이동되기 때문입니다. 실제 시간의 의미는 거의 없습니다.
5) 스레드 생성 실습
6) Thread 상태
- NEW: Thread가 생성되었지만 Thread가 아직 실행할 준비가 되지 않았음
- RUNNABLE: Thread가 JVM에 의해 실행되고 있거나 실행 준비되어 스케쥴링을 기다리는 상태
- WAITING: 다른 Thread가 notify(), notifyAll()을 불러주기를 기다리고 있는 상태.
- TIMED_WAITING: Thread가 sleep(n) 호출로 인해 n 밀리초 동안 잠을 자고 있는 상태
- BLOCK: Thread가 I/O 작업을 요청하면 JVM이 자동으로 이 Thread를 BLOCK 상태로 만듬
- TERMINATED: Thread가 종료된 상태
- Thread 상태는 JVM에 의해 기록되고 관리됨
- run() 메서드가 종료되면 Thread는 종료
- 한번 종료한 Thread는 다시 시작시킬 수 없음
- Thread에서 다른 Thread를 강제 종료시킬 수 있음
- 상태 변화 - 수명 주기
7) Daemon Thread
- 다른 스레드가 수행 중이 아니면 자동으로 종료되는 스레드
- 스레드의 역할을 도와주는 보조적인 목적으로 사용
- start 하기 전에 setDaemon 메서드를 호출하면 되는데 true 값을 설정해주면 됩니다.
8) Thread의 Priority(우선 순위)
- 여러개의 스레드가 수행될 때 어떤 스레드가 먼저 되고 어떤 스레드가 나중에 수행될지는 알 수 없습니다.
- 우선 순위를 설정하면 확률적으로(반드시는 아님) 먼저 또는 자주 실행되도록 할 수 있습니다.
- setPriority 메서드를 이용해서 설정합니다.
- setPriority(int priority) - 매개변수가 정수이지만 아무 정수나 사용하는 것은 안되고 제약이 있습니다.
- 이 때, 일반 정수를 사용해도 에러는 발생하지 않지만 되도록이면 Thread.MAX_PRIORITY, NORMAL, MIN의 상수를 사용하는 것을 권장합니다.
9) Thread Group
- 관련된 스레드들을 하나의 그룹으로 묶어서 사용하기 위한 개념
- ThreadGroup 이라는 클래스를 제공하지만 몇몇 메서드가 제대로 동작하지 않는 문제 때문에 대부분의 경우 배열이나 List를 이용해서 구현하는 것을 권장
10) Thread의 종료
- run 메서드 종료
- 스레드의 interrupt 메서드를 호출하고 스레드의 run 메서드 안에서 InterruptedException이 발생하면 run 메서드를 종료하도록 만들어서 강제 종료 하도록 할 수 있습니다.
- Daemon Thread는 Daemon이 아닌 다른 스레드가 존재하지 않으면 자동으로 종료됩니다.
- 스레드의 실행 제어 메서드
void join(long millis, int nanos): 지정된 시간 동안 스레드를 수행하고 다른 스레드에게 제어권을 넘기는 메서드
void suspend(): 스레드를 일시 정지시키는 메서드로 resume 메서드로 다시 시작
void resume(): suspend된 스레드를 다시 시작
void yield(): 다른 스레드에게 제어권을 넘겨주는 메서드
11) Mutual Exclusion(상호 배제)
- 하나의 스레드가 사용 중인 공유 자원을 다른 스레드가 수정하면 안됨
1 > 공유 자원을 여러 개의 스레드가 동시에 사용 했을 때 문제
- 자원을 가지고 연산을 하는 스레드에 사용할 클래스
- 공유 자원을 사용하는 실행 클래스
- 실행을 하면 이상한 결과가 출력됩니다.
ShareData 인스턴스를 1개만 만들어서 공유하는데 하나의 작업이 완료되기 전에 다른 스레드가 공유 자원을 수정하기 때문에 발생하는 문제입니다.
2> 해결 방법
- 한번에 실행되어야 하는 코드를 가진 영역을 찾고 메서드의 synchronized를 붙이던가 코드 영역을 synchronized(공유 객체){}로 묶어주면 됩니다. → 이렇게 묶어주면 묶인 영역의 코드는 동시에 수정할 수 없습니다.
되도록이면 synchronized 메서드를 만드는 것보다는 블럭을 만드는 것을 권장하는 이유는 메서드를 묶게 되면 영역이 커져서 공유도가 떨어지기 때문입니다.
3>ShareData 클래스를 수정해서 다시 실행
자원을 가지고 연산을 하는 스레드에 사용할 클래스
4> 최근에는 위의 방법 말고 ReentrantLock 이라는 클래스를 이용하는 것을 권장합니다.
- 인스턴스를 생성하고 lock이라는 메서드로 공유 영역을 만들고 unlock이라는 메서드로 공유 영역을 해제
ex) for(int i = 0; i<10; o++){
idx++;
Thread.sleep(1); //10이상은 줘야 getResult로 넘어옴.
result = result + idx;
}
Thread sleep(3000); //넣어주면 괜찮아짐
system.out.println(getResult());
12) 생산자와 소비자 문제
- 생산자와 소비자는 동시에 수행되어도 문제가 발생하지 낳는데 소비자는 생산자가 자원을 생성해준 경우에만 동작을 해야 합니다. 소비자가 자원이 생성되지 않았는데 작업을 수행하면 예외가 발생합니다.
1 > 생산자와 소비자 문제 발생
- 공유 자원의 역할을 수행할 클래스 - Product
- 생산자 스레드 클래스: Producer
- 소비자 스레드: Customer
- main 메서드를 소유한 클래스를 만들어서 스레드 2개를 실행 - ProducerCousumerMain
- 실행을 하다보면 예외가 발생
생산자 스레드인 Customer 스레드가 리스트에 아무런 데이터가 없는데 꺼낼려고 해서 발생하는 문제
소비자 스레드의 메서드는 데이터가 없을 때는 대기하고, 생산자 스레드는 데이터를 만들어 낸 경우 데이터가 만들어졌다고 알려주어야 합니다.
기다리기 위해서 호출하는 메서드는 wait 이고, 알려주는 메서드는 notify와 notifyAll 이라는 메서드입니다.
이 메서드들은 Object 클래스의 메서드이고 synchronize 메서드 안에서만 동작합니다.
- Product 클래스를 수정하고 다시 실행해보면 예외가 발생하지 않습니다.
13) Dead Lock
- 결코 발생할 수 없는 사건을 무한정 기다리는 것
- synchronize 블럭이 여러개 있는 경우에 멀티 스레드를 사용하면 발생
14)Semaphore
- 동시에 사용할 수 있는 스레드의 개수를 제한
- Semaphore 클래스를 이용해서 설정
- 메서드
acquire(): 리소스를 확보하는 메서드
relase(): 리소스를 해제하는 메서드
1> 세마포어를 이용하지 않는 경우 - 스레드가 동시에 전부 실행
- 스레드 클래스
2> Main 메서드를 소유한 클래스를 만들어서 실행 - SemaphoreMain: 4개 스레드가 10초 동시에 메세지를 출력
3> 세마포어를 이용하도록 수정
- 스레드 클래스 수정
- 메인 클래스 수정
**수업 외 알아둘 것
* 써야되는 상황을 인지하는게 어렵기 때문에 경험이 쌓여야 함.
* 일반적으로 개발자들은 클래스의 인스턴스를 1개만 생성할 때는 변수의 이름을 클래스 이름과 동일하게 만들고 첫글자만 소문자로 변경합니다.
- String string = "dkfksdjfk";
* 어떤 클래스로부터 상속 받는 클래스를 1개만 만들 때는 클래스 이름 뒤에 Ex(extend)를 붙입니다.
- Thread 클래스로부터 상속받는 클래스
- class ThreadEx extends Thread{
}
ThreadEx th = new ThreadEx(); O
Thread th = new ThreadEx(); X
* 인터페이스를 구현한 클래스를 1개만 만들 때는 클래스 이름 뒤에 Impl을 붙입니다.
Runnable 인터페이스를 구현한 클래스
class RunnableImpl implements Runnable{
}
* extends 하거나 implements 했을 때 변수의 이름은 상위 클래스 이름이나 인터페이스 이름을 사용합니다.
ArrayList<String> list = new ArrayList<>();
List<String> list = new ArrayList<>();
'TIL > Java' 카테고리의 다른 글
StringBuffer, StringBuilder가 string보다 성능이 좋은 이유와 원리 (0) | 2023.05.15 |
---|---|
day25-java (0) | 2022.10.31 |
day23-java (0) | 2022.10.27 |
day22-java (0) | 2022.10.26 |
day21-java (2) | 2022.10.25 |