본문 바로가기
Java/자바공부

부트캠프가 끝난뒤...(3)

by 티코딩 2023. 4. 27.

ㅇ 람다식

람다식은 간단히 메서드를 간단하게 표현하기 위한 문법 요소다.

void hello(){
	System.out.println("안뇽");
    }
//위의 메서드를
() -> System.out.println("안뇽");

이렇게 간단히 표현할 수 있다. 람다식은 반환타입과 이름을 생략할 수 있다. 이름을 생략하기에 익명함수라고 부르기도 함.

메서드 바디에 실행문이 한개만 존재하면, 중괄호와 return문을 생략할 수 있다. 이 경우엔 세미콜론까지 생략해야 함.

int sum(int a, int b){
	return a + b;
    }
//람다식으로 표현하면,
(int a, int b) -> a + b

이런 람다식은 사실은 객체다. 자세히 말하면 익명 객체. 익명 객체는 익명 클래스를 통해 만들 수 있다. 익명 클래스는 객체의 선언과 생성을 동시에 해 오직 하나의 객체를 생성하고, 단 한번만 사용되는 일회용 클래스다.

생성과 선언을 동시에 하면,

new Object(){
	int sum(int a, int b){
    	return a + b;
        }
    }

 

ㅇ 함수형 인터페이스

쉬운 예로

@FunctionalInterface
interface ExampleFunction{
	int sum(int a, int b);
    }
    
public class Ex {
	public static void main(String[] args){
    	ExampleFunction exampleFunction = (a, b) -> a + b;
        System.out.println(exampleFunciton.sum(3,5));
        }
    }

ExampleFunction은 함수형 인터페이스로 람다식을 참조할 참조변수를 선언할때 사용하기 위해 필요함.

 

ㅇ 메서드 레퍼런스

메서드 참조는 불필요한 매개변수를 제거할 때 주로 사용함.

(left, right) -> Math.max(left, right)

이건 두개의 값을 받아 더 큰 값을 리턴하는 식이다.

이 경우 메서드 참조를 이용하면, 

//클래스이름 :: 메서드이름
Math :: max	//메서드 참조
IntBinaryOperator operato = Math :: max;

 

ㅇ 스트림

스트림은 배열, 컬렉션의 저장 요소를 하나씩 참조해 람다식으로 처리할 수 있도록 해주는 반복자

스트림을 사용하면 선언형 프로그래밍 방식으로 데이터를 처리할 수 있어 인간 친화적, 직관적인 코드작성이 가능하다.

public static void main(Stirng[] args){
	List<Integer> numbers = List.of(1, 2, 3, 4, 5);
    int sum = 0;
    
    for(int number : numbers){
    	if(number > 4 && (number % 2 ==0)){
        	sum += number;
            }
        }
      
    System.out.println("명령형 프로그래밍" + sum);
}
public static void main(Stirng[] args){
	List<Integer> numbers = List.of(1, 2, 3, 4, 5);
    
    int sum = 
    		numbers.stream()
            			.filter(number -> number > 4 && (number % 2 == 0))
                        .mapToInt(number -> number)
                        .sum();
    System.out.println("선언형 프로그래밍" + sum);
    }
}

위의 코드는 명령형 프로그래밍, 아래 코드는 선언형 프로그래밍이다. 차이가 보이는가. 밑의 선언형 프로그래밍의 코드가 훨씬 직관적이고 간결하다.

그리고 스트림을 사용하면 데이터 소스가 무엇이냐에 관계없이 같은 방식으로 데이터를 가공 및 처리를 할 수 있다.

 

ㅇ 스트림의 4가지 특징

1. 스트림 처리 과정은 생성, 중간 연산, 최종 연산 세 단계의 파이프라인으로 구성될 수 있다.

2. 스트림은 원본 데이터 소스를 변경하지 않는다.

3. 스트림은 일회용이다.

4. 스트림은 내부 반복자이다.

 

ㅇ 스트림의 생성

배열을 데이터 소스로 하는 스트림 생성은 Array클래스의 stream() or Stream클래스의 of()로 할 수 있다.

//.stream() 사용
String[] arr = new String[]{"a","b","c"};	//문자열 배열선언,할당
Stream<String> stream = Arrays.stream(arr);	//문자열 스트림 생성
stream.forEach(System.out::println); 	//출력

//.of() 사용
String[] arr = new String[]{"a","b","c"};	//문자열 배열선언,할당
Stream<String> stream = Stream.of(arr);		//문자열 스트림 생성
stream.forEach(System.out::println);	//출력

참고로 Arrays에는 DoubleStream, IntStream, LongStream도 있다.

Collection으로부터 stream()메서드를 활용해 스트림을 생성할수도 있다.

List<Integer> list = Arrays.asList(1,2,3,4,5);
Stream<Integer> stream = list.stream();

stream.forEach(System::out::print);

ㅇ 스트림의 중간 연산

중간연산자라는것이 존재한다.

가장 많이 사용되는 것이 필터링, 매핑, 정렬등이 있다.

Stream
	.of(1,2,3,4)	//데이터소스
    .filter(n -> n % 2 != 0)	//중간
    .map(n -> n *2)		//연산
    .forEach(n -> System.out.println(n));	//최종 연산

기본적인 구조는 이렇다.

필터링 - 조건에 맞는 데이터들만을 정제하는 역할을 하는 중간 연산자. distinct(), filter() 

names.stream()
                .distinct() //중복 제거
                .forEach(element -> System.out.println(element));
        System.out.println();

매핑 - 원하는 필드만 추출하거나 특정 형태로 변환할 때 사용하는 중간 연산자. map() 람다식으로 정의

names.stream()
                .map(element -> element.toUpperCase()) // 요소들을 하나씩 대문자로 변환
                .forEach(element->System.out.println(element));

정렬 - 정렬할 때 사용함. sorted()

List<String> animals = Arrays.asList("Tiger", "Lion", "Monkey", "Duck", "Horse", "Cow");
				
				// 인자값 없는 sort() 호출
        animals.stream().sorted().forEach(System.out::println);

ㅇ 스트림의 최종 연산

위에서 계속 썼던 .forEach()가 최종 연산 메서드다. 그 외엔 뭐가 있을까?

기본 집계 - sum(), count(), average(), max(), min()

매칭 - allMatch(), anyMatch(), noneMatch()

요소 소모 - reduce()

요소 수집 - collect()

 

ㅇ 스레드

프로세스 - 실행 중인 애플리케이션을 의미함. 데이터, 컴퓨터 자원, 스레드로 구성됨.

스레드 - 데이터와 애플리케이션이 확보한 자원을 활용해 소스 코드를 실행함. 하나의 코드 실행흐름.

메인 스레드 - 자바 애플리케이션을 실행하면 가장 먼저 실행되는 메서드는 main 메서드. 

멀티 스레드 - 하나의 프로세스는 여러개의 스레드를 가질 수 있음. 이것이 멀티 스레드. 여러 스레드가 동시에 작업을 수행할 수 있음.

자바에서 스레드가 수행할 코드를 클래스 내부에 작성하고, run()이라는 메서드 내에 스레드가 처리할 작업을 작성하도록 규정되어 있다.

run() 메서드는 Runnable 인터페이스, Tread클래스에 정의되어 있다.

자바에서 스레드를 생성하고 실행하는 방법은 두가지다.

1. Runnable 인터페이스를 구현한 객체에서 run()을 구현해 스레드를 생성, 실행하는 방법

class Thread1 implements Runnable {
	public void run() {
    	for(int i = 0; i < 100; i++){
        	System.out.println("#");
            }
    }
}
public class ThreadExample1 {
    public static void main(String[] args) {

        // Runnable 인터페이스를 구현한 객체 생성
        Runnable task1 = new Thread1();

        // Runnable 구현 객체를 인자로 전달하면서 Thread 클래스를 인스턴스화 하여 스레드를 생성
        Thread thread1 = new Thread(task1);

        // 위의 두 줄을 아래와 같이 한 줄로 축약할 수도 있습니다. 
        // Thread thread1 = new Thread(new Thread1());

    }
}

2. Thread 클래스를 상속받은 하위 클래스에서 run()을 구현해 스레드를 생성하고 실행하는 방법

class Thread2 extends Thread {
	public void run() {
    	for(int i = 0; i < 100; i++){
        	System.out.println("#");
            }
    }
}
public class ThreadExample2 {
    public static void main(String[]args) {

        // Thread 클래스를 상속받은 클래스를 인스턴스화하여 스레드를 생성
        Thread2 thread2 = new Thread2();
        
        thread2.start();//작업스레드를 실행시켜, run()내부의 코드를 처리
    }
}

 

ㅇ JVM(Java Virtual Machine)

자바로 작성한 소스 코드를 해석해 실행하는 별도의 프로그램. 자바가 운영체제로부터 독립적인건 JVM 덕분.

JVM구조

1. 자바로 소스 코드를 작성하고 실행하면, 컴파일러가 실행되고, 컴파일의 결과로 .java확장자가 .class 확장자로 변환됨.

2. 이후에 JVM은 운영체제로부터 소스 코드 실행에 필요한 메모리를 할당받음. 여기가 Runtime Data Area. 

3. 이후에 Class Loader가 바이트 코드 파일을 JVM 내부로 불러 런타임 데이터 영역에 적재시킴. 자바 소스 코드를 메모리에 로드 시킴.

4. 로드 완료되면 이제 실행 엔진이 런타임 데이터 영역에 적재된 바이트 코드를 실행시킴.

 

runtime data area

여기서 힙 영역과 스택 영역이 나온다.

ㅇ 힙 영역

스택영역은 자료구조의 스택처럼 데이터를 저장함. 프링글스 통 처럼 가장 마지막에 들어간 데이터가 가장 먼저 나오는 구조다.

메서드가 호출되면 그 메서드를 위한 공간인 Method Frame이 생성됨. 메서드 내부에서 사용하는 다양한 값들이 임시로 저장된다.

이런 Method Frame이 Stack에 호출되는 순서대로 쌓이게 되는데, Method의 동작이 완료되면 역순으로 제거됨.

ㅇ 스택영역

JVM에는 하나의 Heap 영역이 존재함. JVM이 작동되면 힙영역은 자동생성됨. 힙 영역은 실제 객체의 값이 저장되는 공간임.

우리가 객체를 다룬다는 건 Stack 영역에 저장되어 있는 참조 변수를 통해 Heap 영역에 존재하는 객체를 다룬다는 의미. Heap 영역은 실제 객체의 값이 저장되는 공간임.

 

ㅇ Garbage Collection

자바에서 메모리를 자동으로 관리하는 프로세스가 포함되어있음. 가비지 컬렉션은 더 이상 사용하지 않는 객체를 찾아 삭제해 메모리를 확보한다. Heap 영역의 객체는 대부분 일회성이고, 메모리에 남아있는 기간이 대부분 짧다는 전제로 설계되어있다. 영역안에서 Young 영역, Old 영역이 나뉜다. Young 영역에는 새롭게 생성된 객체가 할당되는 곳이다. 이 영역에서 활동하는 가비지 컬렉터를 Minor GC. Old 영역에서는 Young영역에서 상태를 유지하고 살아남은 객체들이 복사되는 곳으로 보통 Young 영역보다 크게 할당되고 크기가 큰 만큼 가비지가 적게 발생함. 여기서 활동하는 가비지 컬렉터는 Major GC.

 

'Java > 자바공부' 카테고리의 다른 글

부트캠프가 끝난뒤...(5)  (0) 2023.05.11
부트캠프가 끝난뒤...(4)  (0) 2023.05.03
부트캠프가 끝난뒤...(2)  (0) 2023.04.26
부트캠프가 끝난뒤...(1)  (0) 2023.04.11
setter 대신 builder  (0) 2023.04.09