자바수업 기록

221028 Nested class(내포 클래스), Lambda(람다), 스트림API, Thread

JadeStone 2022. 10. 28. 18:43

12.Thread.pdf
1.10MB

스레드 교안.

내포클래스_람다_스트림API.txt
0.02MB

선생님이 메모장에 정리해주신 오늘 수업내용.

 

 

 **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.Anoymous Class 
=>이름없는 클래스 또는 객체
=>인터페이스를 구현하거나 클래스를 상속받아야 할 때 별도의 클래스를 만들지 않고 필요한 메서드만 재정의해서 사용하는 문법.
=>이벤트 처리할 때 많이 사용됩니다.
=>이 경우에 인터페이스 안에 메서드가 1개인 경우는 람다 표현식으로 작성하는 것이 가능합니다.

5.문법적으로 수업 도중 많이 사용하는 것
=>Anonymous Class입니다. ( Anonymous 이거는 취향을 탐. 보통 백엔드개발자가 선호함. 메모리 절약하는데 이득이 좀 더 있기때문.)

★위 내용이 어려우면 static,local 의 개념만 잘 알고가기. local은 블럭의 개념과 비슷. (블럭=> {}이거임)


6. 실습

=>Outer Class
public class Outer {
	//내포 클래스 - 다른 클래스 안에 만들어진 클래스
	class Inner{
		public int num;
	}
	
	//내포 클래스 안에 static 멤버가 있으면 인스턴스 생성없이 사용할 수 있도록 static을 추가
	static class StaticInner{
		public int num;
		public static int share;
	}
	
	public void method() {
		//메서드 안에 만들어진 클래스 - Local Inner
		//메서드 안에서만 사용이 가능한 클래스
		class LocalInner{
			public int num;
		}
	}
}



7.Anonymous Class(익명 객체 또는 익명 클래스 - 클래스 이름이 없는)
=>작성하는 방법을 알아두어야 합니다★★★.
=>인터페이스를 구현하거나 클래스를 상속받아서 사용을 하고자 하는 경우에 별도의 클래스를 생성하지 않고 사용하는  방법

=>상속이나 구현을 하는 클래스를 만들지 않고 활용

new 인터페이스이름 이나 상위 클래스이름(매개변수 나열){
필요한 메서드 정의
};

 

//메서드가 1개 인 인터페이스
interface Sample{
	//추상 메서드 선언
	public void display();
}

//인터페이스를 구현한 클래스
class SampleImpl implements Sample{

	@Override
	public void display() {
		System.out.println("클래스를 만들어서 사용");
	}
}

public class AnonymousMain {

	public static void main(String[] args) {
		//인터페이스를 구현한 클래스의 인스턴스를 생성해서 메서드 호출
		//인스턴스를 여러 개 만들어야 한다면 클래스를 만드는 것이 효율적
		Sample sample = new SampleImpl();
		sample.display();
		
		//Sample 인터페이스를 anonymous 로 사용
		//인스턴스가 1개만 필요하다면 클래스를 만들지 않는 것이 효율적
		new Sample() {
			@Override
			public void display() {
				System.out.println("클래스를 생성하지 않고 사용");
			}
		}.display();
		
		//배열의 정렬
		String [] ar = {"아담", "강진축구", "프리스톤테일", "카카오택시"};
		
		//배열의 내림차순 정렬
		//Arrays.sort(배열, 비교를 위한 Comparator<T> 인터페이스를 구현한 클래스의 객체)
		//를 호출해야 합니다.
		Arrays.sort(ar, new Comparator<String>() {
			@Override
			public int compare(String o1, String o2) {
				return o2.compareTo(o1);
			}
		});
		
		//배열의 요소를 빠르게 확인
		System.out.println(Arrays.toString(ar));
		
		//하나씩 확인
		for(String app : ar) {
			System.out.println(app);
		}

	}

}


**Lambda(람다)
1.개요
=>jdk 1.7부터 함수형 프로그래밍을 위해서 추가
=>이름없는 함수를 만들기 위해서 사용
=>익명 객체를 만들어서 사용하던 것을 조금 더 간결하게 함수를 대입하는 형태처럼 보이도록 하기 위해서 사용
=>메서드가 1개만 존재하는 인터페이스에서만 사용이 가능

2.작성 방법
(매개변수 나열) -> (내용을 작성)

=>매개변수를 작성할 때는 자료형 과 매개변수 이름을 작성하는데 매개변수 자료형은 생략이 가능합니다.
자료형을 생략하면 호출할 때 자료형을 결정합니다.
=>매개변수가 1개인 경우는 ()생략하는 것이 가능
매개변수가 없거나 2개 이상인 경우는 생략하면 안됨

=>메서드에 return type이 있다면 return 만들면 됩니다.
중괄호 안에 return 하는 문장만 있다면 {}를 생략하는 것이 가능합니다. 
심지어 return도 생략해도 됩니다.

3.이전 작업을 람다를 이용해서 작업

public class LambdaMain {

	public static void main(String[] args) {
		//배열의 정렬
		String [] ar = {"아담", "강진축구", "프리스톤테일", "카카오택시"};
		
		//배열의 내림차순 정렬
		//Arrays.sort(배열, 비교를 위한 Comparator<T> 인터페이스를 구현한 클래스의 객체)
		//를 호출해야 합니다.
				
		//Comparator 인터페이스는 메서드가 1개 밖에 없으므로 람다로 표현하는 것이 가능
		//람다를 만들 때는 인터페이스 이름 과 메서드 이름은 중요하지 않음
		//매개변수의 개수 와 리턴 타입만 확인하면 됩니다.
		//매개변수는 2개이고 리턴 타입은 정수입니다.
		//매개변수가 1개이면 () 생략 가능
		//return 하는 문장만 존재한다면 {} 와 return을 생략하는 것이 가능
		//메서드의 매개변수로 코드(함수)를 대입한 것 처럼 보이도록 함
		//메서드의 매개변수로 코드(함수)를 대입할 수 있는 방식을 함수형 프로그래밍이라고 함
		Arrays.sort(ar, (o1, o2) ->  o2.compareTo(o1));
		
		
		//배열의 요소를 빠르게 확인
		System.out.println(Arrays.toString(ar));
		

	}

}



4.자바가 제공하는 람다 인터페이스
1,3,4,5는 의미가 중요.
어떤 일을 할 때 어떤 모양으로 만들어 줘야 한다는지를 파악하는게 중요.
1)Consumer: 매개변수는 있고 리턴 값은 없는 메서드를 소유한 인터페이스
이런 경우는 System.out.println(); 같은거 밖에 없음.
2)supplier: 매개변수는 없고 리턴값만 있는 메서드를 소유한 인터페이스

3)Function: 매개변수 와 리턴이 모두 있는 메서드를 소유한 인터페이스
변환 작업을 할 때 사용, 굉장히 중요
4)Operator: 매개변수가 있고 리턴도 있는 메서드를 소유한 인터페이스인데 이 인터페이스는 일반적으로 메서드 내부에서 연산 작업 수행
더하기 할 때, 연산할 때 사용.
5)Perdicate: 매개변수가 있고 리턴 값이 boolean인 메서드를 소유한 인터페이스
boolean을 리턴한다는 것은 판정을 한다는 것. 즉 필터링 할 때 사용.


**스트림API
1.개요
배열, List : Collection 이라고도 함.
=>Collection 데이터를 다룰 때 내부 반복자를 만들어서 접근하기 위한 API
내부 반복자를 이용해서 접근하면 반복문이나 빠른 열거를 사용하는 것보다 빠르게 작업을 수행할 수 있습니다.
=>How 보다 What
=>작업의 과정
생성 -> 중간 작업 -> 최종 작업
생성은 배열이나 List를 가지고 수행
(중간 작업은 여러 개를 묶어도 됩니다.)

중간 작업은 스트림의 데이터를 순회하면서 작업을 수행해서 다시 스트림을 리턴하기 때문에 다른 중간 작업을 연속해서 배치하는 것이 가능합니다.

최종 작업은 1번만 수행 가능합니다.
집계를 수행하는 함수나 forEach 처럼 하나씩 순회하면서 작업을 수행하기 위한 함수 또는 collect 처럼 배열이나 List를 생성해주는 함수를 사용합니다.

2.스트림 생성
1)Collection 인터페이스로부터 상속받은 객체는 stream() 이나 paralleStream() 을 호출
=>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.실습

public class StreamMain {

	public static void main(String[] args) {
		//숫자 형태의 문자열의 리스트
		ArrayList<String> list = new ArrayList<>();
		list.add("28");
		list.add("2");
		list.add("3");
		list.add("6");
		list.add("5");
		list.add("9");
		
		//최종 연산을 이용해서 출력
		//forEach 는 매개변수 1개를 갖고 리턴이 없는 메서드를 매개변수로 받음
		//Collection 의 모든 데이터를 매개변수에 대입해서 내용을 수행
		
		//list 안의 데이터를 순차적으로 e에 대입해서 {} 안의 내용을 수행
		//최종 작업만 수행해서 데이터 출력
		//list.stream().forEach(e -> {System.out.println(e);});
		
		//데이터 3개만 출력
		//list.stream().limit(3).forEach(e -> {System.out.println(e);});
		
		//데이터 정렬 후 출력
		//list.stream().sorted().forEach(e -> {System.out.println(e);});
		
		//내림차순 정렬
		//sorted 메서드에 내림차순 정렬을 위한 Comparator 인터페이스를 구현한
		//클래스의 객체를 설정하면 됩니다.
		//Comparator 인터페이스는 매개변수가 2개이고 정수를 리턴하는 메서드 1개만 존재합니다.
		/*
		list.stream()
			.sorted((o1, o2) -> o2.compareTo(o1))
			.forEach(e -> {System.out.println(e);});
		*/
		
		//데이터를 정수로 변환해서 정렬
		//중간 처리 메서드 중에는 Int로 리턴해주는 mapToInt 라는 메서드가 존재하고
		//이 메서드를 사용할 때는 변환에 사용하는 메서드를 설정만 해주면 됩니다.
		//클래스이름::메서드이름
		//문자열을 정수로 변환해서 합계 구하기
		
		/*
		int result = list.stream()
		.mapToInt(Integer::parseInt)
		.sum();
		System.out.println(result);
		*/
	
		//홀수의 합
		//filter: 조건에 맞는 데이터만 추출
		//조건에 맞는 추출하고자 할 때는 하나의 매개변수를 받아서 boolean을 리턴하는
		//람다를 만들어서 대입해주면 됩니다.
		int result = list.stream()
				.mapToInt(Integer::parseInt)
				.filter(o -> o % 2 == 1)
				.sum();
		System.out.println(result);
	}
}


**Thread (굉장히 중요)
=>프로그래밍에서 가장 중요한 개념 중의 하나인데 실제 생성해서 사용하는 경우는 안드로이드나 자바 애플리케이션을 만들 때이고 Web Programming에서는
직접 생성해서 사용하는 경우가 드뭅니다.

1.작업 단위
1)Process: 실행 중인 프로그램
=>프로세서를 할당받아서 실행되는 것
=>한 번 실행되면 자신의 작업이 종료될 때까지 제어권을 다른 프로세스에게 넘기지 않고 계속 수행됩니다.

2)Thread: Process를 작게 나누어서 작업을 수행하는 단위
주의! 쓰레드는 작업을 동시에 하는게 아님 , 동시에 하는것은 parallel임.
=>Thread는 단독으로 실행될 수 없고 Process 안에서 실행되어야 합니다.
=>자신의 작업 도중 쉬는 시간이 생기거나 일정한 시간이 지나면 다른 스레드에게 제어권을 양도 할 수 있습니다.

2. Thread Programming을 할 때 반드시 알아야 될 사항
1)하나의 스레드가 사용 중인 자원은 다른 스레드가 수정하면 안된다.
=>Mutual Exclusion(상호 배제),Synchronus(동기화) 

2)생산자와 소비자 문제
=>소비자는 생산자가 물건을 생성해 주어야만 작업을 수행.

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 나노초 만큼 대기
=>이 메서드를 사용할 때는 InternupedException을 처리해주어야 합니다.
=>실습을 할 때 이 메서드를 사용하는 이유는 일정 시간 이상 대기를 해야만 다른 스레드에게 제어권이 이동되기 때문입니다.
실제 시간의 의미는 거의 없습니다.

5.스레드 생성 실습

//Thread 클래스로 부터 상속받는 클래스를 생성
class ThreadEx extends Thread{
	//public void run 이라는 오버라이딩
	@Override
	//위의 어노테이션은 상위 클래스 나 인터페이스에서 제공하는 메서드가 아닌 경우 에러를 발생시켜
	//주는 어노테이션 입니다.
	public void run() {
		//스레드로 수행할 내용
		//1초마다 Thread 클래스라는 문장을 10번 출력
		for(int i=0; i<10; i++) {
			try {
				Thread.sleep(1000);
				System.out.println("Thread 클래스");
			}catch(Exception e) {
				System.out.println(e.getLocalizedMessage());
			}

		}
	}
}

//Runnable 인터페이스를 구현한 클래스를 생성
class RunnableImpl implements Runnable{

	@Override
	public void run() {
		//스레드로 수행할 내용
		//1초마다 Runnable 인터페이스 라는 문장을 10번 출력
		for(int i=0; i<10; i++) {
			try {
				Thread.sleep(1000);
				System.out.println("Runnable 인터페이스");
			}catch(Exception e) {
				System.out.println(e.getLocalizedMessage());
			}

		}

	}

}


public class ThreadCreate {

	public static void main(String[] args) {
		//Thread 클래스로부터 상속받은 클래스를 이용해서 스레드를 생성하고 실행
		Thread th1 = new ThreadEx();
		//start를 호출하면 run 메서드의 내용을 수행
		th1.start();

		//Runnable 인터페이스를 implements 클래스를 이용해서 스레드를 생성하고 실행
		Thread th2 = new Thread(new RunnableImpl());
		th2.start();

		//Runnable 인터페이스를 Anonymous Class를 이용해서 사용
		Thread th3 = new Thread(new Runnable() {
			public void run() {
				for(int i=0; i<10; i++) {
					try {
						Thread.sleep(1000);
						System.out.println("Anonymous 활용");
					}catch(Exception e) {
						System.out.println(e.getLocalizedMessage());
					}

				}
			}
		});
		th3.start();

		//Runnable 인터페이스는 public void run 메서드 1개만 소유
		//람다를 이용해서 작성하는 것도 가능
		Thread th4 = new Thread(()->{
			for(int i=0; i<10; i++) {
				try {
					Thread.sleep(1000);
					System.out.println("람다 활용");
				}catch(Exception e) {
					System.out.println(e.getLocalizedMessage());
				}

			}
		});
		th4.start();
	}

}


6.Daemon Thread - 소켓서버(게임 회사에서) 만드는데 사용.
=>다른 스레드가 수행 중이 아니면 자동으로 종료되는 스레드
=>스레드의 역할을 도와주는 보조적인 목적으로 사용
=>start 하기 전에 setDaemon 메서드를 호출하면 되는데 true 값을 설정해주면 됩니다.

 

public class DaemonThread {

	public static void main(String[] args) {
		//1부터 10까지를 1초씩 딜레이하면서 출력해주는 스레드
		Thread th = new Thread(() -> {
			for(int i=0; i<10; i++) {
				try {
					Thread.sleep(1000);
					System.out.println(i);
				}catch(Exception e) {
					System.out.println(e.getLocalizedMessage());
				}
			}
		});
		//데몬 스레드로 설정 - 다른 작업이 없으면 자동을 종료
		th.setDaemon(true);
		th.start();
		try {
			Thread.sleep(3000);
			System.out.println("메인 종료");
		}catch(Exception e) {}
		

	}

}


7.Thread의 Priority(우선 순위)
=>여러 개의 스레드가 수행될 때 어떤 스레드가 먼저 되고 어떤 스레드가 나중에 수행될지는 알 수 없습니다.
=>우선 순위를 설정하면 확률적으로 먼저 또는 자주 실행되도록 할 수 있습니다.
=>setPriority 메서드를 이용해서 설정합니다.
setPriority(int priority)
매개변수가 정수이지만 아무 정수나 사용하는 것은 안되고 제약이 있습니다.
일반 정수를 사용해도 에러는 발생하지 않지만 되도록이면 Thread.MAX_PRIORITY,NORMAL,MIN의 상수를 사용하는 것을 권장합니다.


★개발자들의 관습★
-일반적으로 개발자들은 클래스의 인스턴스를 1개만 생성할 때는 변수의 이름을 클래스 이름과 동일하게 만들고 첫글자만 소문자로 변경합니다.
String string = "dkdkdk";

-어떤 클래스로부터 상속받는 클래스를 1개만 만들 때는 클래스 이름 뒤에 Ex를 붙입니다.

-Thread 클래스로부터 상속받는 클래스
class ThreadEx extends Thread{

}

ThreadEx th = new ThreadEx();
thread th = new ThreadEx();

-인터페이스를 구현한 클래스를 1개만 만들 때는 클래스 이름 뒤에 lmpl을 붙입니다.

-Runnable 인터페이스를 구현한 클래스
class Runnablelmpl implements Runnable{

}

-extends 하거나 implements 했을 때 변수의 이름은 상위 클래스 이름이나 인터페이스 이름을 사용

ArrayList<String> list =new ArrayyList<>();
 (이렇게는 잘 안쓰고)
List<String> list = new ArrayList<>();
 (이렇게 많이 씀)



<오늘 기억할 내용>
★★★ 똑같은 코드를 두번이상 쓰지 않도록 해야한다. 한번 고칠 때 중복된 코드가 있으면 하나를 고치려면 여러개를 수정해야됨. 유지보수가 어려워짐. 이를 스파게티 코드라고 함.
-관계형 데이터 베이스 vs NoSQL
관계형 DB : 클래스를 나눠서 만들고 나중에 하나로 합침(조인★★★), 이 작업이 가장 시간이 많이 듦.
NoSQL(비정형 데이터에 씀, 개념 꼭 알아두기) : 한 클래스 안에다가 만드는 것. Embedding -> IoT(최근, 인터넷이 되는것이 임베딩에서 IOT라고 부르게됨) : 기본적으로 임베딩이라는 표현으로 기억하고 있기.
-spring을 사용하는 것에는 legacy, Boot가 두가지가 있는데 최근엔 Boot를 많이씀.
-★★★상속(is a)은 부모->자식 이렇게 표현하는게 아님. 물려주는게 아님. 차라리 super, base 이렇게 표현하기. 
 혹은 부모라고 알고 있던것:super,based 클래스 / 자녀 라고 알고 있던것 : sub,Derived 클래스 라고 함.
 오히려. 동일한 용도의 하위클래스가 다수일 때 하위클래스에서 상위클래스로 물려주는것을 상속이라고 함. 이런것도 중복을 줄이기 위함. 부모꺼를 자식으로 물려주는것이 아님을 반드시 기억하기.
-has a : 부모->자녀 이게 바로 has a, 즉 이것이 내포.   
-★구현을 해놓고 최적화(커스터마이징) 해나가는 것이 중요. 커스터 마이징 해나가는 습관을 들이기.
-혼자 공부하는게 어려운 것은 누가 내가 틀렸다고 잡아주지 않는것. 그러니 같이 할 수 있다면 같이하는게 좋음.
-JPA도 꼭 알아두기.
-자바는 함수형 프로그래밍을 지원하지않음. 사용하는거처럼 보이게 하는것. 
 자바 즉 JV 를 기반으로 함수형 프로그래밍을 가미한 언어들이 코틀린,자이선 등등 이런 것을 자바기반 언어(JV 기반언어라고함.)
-웹에다가 뭘 할수 있는건 자바 뿐.
-요즘엔 데이터가 많기 때문에 DB만 쓰는게 아니라 텍스트 파일로 보관을 많이함. 로그 많이 사용함.
-티맥스 정도만 가도 삶의 질이 다름. 요즘엔 아이티 일 하는 사람들 분당가서 일 하고싶어함.
-조건에 맞는 데이터를 찾는 연습을 많이 해봐야됨. 
-어려우면 왜 해야되는지만이라도 기억하고 있으면됨.
-변수를 찾는 것이 메소드를 불러오는 것보다 훨씬 빠름.
 메서드를 불러올 땐 스택을 만들고 사용한뒤 스택을 해제해줘야함. 메모리를 할당해주고 다시 수거해주는 작업이 필요하다는 말임.
-널포인트 익셉션이 자바에서 가장 많이 일어나는 에러. 값이 널이라서 에러가 뜨는게 아님.
 그래서 .을 찍기전에 if(str != null) 을 꼭 써줘야했었음. 이제는 isPresent()를 써줌.
-데이터 베이스 연동 공부할 때 Optional이 꼭 나와야 정상. 안 가르쳐주면 데이터 베이스 연동 공부 처음부터 다시해야 한다고 생각하기.
 (Optional 이 생겨난 것은, null인지 아닌지를 빠르게 확인하기 위함. 굉장히 중요한 것임. 이거 때문에 새로운 언어들이 나옴.)
 (데이터 베이스에서 null 저장 여부는 데이터의 크기와 밀접하게 연관됨.)
 -상태변화 = 수명주기
 
 ★★★처음에 시작하는 회사가 저어어어엉말 중요. 업종도 처음 회사에 따라 가게됨. 점프 뛰는것도 처음 회사가 어디냐에 따라 한계가 정해짐.★★★