Backend boot camp/Session1
[JAVA] 심화(Effective)
orioncsy
2022. 9. 14. 08:40
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문 사용 불가
- public static final 키워드를 사용하여 표현
- 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()로 바꿀 파일을 익명 클래스로 입력
}
}
}
}