Flaming Soccer ball
JAVA

27일차 22.10.28

leo lee 2022. 10. 28.
반응형

is a - 상속(super 또는 based class -> sub 또는 derived class  / has a - 내포(클래스 안에서 다른 클래스의 인스턴스 사용


**Nested Class(내포 클래스) -> Embedding 가지고 있는 것 = IOT ! 인터넷이 되면 IOT  아니면 Embedded임 
-> 클래스 안에 존재하는 클래스
-> 자바는 클래스 안에 클래스를 생성하는 문법을 지원
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.실습

=>

package naver.srlee3637.nestedclass;

public class Outer {
	//내포 클래스 - 다른 클래스 안에 만들어진 클래스
	class Inner{
		public int num;
	}
	
	//내포 클래스 안에 static 멤버가 있으면 인스턴스 생성 없이 사용할 수 있도로 static을 추가
	//static이 있는 변수는 그냥 사용할 수 있지만, static이 없는 경우는 instance를 생성해서 사용해야함
	static class StaticInner{
		public int num;
		public static int share; 
	}
	
	public void method() {
		//메서드 안에 만들어진 클래스 - Local Inner
		//메서드 안에서만 사용이 가능한 클래스
		//만약 이 Inner를 다른 메서드도 사용한다면 그냥 Inner로 바깥으로 빼서 사용하면 된다.
		class LocalInner{
			public int num;
			public void method() {
				
			}
		}
	}

}
package naver.srlee3637.nestedclass;

import java.util.Arrays;
import java.util.Comparator;

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

class SampleImpl implements Sample{
	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() {
			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);
		}
	}

}



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

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



**Lambda(람다)


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

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

-> 매개변수를 작성할 때는 자료형과 매개변수 이름을 작성하는데 매개변수 자료형은 생략이 가능
자료형을 생략하면 호출할 때 자료형을 결정

-> 매개변수가 1개인 경우는 ( )를 생략 가능
매개변수가 없거나 2개 이상인 경우는 생략 불가

-> 메서드에 return type이 있다면 return 만들면 됨
중괄호 안에 return하는 문장만 있다면 { }를 생략 가능 
이때는 return도 생략가능

3. 실습

=>

package naver.srlee3637.lambda;

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;

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

class SampleImpl implements Sample{
	public void display() {
		System.out.println("클래스 만들어서 사용");
	}
}

public class LambdaMain {

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

			@Override
			public int compare(String o1, String o2) {
				return o2.compareTo(o1);
			}
			
		});
		
//		Collections.sort(ar, (o1,o2) -> {
//			o2.compareTo(o1);
//		});
		
		//두개 다 같은 거임, 위에 anonymous보다 가독성이 떨어짐
		//Comparator 인터페이스는 메서드가 1개 밖에 없으므로 람다로 표현하는 것이 가능
		//람다를 만들 때는 인터페이스 이름과 메서드 이름은 중요하지 않음
		//매개변수의 개수와 리턴 타입만 확인하면 됨
		//매개변수는 2개이고 리턴 타입은 정수
		//매개변수가 1개면 ( ) 생략 가능
		//return 하는 문장만 존재한다면 { } 와 return 생략 가능
		//메서드의 매개변수로 코드(함수)를 대입한 것 처럼 보이도록 함
		//메서드의 매개변수로 코드(함수)를 대입할 수 있는 방식을 함수형 프로그래밍이라고 함 - 
		//java는 함수형 프로그래밍을 지원하지는 않고, 보이는 것을 그렇게 한다. 
		Arrays.sort(ar, (o1,o2) -> {return o2.compareTo(o1);});
		System.out.println(Arrays.toString(ar));
		Arrays.sort(ar, (o1,o2) -> o1.compareTo(o2));
		
		
		//배열의 요소를 빠르게 확인
		System.out.println(Arrays.toString(ar));
		
		//하나씩 확인
		for(String app : ar) {
			System.out.println(app);
		}
	}

}



4. 자바가 제공하는 람다 인터페이스
1) Consumer: 매개변수는 있고 리턴 값은 없는 메서드를 소유한 인터페이스(일만 하고 끝나는 것)

2) Supplier: 매개변수는 없고 리턴 값만 있는 메서드를 소유한 인터페이스

3) Function: 매개변수 와 리턴 값이 모두 있는 메서드를 소유한 인터페이스

4) Operator: 매개변수가 있고 리턴 값도 있는 메서드를 소유한 인터페이스인데 이 인터페이스는 일반적으로 메서드 내부에서 연산 작업 수행

5) Peridicate: 매개변수가 있고 리턴 값이 boolean인 메서드를 소유한 인터페이스(판정, 필터링 할 때)



**스트림 API 
Mapping: 변환
Filtering: 추출
Reduce: 연산

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) sorted(비교 처리를 수행해주는 람다): 람다를 기준으로 정렬
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. 실습

=>

package naver.srlee3637.streamapi;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.OptionalDouble;

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);});//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개만 존재

		//anonymous 
		//      list.stream().sorted(new Comparator<String>() {
		//
		//		@Override
		//		public int compare(String o1, String o2) {
		//			return o2.compareTo(o1);
		//		}
		//      }).forEach(e -> {System.out.println(e);});

		//람다식
		list.stream()
		.sorted((o1,o2) -> o1.compareTo(o2))
		.forEach(e -> {System.out.println(e);});

		//데이터를 정수로 변환해서 정렬
		//중간 처리 메서드 중에는 Int로 리턴해주는 mapToInt라는 메서드가 존재하고
		//이 메서드를 사용할 때는 변환에 사용하는 메서드를 설정만 해주면 됨
		//클래스이름::메서드이름
		//문자열을 정수로 변환해서 합계 구하기
		list.stream()
		.map((o) -> {return Integer.parseInt(o);}) //== .mapToInt(Integer::parseInt) 같은 표현임
		.sorted((o1,o2) -> o1 -o2)
		.forEach(e -> {System.out.println(e);});

		//문자열을 정수로 변환해서 합계 구하기
		int result = list.stream()
				.mapToInt(Integer::parseInt)
				.sum();
		System.out.println(result);

		//홀수의 합
		//filter: 조건에 맞는 데이터만 추출
		//조건에 맞는 추출하고자 할 때는 하나의 매개변수를 받아서 boolean을 리턴하는 
		//람다를 만들어서 대입해주면 됨
		int result1 = list.stream()
				.mapToInt(Integer::parseInt)
				.filter(o -> o % 2 == 1)
				.sum();
		System.out.println(result1);
		
		//홀수의 평균
		//일반적으로 생각하기에는 평균의 결과가 정수나 실수가 나와야 하는데
		//자바에서는 OptionalDouble이 됨
		//Optional이 붙으면 null을 저장할 수 있는 자료형이 되며
		//isPresent 라는 메서드를 이용해서 null 여부를 판단하고
		//get 이라는 메서드로 데이터를 가져옴
		OptionalDouble result2 = list.stream()
				.mapToInt(Integer::parseInt)
				.filter(o -> o % 2 == 1)
				.average();
		//min, max, average 는 optional로 return

		//연산을 정상적으로 수행한 경우
		if(result2.isPresent()) {
			System.out.println(result2.getAsDouble());
		}else {
			System.out.println("평균 계산 실패 - 아마도 데이터가 없는 것 같음");
		}
		
		//숫자의 경우는 크기 비교가 가능해서 별도의 인스턴스를 대입하지 않아도 정렬이 되고
		//내림차순을 하고자 하는 경우 reverse 옵션을 설정해주면 됨
		list.stream()
		.map(Integer::parseInt)
		.sorted(Comparator.reverseOrder())
		.forEach(e -> System.out.println(e));
		
		list.stream()
		.mapToInt(Integer::parseInt)
		.sorted()
		.forEach(e -> System.out.println(e));
	}
}







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

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

2) Thread: Process를 작게 나누어 작업을 수행하는 단위
-> Thread는 단독으로 실행될 수 없고 Process안에서 실행되어야 함
-> 자신의 작업 도중 쉬는 시간이 생기거나 일정한 시간이 지나면 다른 스레드에게 제어권을 양도 할 수 있음


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

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

3) Dead Lock
-> 결코 발생할 수 없는 사건을 무한정 기다리는 것

3. Java에서 Thread를 생성하는 기본적인 방법
1) Thread 클래스로 부터 상속받는 클래스를 만들고 public void run이라는 메서드에 Thread로 수행할 내용을 작성한 후 인스턴스를 만들고 
start 메서드를 호출

2) Runnable 인터페이스로부터 상속 받는 클래스를 만들고 public void run 이라는 메서드에 Thread로 수행할 내용을 작성한 후 
Thread class의 인스턴스를 만들 때 생성자에 생성한 클래스의 인스턴스를 대입하고 Thread class의 인스턴스가 start 메서드를 호출

 

실습

=>

package naver.srlee3637.thread;

class ThreadEx1 extends Thread{
	public void run() {
		for(int i = 0; i < 10; i++) {

			try {
				Thread.sleep(1000);
				System.out.println(i);

			}catch(Exception e) {}
		}
	}
}

public class ThreadMain {

	public static void main(String[] args) {
		new ThreadEx().start();
		new ThreadEx().start();
		
		
	}

}



3) Callable 인터페이스를 구현한 클래스를 이용해서 생성 가능

4) Thread.sleep(long msec [,long nano]) 여기서 [  ]표현은 있을 수도 있고 없을 수도 있다라는 표현 
-> msec 밀리초 동안 현재 스레드를 중지
nano를 입력하면 msec밀리초 + nano 나노초 만큼 대기 
-> 이 메서드를 사용할 때는 InterruptedException을 처리해줘야함 
-> 실습을 할 때 이 메서드를 사용하는 이유는 일정 시간 이상 대기를 해야만 다른 Thread에게 제어권이 이동 되기 때문입니다.

 

5) 실습

=> 

package naver.srlee3637.thread;

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

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

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

}

public class ThreadCreate {

	public static void main(String[] args) {

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

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

//		new ThreadEx().start();
//		new RunnableImpl().run();

		//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("Annonymous 활용");
					} catch (InterruptedException e) {
						System.out.println(e.getLocalizedMessage());
					}
				}		

			}
		});
		th3.start();

		//Runnable 인터페이스를 Anonymous Class를 이용해서 사용
		Thread th4 = new Thread(() ->  {
			for(int i = 0; i < 10; i++) {
				try {
					Thread.sleep(1000);
					System.out.println("Lambda 활용");
				} catch (InterruptedException e) {
					System.out.println(e.getLocalizedMessage());
				}
			}		
			
		});
		th4.start();
	}

}


6) Deamon Thread
-> 다른 Thread가 수행 중이 아니라면 자동으로 종료되는 Thread
-> Thread의 역할을 도와주는 보조적인 목적으로 사용
-> start 하기 전에 setDaemon 메서드를 호출하면 되는데 true 값을 설정해주면 됨

 

실습

=>

package naver.srlee3637.thread;

public class DaemonThread {

	public static void main(String[] args) {
		//1부터 10까지를 1초씩 딜레이하면서 출력해주는 Thread
		Thread th = new Thread(() ->  {
			for(int i = 0; i < 10; i++) {
				try {
					Thread.sleep(1000);
					System.out.println(i);
				} catch (InterruptedException e) {
					System.out.println(e.getLocalizedMessage());
				}
			}		
		});
		//Daemon Thread 설정 - 
		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(10), NORMAL(5), MIN_PRIORITY(1)를 사용하는 것을 권장함 







NullPointerException
-> null이 멤버를 호출해서 발생 시키는 예외, null이어서 발생하는 오류가 아님
String str = "hello"
System.out.println(str); -> hello
str = null;
System.out.println(str); -> null
if(str != null) 이렇게 하지말고 스트링이 아닐 경우
.isPresent( )를 사용하자
System.out.println(str.toUpperCase( )); -> NullPointerException

DB 연동
PrimaryKey - Optional

일반적으로 개발자들은 클래스의 인스턴스를 1개만 생성할 때는 변수의 이름을 클래스의 이름과 동일하게 만들고 첫 글자만 소문자로 변경

String string = "dkdkdkk"

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

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

}


인터페이스를 구현한 클래스를 1개만 만들 때는 클래스 이름 뒤에 Impl을 붙임

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

}

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

ThreadEx th = new ThreadEx( )보다는
Thread th = new ThreadEx( )로 만드는 경우가 더 많음

ArrayList<String> list = new ArrayList<>( );
List<String> list = new ArrayList<>( );



































반응형

'JAVA' 카테고리의 다른 글

30일차 22.11.02 JSON파일 읽어오기  (0) 2022.11.02
28일차 22.10.31  (0) 2022.10.31
26일차 22.10.27  (0) 2022.10.27
25일차 22.10.26  (0) 2022.10.26
24일차 22.10.25  (0) 2022.10.25

댓글