📄 학습내용
애너테이션(Annotation)
- 애너테이션(annotation)
- 다른 프로그램에게 정보 제공
- 테스트를 수행하는 프로그램 외의 다른 프로그램에게는 영향을 주지 않음
- 주석(comment) : 소스 코드를 읽는 사람에게 정보 제공
- 역할
- 컴파일러에게 문법 에러 체크하는 정보 제공
- 빌드 시 코드를 자동으로 생성할 수 있는 정보 제공
- 실행 시 특정 기능을 실행하는 정보 제공
- 종류
- 표준 애너테이션 : 기본으로 제공하는 애너테이션(자바 컴파일러에게 정보 제공하는 역할)
- 메타 애너테이션 : 애너테이션을 정의하는 애너테이션
- 사용자 정의 애너테이션 : 사용자가 직접 정의한 애너테이션
표준 애너테이션 | 메타 애너테이션 | 사용자 정의 애너테이션 |
@Override | @Target | |
@Deprecated | @Documented | |
@FunctionalInterface | @Inherited | |
@SuppressWarning | @Retention | |
@Repeatable |
- 표준 애너테이션
- @Override
- 하위 클래스에서 상위 클래스의 메서드를 오버라이딩할 때 컴파일러에게 알림
- 상위 클래스에 동일한 메서드가 없다면 컴파일 에러 발생
- @Deprecated
- 새로운 것으로 대체되어서 @Deprecated가 붙은 것은 사용하지 않을 것을 권장
- @Functionallnterface
- 함수형 인터페이스 선언이 바르게 되었는지 확인
- 잘못 선언한 경우 에러 발생
- @SuppressWarnings
- 컴파일 경고 메시지 무시
- 둘 이상의 경고 무시 가능 : @SuppressWarning({"null", "deprescation"})
@SuppressWarnings("all") | 모든 경고 무시 | ||
@SuppressWarnings("null") | null 관련 경고 무시 | ||
@SuppressWarnings("deprecation") | deprecation 관련 경고 억제 |
- 메타 애너테이션
- @Target
- 애터테이션의 대상 타입 지정
- java.lang.annotation.ElementType 에 대상 타입의 열거형이 정의되어 있음
- 사용 : @Target({ElementType.TYPE, ElementType.METHOD})
@Target(ElementType.METHOD)
대상 타입 | 범위 |
ANNOTATION_TYPE | 애너테이션 |
CONSTRUCTOR | 생성자 |
FIELD | 필드 |
LOCAL_VARIABLE | 지역변수 |
METHOD | 메서드 |
PACKAGE | 패키지 |
PARAMETER | 매개변수 |
TYPE | 타입(클래스, 인터페이스, 열거형) |
TYPE_PARAMETER | 타입 매개변수 |
TYPE_USE | 타입이 사용되는 모든 대상 |
- @Documented
- javadoc으로 작성된 문서 포함
- @Override와 @SuppressWarnings를 제외한 나머지는 @Documented가 적용됨
- @Inherited
- 하위 클래스가 애너테이션 상속
- 상위 클래스에 애너테이션 선언하면 하위 클래스에 상위 클래스에 적용된 애너테이션 적용
- @Retention
- 애터네팅션 지속시간 설정
- 유지정책(retention policy) : 애너테이션 유지 기간을 정하는 속성
- Retention.SOURCE : 소스 파일 O, 클래스 파일 X
- Retention.CLASS(기본값) : 클래스 파일 O, 실행 시 사용 X
- Retention.RUNTIME : 클래스 파일 O, 실행 시 사용 O
- @Repeatable
- 애너테이션을 여러 번 사용할 수 있도록 허용
- 여러 번 적용 가능해서 하나로 묶어주는 애너테이션 작성
- 사용자 정의 애너테이션
- java.lang.annotation 인터페이스를 상속받아서 다른 클래스나 인터페이스 상속 X
- 정의
@interface 사용자_정의_애너테이션명 {
타입 요소명();
}
람다(Lambda)
- 람다식(Lambda)
- 메서드를 하나의 식으로 표현해서 함수형 프로그래밍 기법을 지원하는 문법 요소
- 함수형 인터페이스에 정의된 추상메서드를 구현해서 사용
- 람다식 = 람다 함수 = 객체 = 익명 함수 = 익명 클래스
- 익명 클래스란? 객체의 선언과 생성 동시에 한 번만 사용하는 일회용 클래스
- 함수형 인터페이스(functional interface)
- 단 하나의 추상 메서드를 가지는 인터페이스
- 람다식 : 함수형 인터페이스 = 1 : 1 매칭
- 사용 이유
- 자바의 람다식은 함수형 인터페이스로만 접근 가능
- 기존 자바 문법을 해치지 않고 함수형 프로그래밍 기법을 사용할 수 있도록 하기 위함
- 람다식 표현 방법
- 구현 : 함수형 인터페이스에 정의된 추상메서드를 구현해서 사용
- 방법
- 메서드의 반환타입과 메서드명 제거 + 화살표 추가
- 특정 조건 만족 시 생략 가능한 것
- 메서드 바디에 실행문이 하나만 있으면 중괄호 생략 가능
- 매개변수 타입의 유추가 가능하면 매개변수 타입 생략 가능
- 매개변수가 하나인 경우 괄호 생략 가능
// 메서드
String sum(String str1, String str2) {
return str1 + str2;
}
// 람다식 : 메서드의 반환타입과 메서드명 제거 + 화살표 추가
(String str1, String str2) -> { return str1 + str2; }
// 람다식 : 중괄호 생략
(String str1, String str2) -> return str1 + str2;
// 람다식 : 매개변수 타입 생략
(str1, str2) -> return str1 + str2;
// 람다식 : 괄호 생략
str1 -> System.out.println(str1);
- 함수형 인터페이스를 이용해서 람다식 표현 방법
- 매개변수와 리턴값이 없는 람다식
// 매개변수와 리턴값이 없는 함수형 인터페이스
@FunctionalInterface
public interface Interface1 {
public void method1();
}
// 람다식 구현
Interface1 inter1 = () -> System.out.println("inter1 구현");
// 람다식 사용
inter1.method1();
- 매개변수가 있는 람다식
// 매개변수가 있는 함수형 인터페이스
@FunctionalInterface
public interface Interface2 {
public void method2(int a, int b);
}
// 람다식 구현
Interface2 inter2 = (a, b) -> {
int sum = a + b;
System.out.println(sum);
};
// 람다식 사용
inter2.method2(5, 10);
- 매개변수와 리턴값이 있는 람다식
// 매개변수와 리턴값이 있는 함수형 인터페이스
@FunctionalInterface
public interface Interface3 {
public int method3(int a, int b);
}
// 람다식 구현
Interface3 inter3 = (a, b) -> { return a - b; };
Interface3 inter3_1 = (a, b) -> a - b;
// 람다식 사용
inter3.method3(50, 10);
- 자바에서 기본적으로 제공하는 함수형 메서드
- 함수형 인터페이스 API document : https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html
- 레퍼런스 문서 : https://codechacha.com/ko/java8-functional-interface/
- 메서드 레퍼런스
- 메서드 참조(메서드 레퍼런스) : 메서드를 이용해서 람다식에서 불필요한 매개변수 제거에 활용
- 참조하는 메서드
- 정적 메서드 참조 : 클래스::메서드
- 인스턴스 메서드 참조 : 참조변수::메서드
- 생성자 참조 : 클래스::new
- 객체 생성
// 함수형 인터페이스
@FunctionalInterface
interface Sum {
public abstract String sum_method(int num1, int num2);
}
// 함수형 인터페이스
@FunctionalInterface
interface New {
public abstract Calculator new_method(int a);
}
class Calculator {
// 생성자
public Calculator() {}
public Calculator(int a) {
System.out.println("생성자 호출");
}
// 정적 메서드
public static String static_method(int num1, int num2) {
int sum = num1 + num2;
return "static_method = " + sum;
}
// 인스턴스 메서드
public String instance_method(int num1, int num2) {
int sum = num1 + num2;
return "instance_method = " + sum;
}
}
public class Test {
public static void main(String[] args) {
Sum sum;
// 정적 메서드
sum = Calculator::static_method;
System.out.println(sum.sum_method(10, 20));
// 인스턴스 메서드
Calculator c = new Calculator();
sum = c::instance_method;
System.out.println(sum.sum_method(10, 20));
// 생성자
New new1 = Calculator::new;
Calculator c1 = new1.new_method(10);
}
}
스트림(Stream)
- 스트림(stream) : 배열과 컬렉션의 요소들을 하나씩 참조해서 람다식으로 처리하는 반복자
- 특징
- 다양한 데이터로부터 스트림을 만들어 표준화된 방법으로 데이터를 다룸
- 단방향 데이터를 연속적으로 전송
- 일회용이고, 읽기만 가능
- 선언형으로 데이터 소스 처리
- 선언형 방식 : 코드의 내부 동작 원리를 몰라도 코드가 하는 일을 이해("무엇을")할 수 있는 방식
- 명령형 방식 : 코드의 내부 동작 원리를 이해해야 코드가 하는 일을 이해하는 방식
- 람다식으로 요소 처리 코드 제공
- Stream의 대부분 요소 처리 메서드는 함수형 인터페이스 매개타입 가짐
- 인자값으로 람다식 또는 메서드 참조 가능
- 내부 반복자로 인한 쉬운 병렬 처리
- 외부 반복자(external iterator) : 직접 컬렉션 요소를 반복적으로 가져오는 코드 방식
- 내부 반복자(internal iterator) : 컬렉션 내부에서 요소를 반복시켜서 요소 처리에만 집중할 수 있도록 하는 코드 방식
- 순차적인 외부 반복자보다 효율적
- parallel() 메서드를 이용해서 병렬 스트림 사용 가능
- 중간 연산 & 최종 연산 가능
- 중간 연산 : 매핑, 정렬 등 수행
- 최종 연산 : 총합, 반복, 평균 등 집계 수행
- 파이프라인(Pipeline)
- 리덕션(reduction) : 큰 데이터를 처리해서 규모를 줄이는 것
- 파이프파인을 이용해서 스트림의 중간 연산과 최종 연산 수행
- 구조
- 여러 개의 스트림이 연결된 구조
- (컬렉션 or 배열).초기_스트림.(중간_연산_스트림).최종연산
- 특징
- 최종 연산이 시작 → 중간 연산 스트림에서 순차적으로 중간 연산 실행 후 최종 연산 수행
- 스트림 과정
- 배열 또는 컬렉션 객체 → 스트림 생성 → 중간 연산 → 최종 연산
- 스트림 생성
- Stream 생성 메서드
- stream() : Collection 인터페이스의 객체(List, set 등) → Stream
- Steam.of(배열 객체) : 배열 객체 → Stream
- Arrays.stream(배열 객체) : 배열 객체 → Stream
// Collection 인터페이스의 객체(List, set 등) → Stream
List<Integer> list = Arrays.asList(1, 2, 3); // Arrays.asList() 메서드로 list 생성
Stream<Integer> stream1 = list.stream();
// 배열 객체 → Stream
Stream<String> stream2 = Stream.of("1", "2", "3");
Stream<String> stream3 = Stream.of(new String[]{"1", "2", "3"});
Stream<String> stream4 = Arrays.stream(new String[]{"1", "2", "3"});
- 특수한 종류의 Stream 생성
- 특수한 종류 Stream : IntStream, LongStream, DoubleStream
- 메서드 : of(값들)
- 원시 자료형 → 특수한 종류의 Stream(= 원시 Stream)
- IntStream와 LongStream의 range(), rangeClosed() 함수를 통해서 원하는 범위의 숫자를 연속적으로 할당 가능
IntStream stream1 = IntStream.range(0, 10); //0 ~ 9의 정수를 Stream으로 변경
IntStream stream2 = IntStream.range(0, 10); //0 ~ 10의 정수를 Stream으로 변경
- 메서드 공부하고 느낌점(주관적)
- Arrays.stream() 메서드 : 배열 → Stream, IntStream, DoubleStream, LongStream ...
- 인자값으로 배열 오기
- 인자값에 따라서 Stream 또는 특수한 형태의 Stream이 반환됨
- Stream.of() 메서드 : 값들 또는 배열 → Stream<>
- 인자값으로 값들을 받아오기(Stream)
- 특수한 형태의 Stream은 IntStream 클래스 등을 사용하기
- Arrays.stream() 메서드 : 배열 → Stream, IntStream, DoubleStream, LongStream ...
- 중간 연산
- 필터링
- distinct() :중복된 데이터 제거
- filter(인자 -> 조건) : 조건이 참인 데이터 필터링
- 매핑
- map(인자 -> 요소 변경값) : Stream 요소들의 값을 새로운 Stream 요소들로 변경
- mapToInt(), mapToLong(), mapToDouble() : Stream 객체 → 원시 Stream 객체
- mapToObj(인자 -> 요소 변경값) : 원시 Stream 객체 → Stream 객체
- boxed() : 원시 Stream 객체 → Stream 객체
- flatMap(인자 -> 요소 변경값) : Stream의 요소를 복수 개로 구성된 새로운 Stream 객체로 변경
// map() : Stream<String> 객체 → Stream<Integer> 객체
List<String> strings = Arrays.asList("10", "1234");
List<Integer> list1 =
strings.stream() // Stream<String>
.map(String::length) // Stream<Integer>
.collect(Collectors.toList()); // List<Integer>
System.out.println(list1); // [2, 4]
// mapToObj() : 원시 Stream 객체(특수한 형태의 Stream 객체) → Stream 객체
List<String> list2 =
IntStream.range(0, 10) //IntStream
.mapToObj(i -> ""+i) // Stream<String>
.collect(Collectors.toList()); // List<String>
System.out.println(list2); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
// mapToInt() : Stream 객체 → IntStream 객체
strings.stream() // Stream<String>
.mapToInt(String::length) //IntStream
.forEach(System.out::println); // 2 4
// flatMap() : Stream<String[]> 객체 → Stream<String>
Stream<String[]> stream = Stream.of(
new String[]{"1", "2", "3", "4"},
new String[]{"a", "b", "c"});
stream // Stream<String[]>
.flatMap(Arrays::stream); // Stream<String>
- 정렬
- sorted() : 오름차순 정렬(디폴트)
- sorted(Comparator.reverseOrder()) : 내림차순 정렬
- sorted(Comparator.comparing(기준값)) : 기준값을 오름차순 정렬, stream의 요소가 특수한 형태인 경우
- 연산 결과 확인
- peek(요소 -> 실행문) : 모든 요소를 돌면서 출력
- forEach()와 유사하지만 중간 연산 메서드이므로 여러 번 사용 가능
- 최종 연산
- 스트림 요소를 사용해서 최종 연산은 딱 한 번만 가능
- 반면 중간 연산은 여러 번 가능
- 연산 결과 확인
- forEach(요소 -> 실행문) : 파이프라인 마지막에서 요소를 하나씩 빼면서 출력
- 매칭
- 특정 조건을 검사해서 만족하면 true, 아니면 false를 반환
- allMatch(함수형 인터페이스) : 모든 요소들이 조건 충족 시 true
- anyMatch(함수형 인터페이스) : 최소한 한 요소가 조건 충족 시 true
- noneMatch(함수형 인터페이스) : 모든 요소가 조건 불충족 시 true
- 기본 집계
- sum() : 합계
- count() : 카운팅
- average() : 평균값
- max() : 최댓값
- min() : 최솟값
- findFirst() : 첫번째 요소값
함수 | 인자 타입(입력) → 반환 타입(출력) |
count() | Stream, IntStream, DoubleStream, LongStream → long |
sum() | IntStream → int DoubleStream → double LongStream → long |
average() | IntStream, DoubleStream, LongStream → OptionalDouble |
max() min() findFirst() |
IntStream → OptionalInt DoubleStream → OptionalDouble LongStream → OptionalLong |
- 집계 결과물
- reduce(연산 함수(함수형 인터페이스나 함수 참조 가능)) : 요소들을 하나의 값으로 만드는 방식
- reduce()의 매개변수(최대 3개)
- Accumulator : 연산 함수
- Identity : 초기값(초기값 설정 시 초기값과 요소1부터 연산 시작, 초기값 설정 없으면 요소1과 요소2부터 연산 시작)
- Combiner : 병렬 스트림의 결과를 하나로 합치는 로직
- collect()
- collect()의 인자 : Collectors 인터페이스 이용
- Collectors.toList() : Stream 객체 → List 객체
- Collectors.toSet() : Stream 객체 → Set 객체
- Collectors.toCollection() : List와 Set 이외의 형태로 변경할 때 정의해서 사용
- ex) Collectors.toCollection(LinkedList::new)
Collectors.toCollection()
- ex) Collectors.toCollection(LinkedList::new)
- 즈스트림 → 배열
- toArray()의 인자 : 메서드 레퍼런드 이용(인자 생략 가능)
- ex) .toArray(Integer[]::new);
.toArray();
- Optional 클래스
- Optional 클래스
- NullPointerException(NPE)를 방지하고자 도입된 클래스
- 모든 타입의 객체를 담을 수 있는 래퍼 클래스
public final class Optional<T> {
private final T value;
}
- 선언
Optional<타입_매개변수> 참조변수 = Optinal.of(객체); // null이 아닌 객체만 인자로 받음
Optional<타입_매개변수> 참조변수 = Optinal.ofNullable(객체); // null인 객체도 받음
Optional<타입_매개변수> 참조변수 = Optinal.ofNullable(value); // null인 경우 value 반환
- 메서드
- ofNullalbe(객체 또는 초기값) : Object 객체 생성, 객체가 null인 경우에 초기값으로 객체 생성
- of(객체) : null이 아닌 객체로 Object 객체 생성
- empty() : 기본값으로 초기화
- isPresent() : 참조변수가 null이면 false, null이 아니면 true 반환
- get() : Optional 객체의 타입 매개변수 형태로 저장된 값 가져오기
- orElse(디폴트) : Optional 객체에 저장된 값 가져오기, 만약 Optional 참조변수가 null인 경우 디폴트 반환
Optional<int[]> opt = Optional.of(new int[]{1, 2, 3});
int[] arr = opt.get();
- OptionalInt, OptionalLong, OptionalDouble 형태의 값 가져오기
- getAsInt()
- getAsLong()
- getAsDouble()
파일 입출력(File I/O)
- 바이트 기반 스트림
- 입출력 단위(byte)가 1byte인 입출력 스트림
- FileReader
- 파일 출력하는 바이트 기반 스트림
- 성능이 개선된 보조 스트림 : BufferedInputStream
FileInputStream read = new FileInputStream(파일명); // file open
BufferedInputStream buffer = new BufferedInputStream(파일명);
while(정수변수 = read.read() != -1) { // 문자하나씩 file read
System.out.print((char)정수변수);
}
read.close(); // file close
- FileWriter
- 파일 입력하는 바이트 기반 스트림
FileOutputStream write = new FileOutputStream(파일명); // file open
writer.write(저장할_ 문자열); // file write
write.close(); // file close
- 문자 기반 스트림의 입출력
- 입출력 단위(char)가 2byte인 입출력 스트림
- 인코딩(encoding) : 문자 기반 스트림과 하위클래스가 사용하는 문자 처리 방식
- 유니코드(UTF-16) : 자바에서 사용하는 문자 처리 방식
- FileReader
- 파일 출력하는 문자 기반 스트림
- 인코딩 → 유니코드 변환
- 성능이 개선된 보조 스트림 : BufferedReader
FileReader read = new FileWriter(파일명); // file open
BufferedReader buffer = new BufferedReader(파일명);
while(정수변수 = read.read() != -1) { // 문자하나씩 file read
System.out.print((char)정수변수);
}
read.close(); // file close
- FileWriter
- 파일 입력하는 문자 기반 스트림
- 유니코드 → 인코딩 변환
FileWirte write = new FileWriter(파일명); // file open
writer.write(저장할_ 문자열); // file write
write.close(); // file close
- 파일 클래스(File Class)
- 파일과 디렉터리 접근하해서 파일을 읽고 쓰는 클래스
- 파일 인스턴스 생성
- File file = new File("디렉터리 위치", "파일명");
- File direct = new File("디렉터리 위치");
- 함수
- createNewFile() : 해당 디렉터리에 같은 파일명이 없다면 새로운 파일 생성
- remateTo(file 인스턴스) : 파일명 변경
- getName() : 파일명 출력
🧶 발생한 문제 및 해결방법
- 문제점) ubuntu에서 자바 파일을 컴파일하려고 javac 명령어를 사용했지만 설치가 되어있지 않음
- 해결방법) sudo apt install default-jdk
ubuntu에서 자바파일 컴파일 및 실행 방법
$ javac 파일명.java // -> 컴파일
$ java 파일명.class // -> 실행
- 문제점) FileInputStream에서 파일을 읽어오지 못함
Error : java.io.FileNotFoundException: codestates
- 해결방법 1 ) 파일명을 절대 경로로 지정해주기
String filename = "C:\\Users\\sinar\\IdeaProjects\\test00\\out\\production\\test00\\codestates";
FileInputStream fileInput = new FileInputStream(filename);
- 해결방법 2 ) 자바 클래스 파일 properties에 설정된 default location 확인 후 그 위치에 파일 설정 또는 상대 경로 지정하기
String filename = ".\\src\\package00\\codestates"
// String filename = "./src/package00/codestates"
FileInputStream fileInput = new FileInputStream(filename);
⭐ 공부 난이도
애너테이션, 람다, 스트림 ☆☆★★★
파일 입출력 ☆☆☆☆★
🌕 느낀점
뭔가 휘몰아치면서 하는 느낌이었다. 메서드 참조와 스트림을 합쳐서 사용하니까 정말 정신을 못차리겠다. 어떻게 동작하는지 잘 모르는 것을 보면 메서드 참조를 잘못 이해한 것 같다. 스트림도 연습이 많이 필요해보인다. 내일 연습문제 풀 때 많은 연습이 필요할 것으로 보인다.
'코드스테이츠 - 3회차 백엔드 부트캠프 > Section 1' 카테고리의 다른 글
2022.09.19 월 - 기술면접 (0) | 2022.09.19 |
---|---|
2022.09.16 금 - 스레드, 자바 가상 머신 (1) | 2022.09.16 |
2022.09.14 수 - 컬렉션 문제 풀기 (0) | 2022.09.14 |
2022.09.13 화 - 열거형, 제네릭, 예외처리, 컬렉션 (0) | 2022.09.13 |
2022.09.08 목 - 프로그램 작성 (0) | 2022.09.08 |