본문 바로가기
Backend boot camp/Session1

[JAVA] 심화(Effective)

by orioncsy 2022. 9. 14.

0. 열거형(Enum)

개요

학습 목표

  • Enum의 개념 이해
  • Enum의 등장 배경과 장점
  • Enum의 문법 요소

열거형(enum)

  • 열거형이란?
    • 관련 있는 상수들의 집합
    • 상수들을 보다 간편하게 관리
  • Enum이 없던 이전
    • public static final 키워드를 사용하여 표현
      • ex) public static final double PI=d;
    • 중복된 상수 이름으로 충돌 발생 가능
      • ex) public static final double PI=1.1;
    • 인터페이스로 해결 가능하나 타입 안정성 문제 발생
    • interface Month{ int JAN=1; } interface Week { int MON=1; } //중복 상수명은 피할 수 있다 if(Month.JAN == Week.MON){ } // 관련 없는 개념을 비교하는 타입 안정성 문제 발생
    • 타입 안정성을 해결하기 위해 서로 다른 객체로 생성
    class Month{
        public static final Month JAN=new Month();
    }
    class Week{
        public static final Week MON=new Week();
    }
    • 코드가 길어지고 사용자 정의 타입으로 switch문 사용 불가
  • Enum의 사용
enum Month{JAN, ...}
enum Week{MON,...}
  • Enum의 장점
    • 중복 상수명, 타입 안정성 피할 수 있다
    • 코드 가독성과 switch문 사용 가능
  • switch문은 사용자 정의 타입은 조건으로 받지 않는다
  • enum Month {JAN, FEB, MAR, APR} //기존 방식으로 하면 오류 발생 class enumExample{ public static void main(String[] args){ Month month = Month.JAN; switch(month){ case JAN: ... case FEB: ... case MAR: ... case APR: ... } } }

열거형의 사용

  • 열거형 선언
    • enum 열거형 이름 { 상수명, …}
    • 열거형 이름은 관례적으로 대문자로 표현
    • 열거형 안에 열거형 객체들 존재
    • 상수값을 지정하지 않아도 0부터 할당
    enum Month {JAN, FEB, MAR, APR} 
    class enumExample{
        public static void main(String[] args){
            Month month = Month.JAN;
            switch(month){
                case JAN: System.out.println("JAN"); //JAN 출력
                case FEB: System.out.println("FEB");
                case MAR: System.out.println("MAR");
                case APR: System.out.println("APR");
            }
            Month[] monthAll = Month.values(); // 모든 enum 객체 배열로 저장
            for(Month mon : monthAll)
                System.out.println("%s, %d", mon.name(), mon.ordinal()); 
                //모든 객체 문자열, 상수값 출력
            System.out.println(valueof("JAN")); // JAN의 열거 객체 출력
        }
    }
  • 열거형 메서드
    • java.lang.Enum에 정의된 메서드
    • name() : 열거 객체가 가지는 문자열 반환
    • ordinal() : 열거 객체 순번 반환
    • compareTo() : 주어진 매개 값과 비교해 순번 차이 반환
    • valueOf() : 주어진 문자열의 열거 객체 반환
    • values() : 모든 열거 객체 배열로 반환

1. 애너테이션(Annotation)

개요

학습 목표

  • 애너테이션 개념
  • 표준 애너테이션 VS 메타 애너테이션
  • 사용자 정의 애너테이션

애너테이션의 용도와 종류

애너테이션의 용도

  • 정보 전달
    • 주석과 다르게 다른 프로그램에게 정보 제공
  • 역할
    • 컴파일러에게 문법 체크 정보 제공
    • 빌드할 때 코드 자동 생성 정보 제공
    • 런타임에 특정 기능 실행 정보 제공
    • 타깃 이외의 다른 프로그램에서는 유효하지 않음
  • 종류
    • 표준 애너테이션 : 자바에서 기본 제공
      • @Override : 메서드 오버 라이딩
      • @Deprecated : 앞으로 사용하지 않을 대상
      • @SuppressWarnings : 컴파일러가 경고 메시지 표시 X
      • @FunctionalInterface : 함수형 인터페이스
    • 메타 애너테이션 : 애너테이션을 정의하는 데 사용
      • @Target : 적용 대상
      • @Documented : javadoc으로 작성된 문서에 애너테이션 정보 포함
      • @Inherited : 하위 클래스에 상속
      • @Retention : 애너테이션 유지 기간
      • @Repeatable : 애너테이션 반복 적용
    • 사용자 정의 애너테이션 : 사용자가 직접 정의

표준 애너테이션

@Override

  • 역할
    • 상위 클래스의 메서드를 오버 라이딩한다는 것을 컴파일러에게 알림
  • 특징
    • 매서드 앞에만 붙일 수 있음
  • 목적
    • 오버라이딩 실수로 잘못 작성했을 때 알림을 받을 수 있도록..

@Deprecated

  • 역할
    • 새로운 것으로 대체하였으니 기존의 것을 사용하지 않는 것을 권장시키는 역할
  • 특징
    • 필드나 메서드 앞에 붙일 수 있음
    • @Deprecated가 붙은 멤버를 사용할 때 오류 발생

@SuppressWarnings

  • 역할
    • 컴파일 경고 메시지 나타나지 않게 설정
  • @SuppressWarnings(”example”)
    • all : 모든 경고 억제
    • deprecation : Deprecated 메서드를 사용했을 때 나오는 경고 억제
    • fallthrough : switch문에 break 없을 때 나는 경고 억제
    • finally : finally 관련 경고 억제
    • null : null 관련 경고 억제
    • unchecked : 검증되지 않은 연산자 경고 억제
    • unused : 사용하지 않은 코드 관련 경고 억제
    • {}로 여러 개 사용 가능

@FunctionalInterface

  • 역할
    • 함수형 인터페이스 선언이 잘 되었는지 확인
    • 함수형 인터페이스는 단 하나의 추상 메서드만 가지는 인터페이스

메타 애너테이션

@Target

  • 역할
    • 애너테이션 적용 대상 지정
    • ANNOTATION_TYPE, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE(클래스, 인터페이스, 열거형), TYPE_PARAMETER, TYPE_USE(타입을 사용되는 모든 대상)
  • 활용
import static java.lang.annotation.ElementType.*;
//ElementType.TYPE 대신 간단하게 TYPE, FIELD 처럼 작성 가능

@Target({FIELD, TYPE, TYPE_USE})    //FIELD, TYPE 적용
public @interface AnnotationExam { }    // AnnotationExam 사용자 정의

@AnnotationExam // TYPE에 적용
class Main {

        @AnnotationExam // FIELD에 적용
    double num;
}

@Documented

  • 역할
    • javadoc으로 작성한 문서에 애너테이션에 대한 정보 포함
  • 특징
    • @Override, @SuppressWarnings를 제외한 모든 애너테이션이 @Documented 적용
  • 활용
@Documented
@Target(ElementType.TYPE)
public @interface AnnotationExam { ... }

@Inherited

  • 역할
    • 하위 클래스가 애너테이션을 상속받게 하는 역할
    • 상위 클래스에 @Inherited를 붙이면 하위 클래스도 동일하게 애너테이션 적용
  • 활용
@Inherited
@interface Example{} //Inherited가 적용된 사용자 정의 애너테이션

@Example
class A {} //하위 클래스도 적용

class B extends A{ ... }

@Retention

  • 역할
    • 애너테이션 지속 기간 지정
  • RetentionPolicy
    • SOURCE : 소스 파일에 존재
    • CLASS : 클래스 파일에 존재, 기본값, 실행 시 사용불가
    • RUNTIME : 클래스 파일에 존재, 실행 시 사용 가능
  • 활용
  • @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override(){ ... } // method에 적용하고 실행 시 사용 불가한 사용자 정의 애너테이션

@Repeatable

  • 역할
    • 애너테이션을 여러 번 붙이는 것을 허용
  • 활용
  • @Repeatable(colors.class) @interface color{ String value(); } @interface colors{ color[] value(); } @color("red") @color("green") @color("blue") class RGB{ ... }

사용자 정의 애너테이션

  • 선언
    • 인터페이스 정의와 유사
    @interface color{
        String value(); //타입 요소명()
    }
  • java.lang.annotation 인터페이스를 상속받아 다른 클래스나 인터페이스를 상속받을 수 없다

2. 람다(Lambada)

개요

학습 목표

  • 람다식
  • 함수형 인터페이스를 통한 람다
  • 람다식을 메서드 참조 방식으로 변환

람다식의 기본 문법

기존 방식과 람다식의 차이

void call(){
    System.out.println("call!!!");
} // 기존 방식
() -> System.out.println("call!!!") //람다식
  • 변환형과 메서드 이름을 생략 가능 (익명 함수라고도 불림)

메서드 → 람다식

int multi(int a, int b){
    return a*b;
}
(a,b)->a*b
  • 반환형 타입과 이름을 제거하고 return과 세미콜론 생략 가능
  • 실행문 한 줄이면 중괄호 생략 가능
  • 매개변수 타입은 유추가 가능한 경우 생략 가능

함수형 인터페이스

  • 함수형 인터페이스 사용 이유
    • 람다식은 익명 클래스(참조 변수 선언 없이 객체 정의와 생성을 동시에 하는 일회성 클래스)
    • Object 클래스로 생성해서 만들면 그 안의 메서드를 사용 불가
    • 이에 따라 함수형 인터페이스를 사용
  • 함수형 인터페이스 특징
    • 단 하나의 추상 메서드만 선언
    • 매개변수, 리턴 값에 따른 함수형 인터페이스 예제
    public Lambda {
        public static void main(String[] args) {
            LambdaMulti1 multi1;
            multi1=()->{
                String str="multi1";
                System.out.println(str);
            }
            multi1.multi(); // multi1 출력(매개변수, 리턴값 없는 경우)
    
            LambdaMulti2 multi2;
            multi2=(int a, int b)->{
                String str="multi2";
                System.out.printf("%s : %d, %d",str,a, b);
            }
            multi2.multi(2,3); // multi2 : 2, 3 출력(매개변수 있는 경우)
    
            LambdaMulti3 multi3;
            multi3=(int a, int b)->{
                String str="multi3";
                System.out.printf("%s : %d*%d=%d",str,a, b, a*b);
            }
            multi3.multi(2,3); // multi3 : 2*3=6 출력(매개변수, 리턴값 있는 경우)
        }
    }
    
    @FunctionalInterface
    public interface LambdaMulti1{
        public abstract void multi(); //매개변수, 리턴값이 없는 람다식
    }
    @FunctionalInterface
    public interface LambdaMulti2{
        public abstract void multi(int a, int b); //매개변수가 있는 람다식
    }
    @FunctionalInterface
    public interface LambdaMulti3{
        public abstract int multi(int a, int b); //매개변수,리턴값 있는 람다식
    }
  • 자바에서 기본 제공하는 함수형 인터페이스 존재

함수형 인터페이스 종류

  • java.util.function 패키지의 함수형 인터페이스
함수형 인터페이스 메서드 명
Runnable run()
Supplier R get()
Consumer accept(T)
Function<T,R> R apply(T)

메서드 레퍼런스

메서드 레퍼런스

  • 람다식을 더 간단하게 사용
  • 클래스 이름 :: 메서드 이름
import java.util.function.IntBinaryOperator;
(a,b)-> Math.max(a,b); //람다식
IntBinaryOperator op = Math :: max; //메서드 참조

정적 메서드와 인스턴스 메서드

  • 정적 메서드
    • 클래스 이름 :: 메서드 이름
  • 인스턴스 메서드
    • 참조 변수 :: 메서드 이름
  • 활용
import java.util.function.IntBinaryOperator;

public class Example{
    public static int stMethod(int a, int b){
        return a-b;
    }
    public int instMethod(int a, int b){
        return a/b;
    }
}

public class ReferenceExam{
    public static void main(String[] args){
        IntBinaryOperator op;
        op = Example :: stMethod;
        System.out.println(op.applyAsInt(6,2)) // 정적메서드 참조(출력: 4)

        Example exam=new Example();
        op=exam :: instMethod;
        System.out.println(op.applyAsInt(6,2)) // 인스턴스 메서드 참조(출력 : 3)
    }
}

생성자 참조

  • 람다식 생성자 참조 방식
    • (a,b) → { return new 클래스명(a,b);};
    • 객체를 생성하고 리턴하여 람다식으로 생성자 참조 가능
  • 생성자 참조
    • 클래스 :: new
    • 함수형 인터페이스에서 추상 메서드와 동일한 매개변수 타입과 개수를 찾아 생성자 실행
  • 활용
import java.util.function.BiFunction;
import java.util.function.Function;

class User{
    private String id;
    private String pw;

    User(int id){
        this.id=id;
    }
    User(int id, pw){
        this.id=id;
        this.pw=pw;
    }
    public String getId(){
        return id;
    }
    public String setId(String id){
        this.id=id;
    }
    public String getPw(){
        return pw;
    }
    public String setId(String pw){
        this.pw=pw;
    }
}

class Example{
    public static void main(String[] args){
        Function<String, User> f1= User::new;
        User user1=f1.apply("kdjf");

        BiFunction<String, String, User> f2=User::new;
        User user2=f2.apply("kdjf", "1234");
  • Function 함수형 인터페이스에 apply라는 메서드 존재(내용이 없는 메서드)
  • interface Function<Input, Output>{ public abstract Output apply (Input); } interface Function<Input1, Input2, Output>{ public abstract Output apply (Input1, Input2); }

3. 스트림(Stream)

개요

스트림

  • 하나씩 참조하여 람다식으로 처리할 수 있는 반복자

학습 목표

  • 스트림 특성, 목적
  • 컬렉션, 배열의 스트림
  • 스트림 주요 메서드

스트림 특징

선언형으로 데이터 소스 처리

  • 명령형
    • 어떻게 수행할지, 절차를 밟기
  • 선언형
    • 무엇을 수행할지, 코드가 무슨 일을 할지를 알 수 있음
  • 활용
List<Integer> nums = List.of(1,2,3,4,5,6);
int result = nums.stream()
    .filter(num -> num % 2==1)
    .mapToInt(num -> num)
    .sum(); // 홀수들의 합을 stream으로 구현

람다식으로 요소 처리 코드 제공

  • 대부분의 요소 처리
    • 함수형 인터페이스 매개 타입을 가지기 때문에 람다식이나 메서드 참조로 매개 값 전달

내부 반복자 사용으로 병렬 처리 용이

  • 외부 반복자
    • 개발자가 코드로 직접 컬렉션 요소를 반복해서 가져오는 형식
    • index를 사용하는 for문, iterator를 사용하는 while문
  • 내부 반복자
    • 컬렉션 내부 요소를 반복하고 요소당 처리할 코드만 제공하는 형식
  • 내부 반복자 장점
    • 어떻게 반복할 것 인가를 컬렉션에게 맡기고 개발자는 처리할 코드만 집중
    • 멀티코어나 CPU를 최대한 활용하기 위해 병렬 작업 가능(parallel() 메서드로 가능)

중간 연산과 최종 연산 가능

  • 중간 연산
    • 매핑, 필터링, 정렬 수행
  • 최종 연산
    • 반복, 카운팅, 평균, 종합

파이프라인 구성(.)

리덕션(Reduction)

  • 대량의 데이터를 축소하는 과정
  • 리덕션의 결과물을 바로 집계하기 힘든 경우 중간 연산 필요

파이프라인

  • 파이프라인
    • 여러 개의 스트림이 연결되어 있는 구조
    • 최종 연산을 제외하고 중간 연산 스트림
    • 최종 연산 시작되면 중간 스트림에서 연산되고 최종 연산까지 진행
  • Stream 인터페이스
    • 필터링, 매핑, 정렬 등 많은 중간 연산 메서드 존재
    • 중간 연산 스트림을 리턴하고 반복하여 파이프라인 형성

스트림 생성, 중간 연산, 최종 연산

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

스트림 생성

  • Collection 인터페이스에 stream() 정의
  • Collection 인터페이스를 구현한 객체들(List, Set) 등은 모두 스트림 생성 가능
  • Stream 생성 방식
  • List list = Arrays.asList("a","b","c"); Stream stream=list.stream(); // 1. stream()메서드 활용 //배열 원소를 스트림으로 생성 Stream stream2 = Stream.of(new String[]{"a","b","c"}) //2. Stream.of() Stream stream3=Arrays.stream(new String[]{"a"."b","c"},0,3) //3. Arrays.stream() //마지막 인덱스는 미포함
  • Primitive type(원시 자료형)을 위한 stream(IntStream, LongStream, DoubleStream)
  • IntStream intStream = IntStream.range(1, 5); //range를 활용해 첫 번째 인덱스 이상 //마지막 인덱스 미만을 요소로 갖는 스트림 생성
  • 소스에 따른 Stream
    • 컬렉션 : java.util.Collection.Stream(), java.util.Collection.parallelStream( )
    • 배열 : Arrays.stream(T[]), Arrays.stream(int[]), Arrays.stream(long[]), Arrays.stream(double[]), Stream.of(T[]), IntStream.of(int[]) LongStream.of(long[]), DoubleStream.of(double[])
    • int : IntStream.range(int, int), IntStream.rangeClosed(int, int)
    • long : LongStream.range(long, long), LongStream.rangeClosed(long, long)
  • Stream 사용 주의 사항
    • Read only - 데이터를 읽기만 하고 수정 불가
    • 1회용 - 한 번 사용하면 스트림 닫힘

중간 연산

  • 필터링(filter())
    • filter() : 매개 값에 조건을 넣고 참인 것들만 추출
    • distinct() : 중복제거
    List<Integer> list = Arrays.asList("13", "256", "3235", "4246", "5679", "13");
    list.stream()
        .distinct() // 중복 제거
        .filter(n -> n.startsWith("1") // 조건 추출
        .forEach(n->System.out.println(n)); // 13
  • 매핑(map())
    • map() : 기존의 stream 요소들을 대체하는 새로운 stream 생성
    • mapToInt(), mapToLong(), mapToDouble() : 원시 stream으로 변환
    • mapToObject() : 일반 stream 객체로 변환
    • flatMap() : 모든 원소를 단일 원소 스트림으로 반환
    List<String> list = Arrays.asList("Apple", "Banana");
    list.stream()
        .map(s->s.toLowerCase())
        .forEach(s->System.out.println(s)); // apple banana 출력
    
    Stream<String[]> example
        .stream.of(new String[]{"A", "B", "C"}, new String[]{"D", "E"})
        .flatMap(Arrays::stream)
        .forEach(System.out::println); // A, B, C, D, E 출력
  • 정렬(sorted())
    • sorted() 사용해서 기본 오름차순으로 정렬
    • 내림차순은 Comparator.reverseOrder()를 매개변수로 입력
    • Comparable을 구현하지 않은 클래스는 Comparator.comparing()을 활용 매개변수에 어떤 것 비교할지 메서드 참조나 함수형 인터페이스로 구현
List<String> list = Arrays.asList("Apple","Melon","Banana");
list.stream()
    .sorted(Comparator.reverseOrder())
    .forEach(System.out::println) // Melon Banana Apple 출력

Stream<Student> streamExample
    .sorted(Comparator.comparing(Student::getName))
    .forEach(System.out::println); // Student 클래스에 Name을 정렬하여 출력
  • 연산 결과 확인(peek())
    • 요소를 하나씩 돌면서 출력
    • peek() : 중간 연산 메서드(하나의 스트림에 여러 번 가능)
    • forEach() : 최종 연산 메서드(한 번만 호출 가능, 재호출 하려면 새 스트림 생성 필요)
streamExample
    .filter(n-> n%2==0)
    .peek(n->System.out.println(n)) // 중간 연산 확인
    .sum(); // 짝수 합

최종 연산

  • 한 번만 호출 가능
  • 연산 결과 확인(forEach())
    • 마지막에 하나씩 연산
    • 출력 외 리턴 값이 없는 경우에도 사용
  • 매칭(match())
    • 함수형 인터페이스 Predicate를 받아서 해당 조건을 만족하는지 검사하고 boolean으로 출력
    • allMatch() : 모든 요소가 해당 조건을 만족하는지 출력
    • anyMatch() : 하나라도 해당 조건을 만족하는 지 출력
    • noneMatch() : 모든 요소가 해당 조건을 만족하지 않는지 출력
    int[] arr={1,2,3,4,5,6};
    boolean check1=Arrays.stream(arr).allMatch(n-> n%3==0); //3의 배수인지 확인, false
    boolean check2=Arrays.stream(arr).anyMatch(n-> n%3==0); // 하나라도 3의 배수인지 확인, true
    boolean check3=Arrays.stream(arr).noneMatch(n-> n%3==0); // 모두 3의 배수가 아닌지 확인, false
  • 기본 집계(sum(), count(), average(), max(), min())
    • 하나의 값으로 산출
    int[] arr={1,2,3,4,5,6};
    long result1=Arrays.stream(arr).sum(); // 21 배열이기 때문에 mapToInt를 안해도 된다
    // .sum은 반환형이 int, long, double
    long result2=Arrays.stream(arr).count(); // 6 .count는 반환형이 long
    double result3=Arrays.stream(arr).average().getAsDouble(); // 3.5 반환형이 Optional
    int result4=Arrays.stream(arr).max().getAsInt(); // 6 반환형이 Optional
    int result5=Arrays.stream(arr).min().getAsInt(); // 1 반환형이 Optional
    int result6=Arrays.stream(arr).findFirst().getAsInt(); // 1 반환형이 Optional
    //getAsInt 대신에 orElse()를 쓸수 있다
    //orElse()는 Optional 객체 안에 있는 내용을 반환하거나 null이라면 인자를 반환
    //orElse()의 인자는 객체로만 들어가고 그 객체가 null로 받아들이지 않아 null로 들어가면
    //NullPointerException 에러가 난다
  • reduce()
    • 누적하여 하나로 응축하는 방식
    • 앞의 두 요소와 연산하여 다음 요소를 연산
    • reduce(Identity, Accumulator, Combiner)
      • Identity : 계산을 위한 초기값 (0으로 설정하면 요소가 없을 경우 0 반환)
      • Accumulator : 중간 결과를 생성하기 위한 연산
      • Combiner : 다른 스레드에서 실행한 결과 합치는 것(parallelStream에서만 작동)
    • count(), sum()은 집계 내부에 reduce() 포함
    int[] arr={1,2,3,4,5,6};
    int result1=Arrays.stream(arr).reduce(0, (a,b)->a+b)
  • collect()
    • List, Set, Map 등 다른 컬렉션으로 결과를 가져올 때 사용
    • Collectors 객체에서 static 메서드 사용 가능
    • 없는 경우 Collector 인터페이스로 구현 가능
    int[] arr={1,2,3,4,5,6};
    List<Integer> list=Arrays.stream(arr).collect(Collectors.toList());
    Set<Integer> set = Arrays.stream(arr).collect(Collectors.toCollection(HashSet::new);

Optional

  • NullPointerException(NPE) null 값으로 인한 에러를 객체 차원에서 방지
  • 연산 결과를 Optional에 담아서 반환하면 NPE 방지

Optional

  • 모든 타입의 객체를 담을 수 있는 Wrapper class
  • public final class Optional { private final T value; // T타입의 참조 변수 }
  • Optional 객체 생성
    • of() : null 값을 인자로 받는 경우 에러 발생
    • ofNullable() : null 값도 받아들임
    Optional<String> opt1 = Optional.of("Yes");
    Optional<String> opt2 = Optional.of(null); // 오류 발생
    Optional<String> opt3 = Optional.ofNullable(null);
    System.out.println(opt1); // Optional[Yes] 출력
    System.out.println(opt1.get()); // Yes 출력
    
    System.out.println(opt1.isPresent()); //null이 아니면 true, null이면 false 반환
    opt1.ifPresent(s-> System.out.println(s)); //null 값이 아니면 출력
    String nullname=null;
    String example = Optional.ofNullable(nullName).orElse("default");
    //null 유무에 상관 없이 불러서 디폴트로 설정 (아닐경우를 대비해 미리 설정)
    String example = Optional.ofNullable(nullName).orElseGet(()->"default");
    //null일 경우에만 가져옴 (아닐 경우에 가져옴)
    
    opt1.<String>empty(); //기본값으로 초기화
  • Optional 심화
    • map() : Optional.of로 감싸서 반환
    • flatmap() : 그냥 반환
    • import lombok으로 @Getter, @AllArgsConstructor로 자동 getter, constructor 생성 가능
    import lombok.AllArgsConstructor;
    import lombok.Getter;
    
    @Getter
    @AllArgsConstructor
    public class Topic{
        String todayTopic;
    }
    
    @Getter
    @AllArgsConstructor
    public class MathField{
        Optional<Topic> topic;
    }
    
    @Getter
    @AllArgsConstructor
    public class MathTeacher{
        String name;
        String id;
        Optional<MathField> mathField;
    }
    public class MakingSystem{
        public Optional<MathTeacher> makeMathTeacher(String name){
            Optional<Topic> topic = Optinal.of(new Topic("math1"));
            Optional<MathField> mathField = Optional.of(new MathField(topic));
            return Optional.of(new MathTeacher(name, "0139", mathField));
        }
    }
    
    public class OptionalExample{
        public static void main(String[] args){
            MakingSystem makingSystem = new MakingSystem();
            String todayTopic= makingSystem
                                        .makeMathTeacher("John")
                                        .flatMap(MathTeacher::getMathField)
                                        .flatMap(MathField::getTopic)
                                        .map(Topic::getTodayTopic);
                                        .orElseGet("Null");
            System.out.println(todayTopic); // math1 출력
        }
    }

4. 파일 입출력(I/O)

개요 및 학습 목표

  • 바이트 기반 스트림의 입출력 코드
  • 문자 기반 스트림의 입출력 코드
  • 파일 클래스

InputStream, OutputStream

입출력

  • InputStream, OutputStream으로 데이터 단방향 전송
  • 입출력 대상
    • File : FileInputStream / FileOutputStream
    • 프로세스 : PipedInputStream / PipedOutputStream

FileInputStream

  • 터미널에서 echo hello, world >>hello.txt
  • FileInputStream : 파일 내용을 stream에 읽는 스트림
  • BufferedInputStream : 버퍼는 바이트 배열로 많은 바이트를 저장하여 한 번에 많은 양을 처리하여 데이터 입출력을 도와주는 임시 저장 공간으로 성능 향상 기여
import java.io.FileInputStream;
import java.io.BufferedInputStream;
public class FileInputStream{
    public static void main(String[] args){
        try{
            FileInputStream fileInput =new FileInputStream("hi.txt"); // FileInputStream클래스 생성
            BufferedInputStream bufferedInput = new BufferedInputStream(fileInput);
            int i=0;
            while((i=bufferedInput.read())!=-1){ // 스트림에서 읽어들이고 -1이면 나온다
                System.out.print((char)i);
            }
            fileInput.close(); // 스트림 닫기
        }
        catch(Exception e){
            System.out.println(e);
        }
    }
}

FileOutStream

import java.io.FileOutputStream;

public class FileOutputStream{
    public static void main(String[] args){
        try{
            FileOutputStream fileOutput =new FileOutputStream("hi.txt"); // FileOutputStream클래스 생성
            String str="World!";
            for(byte b : str.getBytes())
                fileOutput.write(b);
            fileOutput.close(); // 스트림 닫기
        }
        catch(Exception e){
            System.out.println(e);
        }
    }
}

FileReader, FileWriter

  • FileReader/FileWriter
    • FileInputStream과 FileOutputStream은 1바이트 기준으로 입출력을 진행하는 바이트 기반
    • char 타입은 2byte로 문자 기반 입출력을 위해 FileReader. FileWriter를 사용 가능
    • FileReader는 인코딩을 유니코드(UTF-16)로 변환
    • FileWriter는 유니코드를 인코딩으로 변환

FileReader

import java.io.FileReader;
import java.io.BufferedReader;

public class FileReaderEx{
    public static void main(String[] args){
        try{
            FileReader fileReader =new FileReader("hi.txt"); // FileReader클래스 생성
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            int i=0;
            while((i=bufferedReader.read())!=-1){ // 스트림에서 읽어들이고 -1이면 나온다
                System.out.print((char)i);
            }
            fileReader.close(); // 스트림 닫기
        }
        catch(Exception e){
            System.out.println(e);
        }
    }
}

FileWriter

import java.io.FileWriter;
public class FileWriterEx{
    public static void main(String[] args){
        try{
            FileWriter fileWriter =new FileWriter("hi.txt"); // FileWriter클래스 생성
            String str="Hello, world!";
            fileWriter.write(str);
            fileWriter.close(); // 스트림 닫기
        }
        catch(Exception e){
            System.out.println(e);
        }
    }
}

File 클래스

  • File class
    • 파일과 디렉터리에 접근 가능
  • Fileclass 사용 예제
import java.io.*;

public class Main{
    public static void main(String[] args) throws IOException{
        File file = new File("./src/hi.txt");
        File newFile = new File("./src/", "hello.txt"); // 부모경로와 파일명을 입력
        newFile.createNewFile(); // 새로운 파일 생성

        System.out.println(file.getPath()); // file에 입력된 경로를 문자옇 형태로 반환
        System.out.println(file.getParent()); //file에 입력된 부모 경로명을 문자열 형태로 반환
        System.out.println(file.getCanonicalPath()); // 파일의 canonical경로를 반환
        System.out.println(file.canWrite()); // 파일을 쓸 수 있는 지 여부 반환
    }
}
//출력
//.\\src\\hi.txt
//.\\src
//C:\\Users\\user\\IdeaProjects\\Example\\src\\hi.txt
//true
  • File class 활용
import java.io.*;

public class Main{
    public static void main(String[] args) throws IOException{
        //IOException 발생시 해당 클래스 벗어남
        //try catch문으로 예외 처리 가능
        File dir = new File("./src/"); // 부모 디렉터리를 File 객체로 생성
        File [] files = dir.listFiles(); // listFiles()로 그 안의 모든 내용을 File list로 생성
        String suffix = " World!"; // 이름 뒤에 붙일 문자열

        for(int i=0; i<files.length; i++) {
            String fileName=files[i].getName(); // getName()으로 파일 이름 가져오기
            if (fileName.endsWith("txt")){ //txt로 끝나는 파일 선택
                files[i].renameTo(new File(dir, fileName.substring(0,fileName.length()-4)+suffix+".txt"));
                //renameTo()로 바꿀 파일을 익명 클래스로 입력
            }
        }
    }
}

'Backend boot camp > Session1' 카테고리의 다른 글

Session1 회고  (1) 2022.09.19
[JAVA] 심화 - Thread, JVM  (1) 2022.09.19
[JAVA] 컬렉션(Collection)  (0) 2022.09.06
[JAVA] 객체 지향 프로그래밍 심화  (0) 2022.09.06
[JAVA] 객체 지향 프로그래밍 기초  (0) 2022.09.06