Stream API에 map()에는 람다식과 메서드 참조가 인수로 들어가야한다고 한다.

여기서 람다식은 익명클래스를 간결하게 작성하기 위해 나온 문법이다.

 

익명 클래스

이름 없는 클래스

일회성

인터페이스 구현 또는 추상 클래스 상속받아 그 메서드를 바로 정의할 때 사용

클래스 파일 따로 정의하지 않고, 코드 내에서 즉석으로 구현하는 방식

java 8 이전 방식 8버전에서도 사용해도 에러는 발생 안함

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello from Anonymous Class!");
    }
};
new Thread(runnable).start();

람다식

익명클래스를 더 간단하게 표현하기 위한 방식(Java 8이후)

함수형 인터페이스(구현해야할 메서드(추상메서드)가 하나뿐인 인터페이스)를 구현할 때 사용가능

간결한 코드, 가독성 높은 코드

Runnable runnable = () -> System.out.println("Hello from Lambda!");
new Thread(runnable).start();

메서드 참조

람다식보다 간결하게 하는 기능

기존 메서드나 생성자를 참조하여 더 간단하게 표현

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// 람다식
names.forEach(name -> System.out.println(name));

// 메서드 참조
names.forEach(System.out::println);

4가지 유형 존재

  1. 정적 메서드 참조: ClassName::staticMethod
  2. 특정 객체의 인스턴스 메서드 참조: instance::instanceMethod
  3. 임의 객체의 인스턴스 메서드 참조: ClassName::instanceMethod
  4. 생성자 참조: ClassName::new

 

본론으로 돌아와서

Stream API 의 map()과 같은 메서드는 인수에 Function<T, R> 이라는 함수형 인터페이스를 구현해야한다.

그걸 람다식과 메서드 참조로 하는 것이다.

Function<T, R> 에는 R apply(T t); 라는 추상 메서드가 존재한다.

이걸 구현해줘야하는 것

익명 클래스, 람다식, 메서드 참조 코드를 보면

// 익명 클래스 방식
Function<String, Integer> lengthFunction = new Function<String, Integer>() {
    @Override
    public Integer apply(String s) {
        return s.length(); // 문자열의 길이를 반환
    }
};

// 람다식
Function<String, Integer> lengthFunction = s -> s.length();// 메서드를 구현

// 메서드 참조
Function<String, Integer> lengthFunction = String::length;// 이미 있는걸 가지고 와 구현

 

 

추가적으로 

map과 mapToInt(Double, Long) 

map은 모든 타입의 스트림 사용가능

-> 변환 후 Stream<객체형>

mapTorInt 기본형 스트림으로 변환할 때 사용

-> 변환 후 기본형Stream 

-> 바로 sum(), average() 같은 계산 메서드 사용가능

'공부일지 > Java' 카테고리의 다른 글

JAVA, 스트림(Stream)  (0) 2024.10.23
[JAVA] Arrays.asList()  (0) 2024.07.04
[JAVA] String, StringBuffer, StringBuilder  (0) 2024.07.01
[JAVA] 6/21에 배운 것  (0) 2024.06.26
[JAVA] 6/20 배운거 클래스  (0) 2024.06.20

스트림(Stream)이란 무엇인가

스트림은 자바 8에서 도입된 기능으로 컬렉션(Collection)과 같은 데이터 소스를 함수형 프로그래밍 방식으로 처리할 수 있도록 도와주는 API 이걸 사용해 데이터를 필터링, 매핑, 정렬, 집계 등 빠르고 직관적으로 수행가능

 

컬렉션(Collection) 인터페이스에 디폴트(Default) 메서드로 stream()이 정의돼있는데 이 메서드를 사용해서 해당 컬렉션의 데이터 흐름인 Stream을 만들 수 있다.

 

List<String> list = Arrays.asList("apple", "banana", "orange");

// 컬렉션을 스트림으로 변환하여 각 요소를 출력
list.stream().forEach(System.out::println);  // apple, banana, orange 출력

 

스트림의 특징 3가지

1. 지연 연산: 중간연산은 지연연산, 최종연산이 호출 전까지는 데이터를 처리하지 않는다.

     -> 성능 최적화

2. 일회성: 만들어진 스트림은 한 번의 연산 후 더 이상 사용할 수 없다.

     -> 그래서 일반적으로 set.stream()으로 변수에 넣지 않고 바로 사용

3. 순차 또는 병렬 처리: 순차 처리 기본, parallelStream()을 통해 병렬 스트림 생성하여 동시에 처리 가능

 

스트림 연산 종류

1. 중간 연산(Intermediate Operations)

중간 연산은 스트림 변형, 필터링 연산

- filter(): 조건에 맞는 요소만 남기기

- map(): 각 요소 다른 형태로 변환

- sorted(): 요소 정렬

- distinct(): 중복제거

 

2. 최종연산 (Terminal Operation)

스트림의 연산을 완료하고 결과 반환, 이 이후 해당 스트림은 사용 불가(일회성)

- forEach(): 각 요소에 대해 작업 수행

- collect(): 스트림을 컬렉션이나 다른 형태로 변환

- reduce(): 스트림 요소를 하나로 합침

 

스트림 장점

- 간결한 코드: 복잡한 반복문 대신 간결한 함수형 코드 작성 가능

- 함수형 프로그래밍 지원: filter(), map(), reduce() 같은 연산을 통해 데이터를 선언적 처리

- 병렬 처리: 병렬 스트림을 통해 빠르게 처리

 

외부 반복 VS 내부 반복

외부 반복: for, while 과 같은 원래 쓰던 반복

내부 반복: 스트림을 사용하여 데이터를 처리하는 방식 데이터의 순회와 처리는 스트림에서 처리

 

차이점 요약

1. 제어권의 차이

외부: 개발자가 반복 설계

내부: 스트림이 알아서, 처리 로직만 제공하면 됨

2. 코드의 간결함

외부: 코드 장황

내부: 간결

3. 병렬 처리

외부: 스레드 관리 수동관리

내부: 병렬 스트림 통해 병렬처리 간편

 

연습코드

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamExample {
    public static void main(String[] args) {
        // 리스트 초기화
        List<String> fruits = Arrays.asList("apple", "banana", "avocado", "grape", "orange");

        // 1. 'a'로 시작하는 문자열 필터링
        // 2. 각 문자열을 대문자로 변환
        // 3. 결과를 알파벳 순으로 정렬
        // 4. 결과를 리스트로 수집
        List<String> result = fruits.stream()               // 스트림 생성
                                    .filter(s -> s.startsWith("a")) // 'a'로 시작하는 문자열 필터링
                                    .map(String::toUpperCase)       // 대문자로 변환
                                    .sorted()                      // 정렬
                                    .collect(Collectors.toList());  // 리스트로 수집

        // 결과 출력
        System.out.println(result);  // [APPLE, AVOCADO]

        // 숫자 리스트 초기화
        List<Integer> numbers = Arrays.asList(1, 2, 3, 3, 4, 5, 6);

        // 1. 중복된 값 제거 (distinct)
        // 2. 3보다 큰 숫자만 필터링
        // 3. 처음 2개를 제외하고 나머지를 출력
        numbers.stream()
               .distinct()               // 중복 제거
               .filter(n -> n > 3)       // 3보다 큰 숫자 필터링
               .skip(2)                  // 처음 2개를 건너뛰기
               .forEach(System.out::println);  // 결과 출력: 6

        // 숫자 합계 계산
        int sum = numbers.stream()
                         .reduce(0, Integer::sum);  // 스트림 요소의 합 계산
        System.out.println("Sum: " + sum);  // 합계 출력: Sum: 24
    }
}

 

 

 

java.util에 있는 Arrays에는 asList라는 정적 메서드가 존재

해당 메서드는 배열을 리스트의 형태로 바꿔준다고한다.

이때 바꿔주는 리스트가 무슨 리스트인지 궁금했다.

List는 인터페이스니까 구현한 ArrayList, LinkedList 등등이 있을 텐데..

 

Arrays 의 ArrayList가 존재한다.(Arrays의 static inner class) 이건 java.util.ArrayList와는 다른 점이 존재한다.

 

Arrays의 asList

ArrayList를 반환해준다.

 

근데 여기서 사용하는 배열은 final로 수정불가하다. add/remove등 불가 가능하게 하려면 이걸 인자로 새로운 ArrayList를 만들면 된다.

생성 시 입력받은 받은 원본 배열의 참조로 생성하기에 원본배열이 바뀌면 만들어진 List도 값이 변경된다.

배열을 asList로 바꾸는 이유는 Collection의 다양한 메서드들을 사용할 수 있기 때문이다.

Stream 사용 가능!

 

 

String

public final class String
	extends Object
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc

 

Interface

  • Serializable
    • 직렬화 및 역직렬화가 가능한지를 알려주는 마커역할을 하는 인터페이스다.
    • 직렬화는 객체를 저장하거나 전송할 때 객체의 상태를 바이트 스트림으로 변환하여 전송하는 걸 말한다.
    • 역직렬화는 이렇게 변환된 바이트 스트림을 다시 원래 객체로 변환하는 걸 말한다.
    • 객체 그대로 전송하면 복잡한 상태를 보존하기 어렵기 때문에 관리하기 쉬운 바이트 스트림으로 전송 저장한다.
  • Comparable<String>
    • 숫자는 비교할 기준이 존재한다.(크기) 하지만 사용자가 정의한 클래스라면 어떤 속성들로 비교를 할지 모호하다. 그렇기에 Comparable인터페이스의 메서드인 compareTo메서드를 구현함으로써 자신 뭘 기준으로 비교를 할지 정할 수 있다.
    • String에 경우 str.compareTo(anotherStr) 이렇게 사용하는데 str과 anotherStr을 비교하여 같으면 0, str이 사전순으로 앞에 있다면 음수값, str이 뒤에 있다면 양수값을 출력해 준다.
  • CharSequence
    • 이 인터페이스는 문자열을 다루는 클래스에서 공통으로 사용될 메서드를 포함한다. 아래의 메서드를 사용할 수 있다.
      • char charAt(jint index): 해당 인덱스의 해당하는 문자 반환
      • int length(): 문자열 길이 반환
      • CharSequence subSequence(int start, int end): 시작인덱스부터 끝인덱스까지의 부분문자열 반환
        • String에서 substring으로 부분문자열을 리턴해준다.
      • String toString(): 문자열 출력을 위해 문자열 객체 반환
  • Constable & ConstantDesc
    • 이 둘은 상수풀 관련한 것 같은데 아직 잘 모르겠다.

String의 객체는 불변이다. 

    @Stable
    private final byte[] value;
    
    //인코딩 방식을 의미 LATIN1 & UTF16 이거 중 하나가 들어감
    private final byte coder;

final로 되어있어 변경 불가이다.

 

하나 특이한 점이 있다면 문자 리터럴로 생성 시 문자열 상수풀에 저장되고 다른 객체가 동일한 문자열을 생성 시 원래 만들어 놓은 걸 가져다 쓴다. 그렇기에 1,2의 주소가 같다.

하지만 new로 새로운 객체를 만들면 동일한지 아닌지 상관없이 새로운 객체를 힙에 생성한다.

String str1 = "hello";
String str2 = "hello";

String str3 = new String("hello");
String str4 = new String("hello");

//str1 == str2
//str3 != str4

 

돌아가서 String은 불변이라 했다. 이로써 얻는 장점도 물론 존재한다.

안정성과 재사용성이 있겠다.

하지만 불변이므로 생기는 문제도 있을 수 있다.

수정을 할 수 없기에 수정하는 것처럼 보이는 것은 전부 새로운 객체를 추가적으로 만들고 그 객체를 가리키게 바꾼 것이다.

잦은 수정은 성능을 떨어트일 수 있다.

아래의 두 클래스는 문자열을 수정할 수 있는 클래스이다.

 

StringBuilder

public final class StringBuffer
    extends AbstractStringBuilder
    implements Serializable, Comparable<StringBuffer>, CharSequence

 

StringBuffer

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, Comparable<StringBuilder>, CharSequence

 

Abstract Class

  • AbstractStringBuilder
abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage.
     */
     //실질적 데이터 !가변!
    byte[] value;

    /**
     * The id of the encoding used to encode the bytes in {@code value}.
     */
    byte coder;

    /**
     * The count is the number of characters used.
     */
    int count;

    private static final byte[] EMPTYVALUE = new byte[0];

    /**
     * This no-arg constructor is necessary for serialization of subclasses.
     */
    AbstractStringBuilder() {
        value = EMPTYVALUE;
    }

    /**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    AbstractStringBuilder(int capacity) {
        if (COMPACT_STRINGS) {
            value = new byte[capacity];
            coder = LATIN1;
           
        } else {
            value = StringUTF16.newBytesFor(capacity);
            coder = UTF16;
        }
    }
        AbstractStringBuilder(String str) {
        int length = str.length();
        int capacity = (length < Integer.MAX_VALUE - 16)
                ? length + 16 : Integer.MAX_VALUE;
        final byte initCoder = str.coder();
        coder = initCoder;
        value = (initCoder == LATIN1)
                ? new byte[capacity] : StringUTF16.newBytesFor(capacity);
        append(str);
    }

 

Interface

  • Serializable
  • Comparable<StringBuffer>
  • CharSequence

이 둘은 동시성 제어를 지원하냐 안하냐의 차이만 존재한다.

Buffer에 경우 Sychronized 키워드를 통해 동시성을 제어한다.(성능은 낮아질 수 있기에 멀티스레드 환경이 아닌 코테에서는 사용X)

 

같은 추상클래스를 확장하고 있는데 이곳에는 append, remove 등과 같은 구현해야할 메서드들을 적어놨다.

둘 다 실제 데이터인 바이트 배을은 이 추상 클래스에 두고 사용한다.

AbstractStringBuilder append(AbstractStringBuilder asb) {
    if (asb == null) {
        return appendNull();
    }
    int len = asb.length();
    ensureCapacityInternal(count + len);
    if (getCoder() != asb.getCoder()) {
        inflate();
    }
    asb.getBytes(value, count, coder);
    count += len;
    return this;
}

append의 경우 이미 구현해 놓고 아래와 같이 오버라이딩해서 사용한다.

public StringBuilder append(StringBuffer sb) {
    super.append(sb);
    return this;
}

    @Override
    public StringBuilder reverse() {
        super.reverse();
        return this;
    }

 

'공부일지 > Java' 카테고리의 다른 글

JAVA, 스트림(Stream)  (0) 2024.10.23
[JAVA] Arrays.asList()  (0) 2024.07.04
[JAVA] 6/21에 배운 것  (0) 2024.06.26
[JAVA] 6/20 배운거 클래스  (0) 2024.06.20
[JAVA] 인터페이스와 객체지향  (0) 2024.06.19

생성자란 클래스를 초기화 하기 위한 메서드

1. 클래스명과 동일하다

2. 반환타입 없음

3. 지역변수 사용가능

4. 사용자 정의 생성자 사용 시 디폴트 생성자를 선언해줘야한다.

 

클래스의 메서드는 자기 자신을 인자로 받는다(생략되어있다)

class Book{

public getTitle(Book this){

return this.title

}

}

Book 이 new 될 때 인스턴스 메서드가 생성되며 해당 객체의 주소가 this로 넘어간다.

static 메서드의 경우 인스턴스 생성 전에 클래스 레벨에서 저장된다. 따라서 인스턴스와 연관되지 않는다.

 

메모리 영역

메소드영역

1. 클래스 정보

클래스이름 부모 클래스 이름, 구현된 인터페이스 목록 등 클래스 자체에 대한 정보

 

2. 런타임 상수 풀

상수 풀은 컴파일 타임 상수들과 런타임 상수들을 포함합니다. 여기에는 숫자 상수, 문자열 상수, 클래스와 메서드 참조 등이 포함

런타임 상수 풀은 클래스 파일의 Constant pool table을 런타임에 사용하는 구조다.

 

3. 필드 정보

각 필드의 이름,타입,접근 제어자 등의 정보

 

4. 메서드와 과련된 데이터

각 메서드의 이름, 리턴 타입, 매개변수 타입, 접근 제어자, 메서드의 바이트코드 등

메서드의 실행  코드를 담고 있는 바이트코드

 

5. 메서드와 관련된 데이터

메서드의 리턴 타입, 매개변수 타입, 로컬 변수 테이블, 예외 처리 테이블 등

 

6. 정적 변수

클래스에 속하는 정적 변수들, 정적 변수는 클래스가 메모리로 로드될 떄 Method 영역에 저장되고, 클래스 로더에 의해 초기화

정적 변수는 클래스 로드 시 한 번 로드되고 객체들이 공유한다. 

 

7. 메서드와 필드의 심볼릭 참조

메서드와 필드외 이름 및 서명 정보. 이 정보는 런타임에 실제 메모리 주소로 변환됩니다.

 

8. 클래스 초기화 코드

클래스 초기화 블록이나 정적 초기회 블록에 포함된 코드. 클래스가 처음 로드될 때 실행됩니다.

 

 

역할

클래스 로딩

클래스 연결

클래스 초기화 

힙영역

 

메서드 영역은 JVM(Java Virtual Machine) 메모리 구조의 한 부분으로, 클래스에 대한 메타 메이터를 저장하는 곳

클래스 구조

메서드와 생정자의 바이트코드

런타임 상수 풀

정적 변수

 

 

스택영역

구조

스택 프래임 메서드 호출마다 생성되는 메모리 블록

 

흐름

메서드 호출

메서드 실행

메서드 종료

 

 

'공부일지 > Java' 카테고리의 다른 글

[JAVA] Arrays.asList()  (0) 2024.07.04
[JAVA] String, StringBuffer, StringBuilder  (0) 2024.07.01
[JAVA] 6/20 배운거 클래스  (0) 2024.06.20
[JAVA] 인터페이스와 객체지향  (0) 2024.06.19
[JAVA] 추상클래스와 인터페이스  (0) 2024.06.19

객체지향의 추상화를 구현시켜 주는 개념

미술에서 추상화란 점, 선, 면으로 대상을 표현하는 것이라고 한다.

클래스는 객체지향의 기본단위인 객체를 생성하기 위한 설계도이다.

 

 

객체지향의 추상화도 비슷한 의미 같다. 현실 세계의 사물은 매우 방대한 정보를 가진다.

이 정보를 모두 입력하기란 불가능 할 것이다. 이걸 추상화해 단순하게 표현한 것이라고 이해했다.

단순화한다는 면에서 모델링과도 일맥상통하는 것 같다.

 

클래스의 멤버 변수(중 인스턴스 변수만)는 인스턴스 생성 시 초기화된다.

멤버 변수에는 인스턴스 변수와 클래스 변수가 있다.

인스턴스 변수는 인스턴스 마다 각각 가지고 있다. 인스턴스 생성 시 초기화

클래스 변수는 static으로 클래스가 로딩될 때 메모리에 올라가며 모든 인스턴스가 공유한다. 로딩 시 초기화

하나의 클래스 파일에는 하나의 public 클래스만 가능하다. 파일명과 같은 클래스가

 

인스턴스 변수는 객체 생성과 함께 힙영역에 저장된다.

 

클래스의 클래스 변수(정적변수), 메서드(정적, 인스턴스) 모두 메서드 영역에 저장

정적이든 인스턴스든 메서드는 클래스 파일(.java에서 javac로. class)이 JVM에 처음 참조 될 때 클래스가 메모리에 로드된다.

이때 모든 메서드가 메서드 영역에 저장됨

 

 

int [2][]는 되고 int [][5]는 안 되는 이유 1) 경우 [null, null] 차후에 배열의 주소값을 넣어 줄 수 있다.

C에서는 그냥 연속된 메모리인 1차원 배열을 행과 열로 나눈 거에 불가한데 

java는 2차원 배열은 배열의 배열이라는 것 그렇기에 어떤 배열의 길이도 가능하다. 연속적일 필요가 없다.

 

String을 리터럴로 생성하면 해당 객체는 문자열 상수 풀에 저장 문자열 상수 풀은 힙메모리의 일부이지만 특별히 관리된다.

(런타임 상수 풀은 메소드 영역에 존재 Class Constant pool(컴파일 시간에 생성됨)에서 읽어온 상수 값, 클래스 메타데이터 저장(해당 클래스가 로드 될 때 런터임 상수 풀에 즉 런타임에 올라온다.))

그렇기에 가비지 컬렉션에 의해 회수되지 않는다.

만약 동일하게 리터럴로 문자열 생성 시 문자열 상수 풀에서 동일한 객체가 있는지 확인하고 있다면 해당 주소로 반환하고 없을 경우 상수 풀에 추가한다.

new를 통해 객체 생성 시는 상수풀에서 확인하지 않고 힙 메모리에 생성

참조가 사라질 경우 가비지 컬렉션이 회수해 버림

'공부일지 > Java' 카테고리의 다른 글

[JAVA] String, StringBuffer, StringBuilder  (0) 2024.07.01
[JAVA] 6/21에 배운 것  (0) 2024.06.26
[JAVA] 인터페이스와 객체지향  (0) 2024.06.19
[JAVA] 추상클래스와 인터페이스  (0) 2024.06.19
[JAVA] Wrapper Class  (0) 2024.06.18

자바에는 추상클래스와 인터페이스이 존재한다.

이 둘은 객체지향적인 특성을 더해준다고 한다.

이제 자바를 시작하는 입장에서는 바로 와닿는 개념이 아니라 먼저 정리해보고

더 배워보면 언젠간 무릎을 탁 치는 순간이 오길 바란다.


인터페이스의 효과

느슨한 결합

결합도란 구성 요소 간의 의존성을 의미한다.

어디에 의존 한다는 건 그 요소가 변경되었을 경우 자기도 변경 되어야 한다는 의미이다.

인터페이스는 결합도를 낮출 수 있다.

interface Payment {
    void processPayment();
}

class CreditCardPayment implements Payment {
    @Override
    public void processPayment() {
        System.out.println("Processing credit card payment");
    }
}

class PaypalPayment implements Payment {
    @Override
    public void processPayment() {
        System.out.println("Processing PayPal payment");
    }
}

class PaymentService {
    private Payment payment;

    public PaymentService(Payment payment) {
        this.payment = payment;
    }

    public void makePayment() {
        payment.processPayment();
    }
}

 

여기서 PaymentService는 Payment에 의존한다.

하지만 그 Payment를 구현한 구현체들이 어떻게 구현 됐는지는 알 필요 없이 사용가능하다.

그 구현체가 변경되어도 PaymentService의 코드는 바꿀 필요가 없으니 의존성이 없다는 것


다형성

다형성은 객체지향에서 매우 중요한 개념이라고 한다.

부모 클래스나 인터페이스를 통해 하위 클래스들을 다룰 수 있는 걸 의미한다.

class Main {
    public static void main(String[] args) {
        Payment payment1 = new CreditCardPayment();
        Payment payment2 = new PaypalPayment();

        payment1.processPayment(); // Processing credit card payment
        payment2.processPayment(); // Processing PayPal payment
    }
}

상위 인터페이스인 Payment로 하위클래스 CreditCardPayment와 PaypalPayment를 모두 사용할 수 있다.


의존성 주입

의존성을 외부에서 주입해주는 것

  1. 생성자 주입(Constructor Injection): 의존성을 생성자의 매개변수로 전달받아 주입하는 방식.
  2. 세터 주입(Setter Injection): 의존성을 세터 메서드를 통해 주입하는 방식.
  3. 필드 주입(Field Injection): 의존성을 직접 필드에 주입하는 방식 (주로 프레임워크에서 사용)

이렇게 세 방법이 있다.

 

생성자 주입

interface MessageService {
    void sendMessage(String message);
}

class EmailService implements MessageService {
    @Override
    public void sendMessage(String message) {
        System.out.println("Sending email: " + message);
    }
}

class SMSService implements MessageService {
    @Override
    public void sendMessage(String message) {
        System.out.println("Sending SMS: " + message);
    }
}

class NotificationService {
    private MessageService messageService;

    public NotificationService(MessageService messageService) {
        this.messageService = messageService;
    }

    public void notify(String message) {
        messageService.sendMessage(message);
    }
}

public class Main {
    public static void main(String[] args) {
        MessageService emailService = new EmailService();
        NotificationService notificationService = new NotificationService(emailService);
        notificationService.notify("Hello, world!");  // "Sending email: Hello, world!"

        MessageService smsService = new SMSService();
        notificationService = new NotificationService(smsService);
        notificationService.notify("Hello, world!");  // "Sending SMS: Hello, world!"
    }
}

 

NotificationService는 생성자에 의존할 코드를 주입받는다.

 

세터 주입

    public void setMessageService(MessageService messageService) {
        this.messageService = messageService;
    }
    
    notificationService.setMessageService(emailService);

이렇게 생성자에서 주입하지 않고 세터로 주입 할 수도 있다.

 

필드 주입은 스프링 배울 때 다시 포스팅 해야겠다.


'공부일지 > Java' 카테고리의 다른 글

[JAVA] String, StringBuffer, StringBuilder  (0) 2024.07.01
[JAVA] 6/21에 배운 것  (0) 2024.06.26
[JAVA] 6/20 배운거 클래스  (0) 2024.06.20
[JAVA] 추상클래스와 인터페이스  (0) 2024.06.19
[JAVA] Wrapper Class  (0) 2024.06.18

추상이란?

실체들 간에 공통되는 특성을 추출한 것

새 사자 고양이 -> 동물

사과 딸기 귤 -> 과일

 

추상 클래스(Abstract Class)

하나 이상의 추상 클래스(구현 되지 않은 메서드)를 포함하는 클래스

인스턴스화 될 수 없다, 상속 받은 하위 클래스가 추상 메서드를 구현해야 인스턴스화 가능

추상클래스는 왜 쓰는가?

상속 계층에서 자식 멤버의 이름을 통일할 수 있다.

실체 클래스 작성 시 시간절약 가능하다.

실체 클래스 설계 구격을 만들 수 있다.

 

추상클래스는 어떻게 사용하는가

abstract 키워드 사용 하위는 extends로 상속 받고 추상 메서드 구현 후 사용

추상 메서드 1개 이상 사용 + 실체 메서드 포함 가능 + 정적 메서드 포함 가능

필드(속성) 포함 가능

생성자 소멸자 포함 가능(하위에서 super()로 접근)

하위 클래스는 단 하나의 추상 클래스만 상속가능

 

인터페이스

클래스가 구현해야 하는 메서드의 집합을 정의, 모든 메서드는 추상메서드이며, 구현X

인터페이스를 통해 동일한 규격을 가지게 할 수 있다.

 

인터페이스는 어떻게 사용하는가

interface 키워드를 통해 사용

모든 멤버 변수: public static final -> 모든 필드는 상수로 사용해야한다.

모든 메서드(디폴트, 정적 제외): public abstract -> 추상메서드로

디폴트 메서드 포함가능: 구현 제공 -> 객체 생성 후 사용가능

정적 메서드 포함가능: -> 인터페이스로 직접 호출, 구현한 모든 클래스가 공유하는 메서드

 

클래스는 복수의 인터페이스 상속이 가능하다.

class 자식 extends 추상클 implements 인터페이스1, 인터페이스2 {}

 

 

추상 클래스, 인터페이스 비교

구분 추상 클래스 인터페이스
목적 클래스의 공통적인 동작을 정의하고 상속받아 재사용하기 위해 사용 클래스가 구현해야 하는 동작을 명시적으로 규약(Contract)하기 위해 사용
키워드 abstract interface
메서드 추상 메서드와 구체 메서드 모두 포함 가능 디폴트 메서드와 정적 메서드 외에는 구체 메서드 포함 불가
변수 인스턴스 변수와 정적 변수 포함 가능 상수(public, static, final)만 포함
상속 단일 상속만 가능 (클래스 다중 상속 불가) 다중 상속 가능
구현 서브 클래스에서 extends 키워드 사용 클래스에서 implements 키워드 사용
생성자 생성자를 가질 수 있음 생성자를 가질 수 없음
사용 용도 클래스 간의 공통된 행동이나 상태를 정의하기 위해 사용 클래스가 반드시 구현해야 하는 메서드를 정의하기 위해 사용
접근 제한자 메서드와 변수에 다양한 접근 제한자 사용 가능 인터페이스의 메서드는 기본적으로 public (디폴트 메서드 포함)
다형성 제한된 다형성 지원 (단일 상속) 더 유연한 다형성 지원 (다중 상속)
추가 가능 기능 자바 8부터 디폴트 메서드와 정적 메서드 포함 가능 자바 8부터 디폴트 메서드와 정적 메서드 포함 가능

 

 

이 둘 모두 객체지향의 중요한 부분인 느슨한 결합, 다형성, 의존성 주입을 도와준다.

아직 이런 부분이 와닿진 않아 일단 정리만 해봐야겠다.

다음 포스팅으로..

'공부일지 > Java' 카테고리의 다른 글

[JAVA] String, StringBuffer, StringBuilder  (0) 2024.07.01
[JAVA] 6/21에 배운 것  (0) 2024.06.26
[JAVA] 6/20 배운거 클래스  (0) 2024.06.20
[JAVA] 인터페이스와 객체지향  (0) 2024.06.19
[JAVA] Wrapper Class  (0) 2024.06.18

+ Recent posts