Post

Java Standard - 입출력(IO)

...

Java Standard - 입출력(IO)

스트림(stream)

입출력이란? 컴퓨터 내부 또는 외부의 장치와 프로그램간의 데이터를 주고받는 것

  • 의미: 데이터 전달을 위해 대상을 연결하고 데이터를 전송하는 것, 데이터를 운반하는데 사용되는 연결 통로
    • 이전에 배운 스트림과 이 스트림은 같은 용어지만, 다른 개념
  • 특징
    • 단방향 통신만 가능 → 입력 스트림, 출력 스트림 각각 따로 존재
    • 중간에 건너 뜀 없이 연속적으로 데이터를 주고 받음
    • First In First Out 구조
  • 구조
    • 스트림
      • 바이트기반 스트림
        • FileInputStream/FileOutputStream
        • ByteArrayInputStream/ByteArrayOutputStream
        • PipedInputStream/PipedOutputStream
        • AudioInputStream/AudioOutputStream
        • 바이트기반 보조 스트림
      • 문자기반 스트림
        • 문자기반 보조 스트림

바이트기반 스트림 - InputStream, OutputStream

  • 의미: 바이트 단위로 데이터를 전송, 입출력을 처리할 수 있는 표준화된 방법을 제공
  • 메서드
    • read() / write(): 추상 메서드, 입출력 대상에 따라 방법이 달라지기 때문
    • read(byte[] b)/write(byte[] b): read() / write() 를 이용하기 때문에, 이를 구현하지 않으면 의미가 없음

    read() / write() 는 반드시 구현되어야하는 핵심 메서드

보조스트림

  • 의미: 스트림의 기능을 보완하기 위한 스트림
  • 특징
    • 실제 데이터를 주고받는 스트림이 아님 → 스트림을 생성한 뒤 이를 이용해 보조스트림을 생성해야함
    • 스트림의 기능을 향상시키거나 새로운 기능 추가 가능

    ex) 보조스트림 BufferedInputStream은 기반 스트림인 FileInputStream 에게 버퍼라는 기능만 제공하고, 실제 데이터 입출력은 FileInputStream이 수행함

문자기반 스트림 - Reader, Writer

  • 의미: 문자 데이터를 입출력하는 스트림
    • 기존에는 바이트단위(=입출력 단위가 1byte) 이기 때문에 문자(2byte)를 다루기에 어려움이 있었는데, 이를 보완하기 위함
  • 특징
    • 자식 클래스의 이름만 조금 다를 뿐, 대부분이 같음

바이트기반 스트림

InputStream과 OutputStream

  • 의미: 모든 바이트기반 스트림의 조상
  • 메서드
    • InputStream

      Build source

    • OutputStream

      Build source

      • flush() : 버퍼가 있는 출력스트림의 경우에만 의미가 있음
      • close(): 모든 스트림은 작업후에 닫아야하지만, 메모리를 사용하는 스트림과 표준 입출력 스트림(System.in, System.out 등)은 닫아주지 않아도 됨

ByteArrayInputStream과 ByteArrayOutputStream

  • 의미: 바이트배열에 데이터를 입출력하는데 사용되는 스트림
    • 다른 곳에 입출력하기 전에 데이터를 임시로 바이트배열에 담아 변환등의 작업을 할 때 사용함
  • 특징
    • 사용하는 자원이 메모리밖에 없어 가비지컬렉터에 의해 자동적으로 자원을 반환 → close()로 닫지 않아도 됨
  • 메서드
    • read(byte[] b, int off, int len)/write(byte[] b, int off, int len) : 한번에 옮기는 데이터의 크기를 byte[] b 만큼으로 조정함 → 작업의 효율 증가
    • read()/write() : IOException 발생시킬수 있어서 try-catch문으로 감싸줘야함
    • available(): blocking 없이 읽어올 수 있는 바이트의 수 반환

    blocking 블락킹이란? 데이터를 읽어올 때, 데이터를 기다리기 위해 멈춰 있는 것 ex) 사용자가 데이터를 입력하기 전까지 기다리고 있을때 = 블락킹 상태

FileInputStream과 FileOutputStream

  • 의미: 파일에 입출력을 하기 위한 스트림
  • 메서드
    • 생성자
      • 파일 이름으로 생성: 실제 파일과 연결된 스트림 생성, String(=파일이름)으로 생성하는 것
      • File인스턴스로 생성: 이전과 같음, File인스턴스로 생성
      • 파일 디스크립터로 생성

        파일디스크립터?

    • 메서드
      • read(): 반환값이 int형인 이유는 1byte씩 데이터를 읽지만, 데이터의 범위가 십진수로 0~255범위의 정수 값이고, 값이 없음을 알리는 -1까지 필요했기 때문

바이트기반의 보조스트림

FileInputStream과 FilterOutputStream

  • 의미: 모든 보조스트림의 조상
  • 특징
    • 모든 보조스트림은 자체적으로 입출력을 수행할 수 없음
      • 보조스트림은 기반 스트림을 필요로함 ⇒ 생성자로 표현
      • 기반 스트림의 메서드를 그대로 호출함
    • 생성자는 접근제한자가 protected이기 때문에 인스턴스 생성이 아니라 상속을 통해 오버라이딩 되어야함

BufferedInputStream과 BufferedOutputStream

  • 의미: 입출력 효율을 높이기 위해 버퍼를 사용하는 스트림
  • 특징
    • 적당한 버퍼의 크기는 버퍼의 크기변경한 후 테스트를 통해 알 수 있음
  • 동작 방식
    • 데이터 읽기
      1. 입력소스로 부터 버퍼의 크기만큼 데이터를 읽음
      2. 읽은 데이터는 내부 버퍼에 저장
      3. 프로그램에서는 BufferedInputStream의 내부 버퍼에 저장된 데이터를 읽음
      4. 프로그램에서 버퍼의 모든 내용을 다 읽으면, 그 다음 데이터를 읽기 위해 read()를 호출
      5. 입력소스에서 버퍼의 크기만큼 데이터를 가져오는 1번 과정으로 돌아가 끝까지 다 읽을때까지 반복
  • 데이터 쓰기
    1. 프로그램에서 write()를 이용한 데이터 출력이 BufferedInputStream의 내부 버퍼에 저장
    2. 버퍼가 가득 차면, 버퍼의 모든 내용을 출력소스에 출력
    3. 버퍼를 비움
    4. write()를 통한 데이터 출력 내용이 버퍼에 저장되는 1번 과정으로 돌아가 출력의 끝까지 반복

flush()를 쓰는 이유 버퍼가 가득 찼을때만 출력소스에 출력을 하기 때문에, 마지막 부분이 출력이 되지 않을 수 있음 이를 방지하기 위해서 flush()가 있음

  • 보조스트림의 close()

    flush() 호출 → 기반 스트림의 close() 호출

    때문에 보조스트림 이용시 보조스트림의 close()를 호출 하는 것이 좋음

DataInputStream과 DataOutputStream

  • 의미: 데이터를 읽고 쓸 때 1byte씩 읽고 쓰는 것이 아닌, 8가지 기본 자료형의 단위로 읽고 쓸 수 있음
  • 특징
    • 출력 형식이 각 기본 자료형의 값을 16진수로 표현해 저장 →데이터를 변환하지 않아도됨
    • 각 자료형의 크기가 다르므로, 출력한 데이터를 읽어 올 때 출력했던 순서를 염두해야함
    • 데이터를 읽을때 파일의 끝에 도달하면 EOFException , I/O 에러시 IOException 발생

      try-catch문으로 감싸주는 방식으로 코드를 짜야함

    • 스트림의 참조변수가 null인지 확인 후 close() 호출
  • 메서드
    • DataInputStream

      Build source

    • DataOutputStream

      Build source Build source

SequenceInputStream

  • 의미: 여러개의 입력스트림을 연속적으로 연결해서 하나의 스트림으로부터 데이터를 읽는 것과 같이 처리할 수 있도록 함
    • 큰 파일을여러개의 작은 파일로 나누거나, 하나의 파일로 합치는 작업시 유용
  • 특징
    • 다른 보조스트림과 달리 FileInputStream을 상속 한 것이 아니라, InputStream을 직접 상속함
  • 스트림 연결 방법

    Build source

    • 2개가 넘는 스트림 연결시 Enumeration에 저장해야함
      • Enumeration에 저장된 순서대로 스트림이 이어짐

PrintStream

  • 의미
    • 기반스트림에 다양한 형태로 출력할 수 있는 메서드(print, printf, println 등)를 오버로딩해서 제공
    • 문자기반 스트림의 역할을 수행함
  • 특징
    • 예외를 던지지 않고, 내부에서 처리함 → 매우 자주 사용되기 때문에
  • PrintWriter : PrintStream 보다 향상된 스트림
    • System.out이 PrintStream 이라 둘 다 사용하게 됨
    • 다양한 언어의 문자를 처리하는데 적함
  • 메서드

    Build source

  • printf의 옵션
    • 정수

      Build source

    • 실수

      Build source

    • 문자열

      Build source

    • 특수문자

      Build source

    • 날짜와 시간

      Build source

    • 예시

      1
      2
      
        System.out.printf("지금은 %tH시 %tM분 %tS초 입니다.", d, d, d);
        System.out.printf("지금은 %1$tH시 %1$tM분 %1$tS초 입니다.", d);
      

      숫자$ 를 옵션 앞에 붙임으로써 출력된 매개변수를 지정할 수 있음

문자기반 스트림

문자데이터를 다루는데 사용된다는 것만 바이트기반 스트림과 다름

Reader와 Writer

  • 의미: 문자기반 스트림의 조상
  • 특징
    • 2byte로 스트림을 처리함
    • 인코딩 처리
      • 유니코드(UTF-16)간의 변환을 자동적으로 처리, 특정 인코딩을 읽어서 유니코드로 변환하거나 반대의 일을 함
  • 메서드
    • Reader

      Build source

    • Writer

      Build source Build source

FileReader와 FileWriter

  • 의미: 파일로부터 텍스트 데이터를 읽고 파일에 쓰는데 사용됨
  • 특징
    • FileInputStream을 이용해 한글 출력시 한글이 깨짐

PipedReader와 PipedWriter

  • 의미: 쓰레드간에 데이터를 주고 받을 때 사용됨
  • 특징
    • 입출력 스트림이 나눠지지 않고, 하나의 스트림
    • 한쪽 쓰레드에서 connect() 호출로 연결이 됨
    • 한쪽 쓰레드에서만 스트림을 닫아도 닫힘
    • 쓰레드 시작 전에 연결해야함
    • 내부적으로 String Buffer를 가지고 있어서 이를 가지고 처리함
  • 동작 방법: 스트림 생성 → 한쪽 쓰레드에서 연결(쓰레드 시작전) → 쓰레드 시작 → 한쪽에서 스트림 종료

StringReader와 STringWriter

  • 의미: 입출력 대상이 메모리인 스트림, 내부의 StringBuffer를 소유하고 있음
  • 메서드
    • StringBuffer getBuffer(): 출력 데이터가 저장된 StringBuffer를 얻음
    • String toString(): 출력된(=StringBuffer에 저장된) 문자열 반환

문자기반의 보조스트림

BufferedReader와 BufferedWriter

  • 의미: 버퍼를 이용해서 입출력의 효율을 높이는 보조스트림
  • 특징
    • 데이터를 라인 단위로 읽을 수 있음

InputStreamReader와 OutputStreamWriter

  • 의미: 바이트기반 스트림을 문자기반 스트림으로 연결시켜주고, 바이트기발 스트림의 데이터를 지정된 인코딩의 문자 데이터로 변환하는 작업을 수행하는 보조스트림
  • 특징
    • 인코딩을 지정하지 않으면 OS의 기본 인코딩으로 데이터를 저장함
  • 메서드
    • InputStreamReader

      Build source

    • OutputStreamReader

      Build source

표준입출력과 File

표준입출력 - System.in, System.out, System.err

Build source

  • 의미: 콘솔을 통한 데이터 입력과 콘솔로의 데이터 출력을 의미
  • 특징
    • 자바에서는 3가지 표준 입출력 스트림을 제공

      System.out - 출력, System.err - 출력, System.in - 입력

    • 표준 입출력 스트림은 애플리케이션 실행과 동시에 사용할 수 있도록 자동으로 생성됨
    • 표준 입출력 스트림의 타입은 InputStremPrintStream이지만, 실제로는 BufferedInputStreamBufferedOutputStream의 인스턴스를 이용함

에디터의 콘솔 출력 콘솔로의 출력을 중간에 가로채서 에디터에 뿌려주는 것

  • 콘솔입력은 버퍼를 가지고 있기 때문에 한번에 버퍼만큼 입력이 가능

표준입출력의 대상변경 - setOut(), setErr(), setIn()

  • 의미: 입출력을 콘솔이 아닌 다른 입출력 대상으로 변경하는 것이 가능
  • 커맨드라인에서의 변경

    1
    2
    
      C:\jdk1.8\work> java StandardIOEx3 > output.txt # 파일에 출력, 내용이 있으면 덮어씀
      C:\jdk1.8\work> java StandardIOEx3 >> output.txt # 파일에 출력, 내용이 있으면 추가
    

RandomAccessFile

  • 의미: 하나의 클래스로 파일에 대한 입력과 출력을 모두 할 수있도록 되어있음
  • 특징
    • DataInput 인터페이스와 DataOutput인터페이스 모두 구현→ DataInputStream과 DataOutputStream과같이 기본자료형 단위로 데이터를 읽고 쓸 수 있음
    • 파일의 어느 위치에서나 읽기/쓰기가 가능함(기존에는 순차적으로 읽기/쓰기 해야함)
      • 내부적으로 파일 포인터 사용(기존에도 사용하지만 포인터의 위치를 옮길수 없었음)
  • 메서드

    Build source

File

  • 의미: 파일과 디렉토리를 다룰 수 있는 클래스
  • 특징
    • OS 독립립적으로 프로그램을 작성하기 위해서는 관련 변수를 이용
    • 유효하지 않더라도 컴파일 에러나 예외를 발생시키지 않음
  • 메서드

    Build source Build source Build source

  • 경로
    • 절대경로: 파일 시스템의 루트로부터 시작하는 파일의 전체경로를 의미
    • 정규경로: 기호나 링크 등을 포함하지 않은 유일한 경로

직렬화(Serialization)

객체를 컴퓨터에 저장했다가 다음에 다시 꺼내 쓰는 방법이나 네트웍을 통해 컴퓨터간에 서로 객체를 주고 받을 수 없을까에 대한 해답을 제공

직렬화란?

Build source

  • 의미
    • 객체를 데이터 스트림으로 만드는 것
    • 객체에 저장된 데이터를 스트림에 쓰기위해 연속적인 데이터로 변환하는 것

      객체를 저장한다는 의미: 객체의 모든 인스턴스변수의 값을 저장한다는 것

      인스턴스변수의 타입이 참조형이면, 참조하고 있는 내용을 모두 저장

객체의 구조 클래스변수나 메서드가 포함되지 않고, 오직 인스턴스 변수들로만 구성되어있음 인스턴스변수는 인스턴스마다 다른 값을 가질 수 있어야하지만, 메서드는 그렇지 않기 때문에 인스턴스마다 메서드의 내용을 포함시킬 이유는 없음

  • 특징
    • 직렬화/역직렬화를 할 수 있는 ObjectInputStreamObjectOutputStream을 사용하는 방법만 알면됨

ObjectInputStream과 ObjectOutputStream

  • 의미
    • 직렬화에는 ObjectInputStream 를 사용
    • 역직렬화에는 ObjectOutputStream 를 사용
    • InputStreamOutputStream을 직접 상속받지만, 기반스트림을 필요로하는 보조스트림
  • 동작 방식
    • 직렬화: 기반스트림 생성 → 보조스트림생성 → 객체 출력
    • 역직렬화: 기반스트림 생성 → 보조스트림생성 → 읽기 → 형변환

      Object타입으로 읽히기 때문에, 객체의 원래 타입으로 형변환 해야함

  • 특징
    • 기본적으로 readObject()writeObject()를 사용해서 직렬화와 역직렬화를 함

      편하지만, 느리기 때문에 속도를 원한다면 오버라이딩 해야함

  • 메서드

    Build source

직렬화가 가능한 클래스 만들기 - Serializable, transient

Serializable

  • 직렬화가 가능한 클래스를 만드는 방법 : java.io.Serializable를 구현하면됨
    • java.io.Serializable: 아무런 내용이 없는 빈 인터페이스, 직렬화를 고려한 클래스인지 판단하는 기준이 됨
  • 특징
    • 조상클래스가 java.io.Serializable 를 구현했다면, 자식 클래스도 자동으로 직렬화가 됨

      → 때문에 Object클래스는 구현하고 있지 않음

    • 조상클래스는 구현하지 않고, 자식클래스만 구현했다면 자식클래스에만 있는 인스턴스 변수만 직렬화의 대상이 됨
    • 인스턴스 변수 타입이 아닌, 실제로 연결된 객체의 종류에 의해 직렬화의 여부가 결정됨
    • 객채의 직렬화와 역직렬화는 순서가 일치 해야함

      → 각각의 객체를 직렬화 하는 것보다 직렬화 할 객체들을 ArrayList와 같은 곳에 담아 이것만 직렬화를 하는 방법이 더 나을 수 있음

transient

  • 의미: 직렬화의 대상에서 제외될수 있도록함
  • 특징
    • 실제로 그 값이 직렬화가 되지 않는 것이 아니라, 해당 자료형의 기본형으로 직렬화가 됨

직렬화가 가능한 클래스의 버전관리

  • 의미: 클래스의 내용이 변경된 경우 역직렬화 실패, 예외 발생

    → 직렬화가 가능한 클래스의 버전 관리 가 필요함

  • serialVersionUID

    • 클래스의 버전으 자동생성해 직렬화 내용에 포함
    • 역직렬화시 클래스의 버전을 비교함으로써 직렬화할 때의 클래스 버전과 일치하는지 확인
    • 클래스의 버전을 수동으로 관리할 때 사용함
This post is licensed under CC BY 4.0 by the author.