입출력의 기본 개념


I/O Package

  • 프로그램에서 데이터를 외부에서 읽고 다시 외부로 출력하는 작업이 번번히 일어남
  • 자바에서 데이터는 스트림 (데이터 흐름, 연결 통로)을 통해 입출력
  • 데이터의 흐름 또는 연결 통로
  • 스트림은 단일 방향 (출발지 → 도착지)으로 연속적으로 흘러감

입력 스트림과 출력 스트림

  • 프로그램이 출발지냐 또는 도착지냐에 따라 스트림 종류가 결정됨

    • 항상 프로그램을 기준으로 데이터가 들어오면 입력 스트림이고 데이터가 나가면 출력 스트림
  • 스트림은 단방향이므로 하나의 스트림으로 입력과 출력을 모두 할 수 없음

  • 입력 스트림

    • 프로그램이 데이터를 입력받을 때
    • 키보드, 파일, 네트워크상의 프로그램에서 입력될 수 있음
  • 출력 스트림

    • 프로그램이 데이터를 보낼 때
    • 모니터, 파일, 네트워크상에서 출력되어 전송될 수 있음

java.io Package

  • 자바의 기본적인 데이터 입출력 API는 java.io 패키지에서 제공

  • java.io패키지에는 파일 시스템의 정보를 얻기 위한 File 클래스와 데이터를 입출력하기 위한 다양한 입출력 스트림 클래스를 제공

  • 스트림 클래스는 크게 바이트 기반 스트림문자 기반 스트림으로 구분

    • 바이트 기반 스트림: 모든 종류의 데이터 (그림, 멀티미디어, 문자 등) 받고 보낼 수 있음
    • 문자 기반 스트림: 오로지 문자만 받고 보낼 수 있음

InputStream 클래스


InputStream 클래스 상속 관계도

  • 바이트 기반 입력 스트림의 최상위 클래스 (추상 클래스)
  • 모든 바이트 기반 입력 스트림은 이 클래스를 상속받아서 만들어짐
  • 주요 메소드

OutputStream 클래스

OutputStream 클래스 상속 관계도

  • 바이트 기반 출력 스트림의 최상위 클래스 (추상 클래스)
  • 모든 바이트 기반 출력 스트림 클래스는 이 클래스를 상속받아서 만들어짐
  • 주요 메소드

Reader 클래스


Reader 클래스 상속 관계도

  • 문자 기반 입력 스트림의 최상위 클래스 (추상 클래스)
  • 모든 문자 기반 입력 스트림은 이 클래스를 상속받아서 만들어짐
  • 주요 메소드

Writer 클래스


Writer 클래스 상속 관계도

  • 문자 기반 출력 스트림의 최상위 클래스 (추상 클래스)
  • 모든 문자 기반 출력 스트림 클래스는 이 클래스를 상속받아서 만들어짐
  • 주요 메소드

콘솔 입출력


콘솔

  • 시스템을 사용하기 위해 키보드로 입력을 받고 화면으로 출력하는 소프트웨어
  • 유닉스나 리눅스 운영체제는 터미널 (terminal), 윈도우 운영체제는 명령 프롬프트 (cmd)에 해당한다.
  • 자바는
    • 콘솔로부터 데이터를 입력받을 때 → System.in
    • 콘솔에 데이터를 출력할 때 → System.out
    • 에러를 출력할 때 → System.err

System.in 필드

  • 콘솔로부터 데이터를 입력받을 수 있도록 System 클래스의 in 정적 필드를 제공
  • System.in은 InputStream 타입의 필드

System.out 필드

  • 콘솔로 데이터를 출력하기 위해서는 System 클래스의 out 정적 필드를 사용
  • `System.out은 PrintStream 타입의 필드
  • PrintStream은 OutputStream의 하위 클래스이므로 out 필드를 OutputStream 타입으로 변환해서 사용할 수 있음

Scanner 클래스

  • Console 클래스는 콘솔로 부터 문자열은 읽을 수 있음, 하지만 기본 타입 (정수, 실수) 값을 바로 읽을 수는 없음
  • java.io 패키지의 클래스는 아니지만, java.util 패키지의 Scanner 클래스를 이용하면 콘솔로부터 기본 타입 값 읽을 수 있음
  • Scanner는 콘솔에만 사용되는 것이 아니며, 생성자 매개값에는 File, InputStream, Path 등과 같이 다양한 입력 소스를 지정할 수 있다.

파일 입출력


File 클래스

  • IO 패키지 (java.io)에서 제공하는 File 클래스는 다음과 같은 기능을 제공
    • 파일 크기, 파일 속성, 파일 이름 등의 정보를 얻어내는 기능
    • 파일 생성 및 삭제 기능
    • 디렉토리를 생성하고 디렉토리에 존재하는 파일 리스트를 얻어내는 기능
  • 파일의 데이터를 읽고 쓰는 기능은 지원하지 않음 → 파일의 입출력은 스트림 이용
File file = new File(path + "file.txt");
  • 주요 메소드
    • exists()
      • File 객체를 생성했다고 해서 파일이나 디렉토리가 생성되는 것은 아님
      • 생성자 매개값으로 주어진 경로가 유효하지 않더라도 컴파일 에러나 예외가 발생하지 않음
      • File 객체를 통해 해당 경로에 실제로 파일이나 디렉토리가 있는지 확인하려면 exists() 메소드를 호출할 수 있음
      • 디렉토리 또는 파일이 파일 시스템에 존재한다면 true를 리턴하고 존재하지 않는다면 false 리턴
      • exists() 메소드의 리턴값이 false라면 createNewFile(), mkdir(), mkdirs() 메소드로 파일 또는 디렉토리를 생성할 수 가능
    • 다른 메소드

FileInputStream

  • 파일로부터 바이트 단위로 읽어들일 때 사용하는 바이트 기반 입력 스트림
  • 바이트 단위로 읽기 때문에 그림, 오디오, 비디오, 텍스트 파일 등 모든 종류의 파일을 읽을 수 있음
// 문자열로 된 파일의 경로를 가지고 FileInputStream을 생성하는 경우
FileInputStream fis = new FileInputStream(path + "dog.jpg");

// 저장할 파일이 File객체로 생성되어 있는 경우 더 쉽게 생성 가능
File file = new File(path + "dog.jpg");
FileInputStream fis = new FileInputStream(file);
  • 주의사항
    • FileInputStream 객체가 생성될 때 파일과 직접 연결이 되는데, 만약 파일이 존재하지 않으면 FileNotFoundException을 발생시키므로 try-catch문으로 예외 처리를 해야 함

FileOutputStream

  • 바이트 단위로 데이터를 파일에 저장할 때 사용하는 바이트 기반 출력 스트림
  • 바이트 단위로 저장하기 때문에 모든 종류의 데이터를 파일로 저장할 수 있음
// 문자열로 된 파일의 경로를 가지고 FileInputStream을 생성하는 경우
FileOutputStream fos = new FileOutputStream(path + "dog.jpg", true);

// 저장할 파일이 File객체로 생성되어 있는 경우 더 쉽게 생성 가능
File file = new File(path + "dog.jpg");
FileOutputStream fos = new FileOutputStream(file, true);
  • 주의사항
    • 주의할 점은 파일이 이미 존재할 경우 데이터를 출력하면 파일을 덮어쓰게 됨
    • 기존의 파일 내용은 사라질 수 있음
    • 기존의 파일 내용 끝에 데이터를 추가할 경우에는 FileOutputStream 생성자 두 번째 매개값을 true

보조 스트림


보조스트림의 개념

  • 다른 스트림과 연결되어 여러 가지 편리한 기능을 제공해주는 스트림
  • 보조 스트림은 자체적으로 입출력을 수행 불가
  • 입력 소스와 바로 연결되 는 InputStreamFileInputStreamReaderFileReader 연결
  • 출력 소스와 바로 연결되는 OutputStreamFileOutputStreamWriterFileWriter 연결
  • 보조 스트림은 문자 변환, 입출력 성능 향상, 기본 데이터 타입 입출력, 객체 입출력 등의 기능을 제공함

보조 스트림 생성

  • 보조 스트림을 생성할 때에는 자신이 연결된 스트림을 다음과 같이 생성자의 매개값으로 받음
보조스트림 변수 = new 보조스트림(연결스트림)
  • 콘솔 입력 스트림을 문자 변환 보조 스트림인 InputStreamReader에 연결하는 코드
InputStream is = System.in;
InputStreamReader reader = new InputStreamReader(is);
  • 스트림 체인
    • 보조 스트림은 또 다른 스트림에도 연결되어 스트림 체인처럼 사용 가능
    • 문자 변환 보조 스트림인 InputStreamReader를 다시 성능 향상 보조 스트림인 BufferedReader에 연결하는 코드
    InputStream is = System.in;
    InputStreamReader reader = new InputStreamReader(is);
    BufferedReader br = new BufferedReader(reader);

문자 변환 보조 스트림

  • 소스 스트림이 바이트 기반 스트림 (InputStream, OutputStream)이면서 입출력 데이터가 문자라면 Reader와 Writer로 변환해서 사용하는 것을 고려해야 함
  • Reader와 Writer는 문자 단위로 입출력하기 때문에
    • 바이트 기반 스트림보다는 편리하고,
    • 문자셋의 종류를 지정할 수 있기 때문에 다양한 문자를 입출력할 수 있기 때문
  • 예시 코드
** 바이트 입력 스트림에 연결되어 문자 입력 스트림인 Reader로 변환시키는 보조 스트림 **

Reader reader = new InputStreamReader(바이트입력스트림);

** 콘솔 입력을 위한 InputStream을 다음과 같이 Reader 타입으로 변환할 수 있음 **

InputStream is = System.in;
Reader reader = new InputStreamReader(is);

** 파일 입력을 위한 FileInputStream도 다음과 같이 Reader 타입으로 변환시킬 수 있음 **

FileInputStream fis = new FileInputStream(path + "input.txt");
Reader reader = new InputStreamReader(fis);

/* `FileInputStream`에 `InputStreamReader`를 연결하지 않고 `FileReader`를 직접 생성가능
   `FileReader`는 `InputStreamReader`의 하위 클래스이기 때문에 `FileReader`가 내부적으로 
   `FileInputStream`에 `InputStreamReader` 보조 스트림을 연결한 것이라고 볼 수 있음       */
   

성능 향상 보조 스트림

  • 프로그램의 실행 성능은 입출력이 가장 늦은 장치를 따라감
    • CPU와 메모리가 아무리 뛰어나도 하드 디스크의 입출력이 늦어지면 프로그램의 실행 성능은 하드 디스크의 처리 속도에 맞춰짐
    • 네트워크로 데이터를 전송할 때도 마찬가지. 느린 네트워크 환경이라면 컴퓨터 사양이 아무리 좋아도 메신저와 게임의 속도는 느릴 수밖에 없음
  • 프로그램이 입출력 소스와 직접 작업하지 않고 중간에 메모리 버퍼 (buffer)와 작업함으로써 실행 성능 향상 가능
    • 프로그램은 직접 하드 디스크에 데이터 보내지 않고 메모리 버퍼에 데이터를 보냄 → 쓰기 속도 향상
    • 버퍼는 데이터가 쌓이기를 기다렸다가 꽉 차게 되면 데이터를 한꺼번에 하드 디스크로 보냄으로써 출력 횟수 줄임
  • 바이트 기반 스트림에는 BufferedInputStreamBufferedOutputStream이 있고,
  • 문자 기반 스트림에는 BufferedReaderBufferedWriter가 있다.

BufferedInputStream과 BufferedReader

  • 입력 소스로부터 자신의 내부 버퍼 크기만큼 데이터를 미리 읽고 버퍼에 저장해둠
    • BufferedInputStream은 최대 8192 바이트
    • BufferedReader는 최대 8192 문자가 저장
Reader reader = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(reader);
String inputStre = br.readLine();

BufferedOutputStream과 BufferedWriter

  • BufferedOutputStream과 BufferedWriter는 프로그램에서 전송한 데이터를 내부 버퍼에 쌓아두었다가 버퍼가 꽉 차면 버퍼의 모든 데이터를 한꺼번에 보냄
  • 프로그램 입장에서 보면 직접 데이터를 보내는 것이 아니라 메모리 버퍼로 데이터를 고속 전송하기 때문에 실행 성능이 향상되는 효과를 얻게 됨
    • BufferedOutputStream은 8192 바이트
    • BufferedWriter는 8192 문자가 최대 저장
BufferedOutputStream bos = new BufferedOutputStream(바이트출력스트림);
BufferedWriter bw = new BufferedWriter(문자출력스트림);
  • 주의 사항
    • 버퍼가 가득 찼을 때만 출력을 하기 때문에 마지막 자투리 데이터 부분이 목적지로 가지 못하고 버퍼에 남아있을 수 있음
    • 그래서 마지막 출력 작업을 마친 후에는 flush() 메소드를 호출하여 버퍼에서 잔류하고 있는 데이터를 모두 보내도록 해야 함

기본 타입 입출력 보조 스트림

  • 바이트 스트림은 바이트 단위로 입출력하기 때문의 자바의 기본 데이터 타입 (boolean, char, short, int, long, float, double) 단위로 입출력할 수 없음
  • DataInputStream과 DataOutputStream 보조 스트림 연결하면 기본 데이터 타입으로 입출력 가능
DataInputStream dis = new DataInputStream(바이트입력스트림);
DataOutputStream dos = new DataOutputStream(바이트출력스트림);
  • 주요 메소드
  • 주의사항
    • 데이터 타입의 크기가 모두 다르므로 DataOutputStream으로 출력한 데이터를 다시 DataInputStream으로 읽어올 때는 출력한 순서와 동일한 순서로 읽어야 함

프린터 보조 스트림

  • PrintStream과 PrintWriter는 프린터와 유사하게 출력하는 print()println() 메소드를 가지고 있는 보조 스트림
  • 지금까지 사용했던 System.out이 바로 PrintStream 타입이기 때문에 print()println() 메소드를 사용할 수 있었음
  • PrintStream은 바이트 출력 스트림과 연결되고 PrintWriter()는 문자 출력 스트림과 연결
  • PrintStream과 PrintWriter는 거의 같은 기능을 가지고 있음
PrintStream ps = new PrintStream(바이트출력스트림);
PrintWriter pw = new PrintWriter(문자출력스트림);

객체 입출력 보조 스트림

  • 자바는 메모리에 생성된 객체를 파일 또는 네트워크로 출력할 수 있음

  • 객체는 문자가 아니기 때문에 바이트 기반 스트림으로 출력해야 함

  • 직렬화 (serialization)

    • 객체를 출력하기 위해서는 객체의 데이터(필드값)를 일렬로 늘어선 연속적인 바이트로 변경
  • 역직렬화 (deserialization)

    • 반대로 파일에 저장되어 있거나 네트워크에서 전송된 객체를 읽을 수도 있는데, 입력 스트림으로부터 읽어들인 연속적인 바이트를 객체로 복원하는 것
  • ObjectInputStream, ObjectOutputStream

    • 객체를 입출력할 수 있는 보조 스트림인 ObjectInputStream과 ObjectOutputStream을 제공
    • ObjectOutputStream은 바이트 출력 스트림과 연결되어 객체를 직렬화하는 역할
    • ObjectInputStream은 바이트 입력 스트림과 연결되어 객체로 역직렬화하는 역할
      ObjectInputStream ois = new ObjectInputStream(바이트입력스트림);
      ObjectOutputStream oos = new ObjectOutputStrema(바이트출력스트림);
  • 직렬화가 가능한 클래스 (Serializable)

    • Serialzable 인터페이스를 구현한 클래스만 직렬화할 수 있도록 제한
    • Serializable 인터페이스는 필드나 메소드가 없는 빈 인터페이스이지만 객체를 직렬화할 때 private 필드를 포함한 모든 필드를 바이트로 변환해도 좋다는 표시 역할을 함
    • 객체를 직렬화하면 바이트로 변환되는 것은 필드들이고 생성자 및 메소드는 직렬화에 포함되지 않음
    • 따라서 역직렬화할 때에는 필드의 값만 복원됨
    • 하지만 모든 필드가 직렬화 대상이 되는 것은 아님
    • 필드 선언에 static 또는 transient가 붙어 있을 경우에는 직렬화가 되지 않는다.
    public class XXX implements Serializable {
      public int field1;
      protected int field2;
      int field3;
      private int field4;
      public static int field5;	// 직렬화에서 제외
    			transient int field6;		// 직렬화에서 제외
    }
profile
github : https://github.com/kiaeh2323 , email : kiaeh9269@gmail.com

0개의 댓글