study/java

자바/JAVA 스트림과 입출력

올스왑 2021. 2. 9. 07:07

입출력 클래스는 다양하고, 사용법도 다양하기 때문에 개념만 잡고 사용할 때마다 문법을 찾아서 사용하자.

 

입출력을 알기 위해 스트림을 알아야 한다.

 

* 스트림은 다양한 장치에 독립적으로 일관성 있게 데이터 입출력을 제공하는 방식이다.

* 입출력이 구현 되는 곳에서는 모두 I/O 스트림을 사용한다.// 키보드, 파일, 메모리, 네트워크

* 단방향이다.

 

스트림은 입력과 출력, 바이트와 문자, 기반과 보조로 나눌 수 있다.

 

입력 스트림: 대상으로부터 자료를 읽어 들이는 스트림
출력 스트림: 대상으로부터 자료를 출력하는 스트림

바이트 단위 스트림: 바이트 단위로 처리하고 동영상, 음악 파일 등을 읽고 쓸 때 사용하는 스트림(XXXInputStream, XXXOutputStream)
문자 단위 스트림: 문자 처리를 위해 2바이트 단위로 처리하도록 구현된 스트림(XXXReader, XXXWriter)

기반 스트림: 대상에 직접 자료를 읽고 쓰는 기능의 스트림
보조 스트림: 직접 읽고 쓰는 기능은 없이 추가적인 기능을 더해주는 스트림. 보조 스트림은 항상 기반 스트림이나 또 다른 보조 스트림을 생성자 매개변수로 포함함

 

 

먼저 자바는 키보드와 모니터를 이용한 표준 입출력은 java.lang.System 클래스의 멤버 변수 in, out, err을 이용해 표준 입력, 표준 출력, 표준 에러를 제공한다. System 클래스는 JVM을 구성하는 표준 장치를 의미한다.

public class System{
	public static final InputStream in;
	public static final PrintStream out;
	public static final PrintStream err;
}

in은(바이트단위스트림) InputStream 클래스가 가진 메서드를 사용할 수 있고, err out PrintStream 클래스가 가진 메소드를 사용할 수 있다.

 

여기서 InputStream과 OutputStream 은 바이트 단위 입력, 출력 스트림의 최상위 클래스다. 두 클래스는 추상 클래스이다. 따라서 대부분 추상 메서드를 정의하고 있고 자식 클래스가 구현한다.

 

또한 PrintStream은 이미 예외를 처리한 메서드들로 구성되어 있어, out, err을 사용할 때 따로 예외 처리 코드를 작성하지 않아도 된다.

 

다른 입력 방법으로는 Scanner 클래스를 사용하는 방법이 있다. java.util 패키지의 입력 클래스다. 문자, 정수, 실수 등 다양한 자료형을 읽을 수 있고, 콘솔이나 파일, 바이트 스트림 등 여러 대상에서 자료를 읽을 수 있다. -> 생성자가 다양한다.

구분자를 이용해 자료를 읽을 수도 있다.

 

Console 클래스를 사용하면 System의 입출력 방법을 사용하지 않고 입출력이 가능하다. 도스창에서 사용가능하다(IDE X)

Console 클래스
출처: stackoverflow, InputStream/OutputStream for standard stream- Java

 

InputStream과 OutputStream에 대해 자세히 보자. 위에서 언급했다시피 두 클래스는 추상 메서드를 포함한 추상 클래스이다. 바이트 단위 입력, 출력 스트림 최상위 클래스.

 

InputStream이 제공하는 메서드

  • int read(); -> 한 바이트 읽기
  • int read(byte b[]); -> 버퍼로 읽기. 특정 크기의 byte배열을 매개변수로 전달하면 그 크기 단위로 읽는다. 읽은 값이 배열에 담긴다. 읽어들인 값의 크기가 반환된다.
    -> 한 버퍼를 연속으로 사용하는 경우, 전에 읽어들인 데이터가 남아 있을 수 있다. 이런 경우에는, for 문을 사용해 반환값만큼(읽어들인 값의 크기) 배열을 읽는다.
  • int read(byte b[], int off, int len); -> 버퍼를 off부터 len만큼 끊어 읽기

OutputStream이 제공하는 메서드

  • write(int b); -> 한 바이트 출력
    • FileOutputStream은 아스키코드 값으로 출력함. ->숫자 출력 안됨. + 파일 출력 관련 메서드에서는 매개변수에 파일명 뒤에 true를 추가하면 덮어쓰기가 아니고 값을 추가해서 붙여 쓴다.
  • wirte(byte[] b); -> 배열에 있는 자료를 배열의 크기만큼 출력
  • write(byte[] b, int off, int len); -> 잘라서 출력
  • flush(); -> 버퍼에 남아있는 데이터 출력, 출력 버퍼를 비울때 사용
  • close(); -> 스트림 닫기

read,write 메소드에서는 IOException, FileStream에서는 FileNotFoundException이 발생할 수 있기 때문에 예외처리가 필요하다.

 

InputStream의 하위 클래스

  • FileInputStream - 파일에서 바이트 단위로 자료를 읽음. 파일이 없는 경우 예외 발생.
  • ByteArrayInputStream - Byte배열 메모리에서 바이트 단위로 자료를 읽음.
  • FilterInputStream - 추가 기능을 제공하는 보조 스트림의 상위 클래스

OutputStream의 하위 클래스

  • FileOutputStream - 바이트 단위로 파일에 쓰기. 파일이 없는 경우, 파일 생성.
  • ByteArrayOutputStream - byte 배열에 바이트 단위로 쓰기
  • FilterOutputStream - 보조 스트림의 상위 클래스

 

 

문자 단위 스트림으로는 Reader와  Wirter가 있다. 문자 단위 입출력을 위한 최상위 입출력 스트림 클래스다.

 

Reader가 제공하는 메소드

  • read() -> 한 문자 읽기, 읽은 값을 반환
  • read(char[] buf); -> 파일로부터 buf 배열의 문자를 읽는다.
  • read(char[] buf, int off, int len); -> buf 배열의 문자 끊어 읽기

 

Writer가 제공하는 메서드

  • write(); -> 배열, char, String도 쓸 수 있다. 배열과 String은 끊어 출력 가능.
  • flust();
  • close(); -> 스트림 닫기, 출력 버퍼도 지워짐

 

 

Reader의 하위 클래스

  • FileReader - 파일에서 문자 단위로 읽는 스트림 클래스 -> 문자 인코딩 대로 읽음. 글자 안 깨짐.
  • InputStreamReader - 바이트 단위로 읽은 자료를 문자로 변환해주는 보조 스트림
  • BufferedReader - 배열을 제공해 한꺼번에 읽을 수 있는 기능을 제공해주는 보조 스트림

 

Writer의 하위 클래스

  • FileWriter - 파일에 문자 단위로 쓰는 스트림 클래스
  • OutputStreamWriter - 바이트 단위로 출력한 자료를 문자로 변환해주는 보조 스트림
  • BufferedWriter -배열을 제공해 한꺼번에 쓸 수 있는 기능을 제공하는 보조 스트림

www3.ntu.edu.sg/

 

 

 

보조 스트림은 보조적인 기능을 추가하는 스트림이다. 보조 스트림 생성자 안에 기반 스트림 생성자(또는 보조 스트림 생성자)를 넣어줘서 사용한다.

ex) InputStreamReader isr = new InputStreamReader(new FileInputStream("input.txt"));

 

FilterInputStream, FilterOutputStream이 보조 스트림의 상위 클래스다.

 

데코레이터 패턴이 적용되었다. -> 다른 스트림의 추가적인 기능을 구현한다.

 

InputStreamReader - 바이트 단위로 읽은 자료를 문자로 바꿔준다 ->Socket.getInputStream()은 바이트로만 받는다. 그래서 보조 스트림을 이용해 문자로 바꿔준다

OutputStreamWriter - 바이트 단위로 쓰는 자료를 문자로 바꿔준다

 

Buffered 스트림 - 버퍼링 기능을 제공해 입출력을 빠르게 할 수 있다. 8K 바이트 배열을 갖고, 생성자에서 크기를 임의로 조정할 수 있다.

 

BufferedInputStream - 바이트 단위로 읽는 스트림에 버퍼링 기능 제공
BufferedOutputStream - 바이트 단위로 출력하는 스트림에 버퍼링 기능 제공
BufferedReader - 문자 단위로 읽는 스트림에 버퍼링 기능 제공
BufferedWirter - 문자 단위로 출력하는 스트림에 버퍼링 기능 제공

 

DataInputStream, DataOutputStream -  바이트, 문자뿐 아니라 모든 기본 자료형(+ UTF-8)을 기반으로 데이터를 읽고 쓸 수 있게 해 준다.

DataInputStream, DataOutputStream 메소드

 

 

stackoverflow

 

 

 

직렬화: 인스턴스의 상태를 그대로 저장, 전송하거나(serialization) 다시 복원하는(deserialization) 방식.


ObjectInputStream, ObjectOutputStream - 보조 스트림을 사용한다.

 

직렬화는 외부에 인스턴스의 내용이 유출되는 것이므로 직렬화하려는 객체의 클래스는 Serializable 인터페이스를 구현해야 한다. 직렬화한다는 의도를 표시하는 방법이다. 이 인터페이스는 구현 코드가 없는데 이런 것을 maker interface라고 한다.

 

writeObject(), readObject() 메소드를 사용해 객체를 쓰고 읽는다.

 

Serializable 대신 Externalizable을 구현할 수도 있는데 그러면 메서드를 재정의해줘야 한다. 그렇게 되면 나중에 readObject(), writeObject() 방식을 원하는 대로 바꿀 수 있다.

 

직렬화가 안되거나, 직렬화하지 않으려는 멤버 앞에는 transient 키워드를 붙이면 된다.

 

자바의 File 클래스는 입출력 기능이 없다.  파일의 속성, 경로, 이름 등을 알 수 있다.

대신 RandomAccessFile 클래스가 있다.

  • 입출력 클래스 중 유일하게 파일 입출력을 동시에 할 수 있는 클래스
  • 파일 포인터가 있어 읽고 쓰는 위치의 이동이 가능하다.
  • 다양한 자료형에 대한 메소드가 제공된다.

 

추가적인 것들.

* read() 메소드에서 파일의 끝을 읽을 땐 -1이 반환된다.

* 문자를 읽는 방법
1. 바로 FileReader 로 읽거나
2. 바이트 스트림을 InputStreamReader로 감싼다 - 데코레이터 패턴