자바 기본 13. I/O

장난·2021년 6월 18일
0

자바 기본

목록 보기
13/15
post-thumbnail

13주차 과제: I/O


📌 목표

자바의 Input과 Ontput에 대해 학습하세요.


📌 학습할 것

  • 스트림 (Stream) / 버퍼 (Buffer) / 채널 (Channel) 기반의 I/O
  • InputStream과 OutputStream
  • Byte와 Character 스트림
  • 표준 스트림 (System.in, System.out, System.err)
  • 파일 읽고 쓰기

📜 시작에 앞서

  • 백기선 님의 라이브 스터디(2020년 11월부터 2021년 3월까지) 커리큘럼을 따라 진행한 학습입니다
  • 뒤늦게 알게 되어 스터디 참여는 못했지만 남아있는 스터디 깃허브 주소유튜브 영상을 참고했습니다

📑 스트림 (Stream) / 버퍼 (Buffer) / 채널 (Channel) 기반의 I/O


스트림

  • 데이터가 들어온 순서로 나가는 단방향 통로(queue와 같은 FIFO 구조)
    • 단방향이기 때문에 입력과 출력을 모두 수행하려면 2개의 스트림 생성
  • 블로킹 방식(동기적)으로 동작
    • 데이터의 흐름 입출력 진행시 해당 쓰레드는 다른 작업을 할 수 없는 상태가 된다

버퍼

  • 임시로 데이터를 담아둘 수 있는 메모리 공간
    • 기존 IO에서는 보조 스트림 BufferedInputStream BufferedOutputStream 사용해 버퍼 제공 가능
    • NIO기본적으로 버퍼 사용
  • 데이터를 모아서 처리하여 OS레벨의 I/O 시스템 콜 수를 줄이기 때문에 입출력 성능 빨라진다
  • NIO에서는 데이터 타입별로 java.nio.Buffer를 상속해 별도의 클래스 제공
  • NIO에서는 관련 메서드를 통해 넌다이렉트 버퍼와 다이렉트 버퍼 선택 가능
    • 다이렉트 버퍼 사용시 JVM이 아닌 운영체제가 관리하는 메모리 공간을 사용하여, 운영체지 native I/O 작업 중 중간 버퍼로 버퍼의 내용을 복사하는 것을 방지하여 입출력 성능이 높다

채널

  • NIO에서 등장한 IO에 대한 새로운 추상화
  • 데이터가 양방향으로 다닐 수 있기 때문에 입력과 출력을 위해 두 통로를 생성할 필요 X
  • 버퍼를 통해서만 읽고 쓰기 수행
  • 블로킹 방식논블로킹 방식 모두 가능

📑 Byte와 Character 스트림


InputStream과 OutputStream

  • 바이트 기반 스트림 최상위 클래스

InputStream

출저: 오니님의짱깬뽀: java 스트림의 개념, 종류/파일 입출력/InputStream/OutputStream/Reader/Writer


주요 메서드

메서드설명
void cloase()스트림을 닫고 사용하던 자원을 반환
abstract int read()다음 1byte를 읽어 온다. 읽어올 데이터가 없다면 -1 반환.
int read(byte[] b)배열 b의 크기만큼 다음 byte를 읽으며 배열을 채운다. 읽은 데이터 수를 반환하며, 읽을 데이터가 없다면 -1 반환
int read(byte[] b, inf off, int len)최대 len개의 다음 byte를 읽고, 배열 b의 off 부터 저장. 읽은 데이터 수 반환하며 읽을 데이터 없다면 -1 반환

OutputStream

출저: 오니님의짱깬뽀: java 스트림의 개념, 종류/파일 입출력/InputStream/OutputStream/Reader/Writer


주요 메서드

메서드설명
void close()입력소스를 닫고 사용하던 자원을 반환
void flush()스트림의 버퍼에 있는 모든 데이터를 출력소스에 쓴다
abstract void write(int b)주어진 값을 출력소스에 쓴다
void write(byte[] b)b에 저장된 모든 데이터를 출력소스에 쓴다
void write(byte[] b, int off, int len)b의 off부터 len개의 데이터를 읽어서 출력소스에 쓴다
  • flush()는 버퍼가 있는 경우에만 사용 의미가 있다

Reader와 Wirter

  • 문자 기반 최상위 입출력 스트림 클래스
  • 자바에서 char형이 2byte기 때문에 2byte로 스트림 처리
  • 문자기반 스트림은 여러 종류의 인코딩과 자바에서 사용하는 유니코드(UTF-16)간의 변환을 자동으로 처리
  • 주요 메서드는 InputStream/OutputStream 거의 동일하므로 생략

출저: 오니님의짱깬뽀: java 스트림의 개념, 종류/파일 입출력/InputStream/OutputStream/Reader/Writer


출저: 오니님의짱깬뽀: java 스트림의 개념, 종류/파일 입출력/InputStream/OutputStream/Reader/Writer


📑 표준 스트림 (System.in, System.out, System.err)

  • 콘솔을 통한 데이터 입출력을 위한 스트림
  • java.lang.System
public final class System {
    ...
    public final static InputStream in = nullInputStream();
    public final static PrintStream out = nullPrintStream();
    public final static PrintStream err = nullPrintStream();
    ...
}
  • 선언부와 달리 in, out의 경우 실제로는 버퍼를 이용하는 BufferedInputStream과 BufferedOutputStream의 인스턴스 사용
  • err은 OS에 의해 버퍼링 되지 않고 즉시 출력

✏️ 보조 스트림


보조 스트림

  • 스트림과 연결되, 여러 편리한 기능을 제공하는 스트림
    • 보조 스트림 만으로는 입출력 불가, 스트림 생성후 이에 연결
    • 스트림에 여러 보조 스트림 연쇄 연결 가능
  • 보조_스트림 변수 = new 보조_스트림(연결_스트림)
//기반 스트림
FileInputStream fis = new FileInputStream("hello.text");

//보조 스트림 생성
BufferedInputStream bis = new BufferedInputStream(fis);

입력출력설명
BufferedInputStreamBufferedOutputStream버퍼를 이용한 입출력 성능 개선
DataInputStreamDataOutputStream프리미티브 타입 단위로 데이터 제어
FilterInputStreamFilterOutputStream필터를 이용한 입출력 처리
ObjectInputStreamObjectOutputStream데이터 객체 단위로 처리, 직렬화와 역직렬화 관련
PrintStreamprint() 관련 버퍼 이용한 추가적인 기능 제공
print(), println(), printf()
PushbackInputStream버퍼를 이용해 읽어온 데이터 되돌리기 가능
SequenceInputStream두 개의 스트림을 하나로 연결

보조 스트림은 어떻게 스트렘에 동적으로 새로운 책임을 추가할까?

  • 데코레이터 패턴

데코레이터 패턴

  • 런타임 동안 객체에 동적으로 새로운 책임을 추가할 수 있게 해준다(탈부착 가능한 책임)
    • 상속은 정적이고 전체 클래스에 적용되기 때문에 실현 불가

출저: w3sdesign


데코레이터 패턴 적용전

  • 상속을 통해 서브클래스를 계속 만드는 것이 비효율적이다
  • 더 많은 옵션을 추가할 수록 조합에 따른 서브클래스 수의 증가량이 늘어난다

데코레이터 패턴 적용후

코드

public interface MyOutput {
    public void write();
}

public class MyHelloOutput implements MyOutput{
    @Override
    public void write() {
        System.out.println("MyHelloOutput: hello");
    }
}

public abstract class MyOutputDecorator implements MyOutput {

    private MyOutput out;

    public MyOutputDecorator(MyOutput out) {
        this.out = out;
    }

    @Override
    public void write() {
        out.write();
    }
}

public class MyBufferdOutput extends MyOutputDecorator{

    public MyBufferdOutput(MyOutput out) {
        super(out);
    }

    @Override
    public void write() {
        System.out.println("MyBufferdOutput: buffer...");
        super.write();
    }
}

public class MyFilterOutput extends MyOutputDecorator{

    public MyFilterOutput(MyOutput out) {
        super(out);
    }

    @Override
    public void write() {
        System.out.println("MyFilterOutput: filter...");
        super.write();
    }
}

public class DecoratorPattern {
    public static void main(String[] args) {

        new MyHelloOutput().write();
        System.out.println("===========================");
        new MyBufferdOutput(new MyHelloOutput()).write();
        System.out.println("===========================");
        new MyFilterOutput(
                new MyBufferdOutput(
                        new MyHelloOutput())
        ).write();
    }
}
/*
MyHelloOutput: hello
===========================
MyBufferdOutput: buffer...
MyHelloOutput: hello
===========================
MyFilterOutput: filter...
MyBufferdOutput: buffer...
MyHelloOutput: hello
*/

관련 읽을 거리


📑 파일 읽고 쓰기

  • 포스트에서 NIO를 다루지 않았으므로 IO에서 파일을 읽고 쓰는 방법을 다룬다
  • 다루는 파일의 종류에 따라 바이트 기반, 문자 기반의 입출력 적절히 선택
    • 바이트 기반의 FileInputStream/FileOutputStream
    • 문자 기반의 FileReader/FileWriter

  • 파일의 공백을 제거해 *.min.* 처럼 해주는 예제
    • 문자 기반 파일을 다룰 것이기 때문에 FileReader/FileWriter 사용
    • 버퍼 이용한 입출력 성능 향상 위해 보조 스트림 BufferedReader/BufferedWriter 사용
package week13;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class MinMaker {

    public static void main(String[] args) {

        try (BufferedReader bfr = new BufferedReader(new FileReader(args[0]));
            BufferedWriter bfw = new BufferedWriter(new FileWriter(args[1]))){
            
            int data = 0;
            while((data = bfr.read()) != -1){
                if(data != '\t' && data != '\n' && data != ' ' && data != '\r'){
                    bfw.write(data);
                }
            }
        
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

D:\javaBox> java week13.MinMaker week13/MinMaker.java week13/MinMaker.min.java

MinMaker.min.java 파일 생성

packageweek13;importjava.io.BufferedReader;importjava.io.BufferedWriter;importjava.io.FileReader;importjava.io.FileWriter;importjava.io.IOException;publicclassMinMaker{publicstaticvoidmain(String[]args){try(BufferedReaderbfr=newBufferedReader(newFileReader(args[0]));BufferedWriterbfw=newBufferedWriter(newFileWriter(args[1]))){intdata=0;while((data=bfr.read())!=-1){if(data!='\t'&&data!='\n'&&data!=''&&data!='\r'){bfw.write(data);}}}catch(IOExceptione){e.printStackTrace();}}}


📑📌📜✏️

0개의 댓글