mjeongriver
article thumbnail
Published 2022. 10. 28. 17:23
day24-java TIL/Java

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) 실습

 

nestedclass - Embedding(IOT)

* 중복된 코드(스파게티 코드) , 똑같은 코드를 두번 쓰지 말자 - 고쳐야 할 상황이 왔을 때 두번 고치는 건 실수할 수 있는 가능성이 있기 때문에 유지 보수가 어려워진다. 

 

* 상속은 부모(슈퍼, 베이스), 자식(서브, 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());

null 여부를 표시, null과 not null인지 빠르게 확인하기 위한 것=Optional

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
profile

mjeongriver

@mjeongriver

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그