Don't give up!

[JAVA] 자바의 정석 정리 (15장 - 입출력 I/O) 본문

개발서적/JAVA의 정석

[JAVA] 자바의 정석 정리 (15장 - 입출력 I/O)

Heang Lee 2021. 6. 13. 23:30
자바의 정석을 읽고 정리한 내용입니다.

Java의 정석 - YES24

 

Java의 정석

최근 7년동안 자바 분야의 베스트 셀러 1위를 지켜온 `자바의 정석`의 최신판. 저자가 카페에서 12년간 직접 독자들에게 답변을 해오면서 초보자가 어려워하는 부분을 잘 파악하고 쓴 책. 뿐만 아

www.yes24.com


1. 입출력에서의 스트림

(1) 입출력에서 스트림은 데이터를 운반하는데 사용되는 연결통로이다. 

(2) 스트림은 단방향통신만 가능하기 때문에 하나의 스트림으로 입력과 출력을 동시에 처리할 수 없다. 입력과 출력을 동시에 수행하려면 입력을 위한 입력스트림과 출력을 위한 출력스트림 2개의 스트림이 필요하다.

(3) 스트림은 먼저 보낸 데이터를 먼저 받게 되어 있으며 건너뜀 없이 연속적으로 데이터를 주고받는다.

(4) 스트림은 바이트단위로 데이터를 전송하며 입출력 대상에 따라 다른 입출력스트림을 사용한다.

입력스트림 출력스트림 입출력 대상의 종류
FileInputStream FileOutputStream 파일
ByteArrayInputStream ByteArrayOutputStream byte 계열 메모리
PipedInputStream PipedOutputStream 프로세스
AudioInputStream AudioOutputStream 오디오장치

(5) 자바에서는 java.io 패키지를 통해서 많은 종류의 입출력관련 클래스들을 제공하고 있으며 입출력의 대상이 달라져도 동일한 방법으로 입출력이 가능하다.

InputStream OutputStream
abstract int read( ) abstract void write(int b)
int read(byte[] b) void write(byte[] b)
int read(byte[] b, int off, int len) void write(byte[] b, int off, int len)

(6) 보조스트림은 스트림의 기능을 보완하기 위해 제공된 스트림이다. 실제 데이터를 주고받는 스트림이 아니지만 스트림의 기능을 향상시키거나 새로운 기능을 추가할 수 있다. 스트림을 먼저 생성한 다음에 이를 이용해서 보조스트림을 생성해야 한다.

입력 출력 설명
FilterInputStream FilterOutputStream 필터를 이용한 입출력 처리
BufferedInputStream BufferedOutputStream 버퍼를 이용한 입출력 성능향상
DataInputStream DataOutputStream int, float와 같은 기본형 단위로 데이터를 처리
SequenceInputStream 없음 두 개의 스트림을 하나로 연결
LineNumberInputStream 없음 읽어 온 데이터의 라인 번호를 카운트
ObjectInputStream ObjectOutputStream 데이터를 객체단위로 읽고 쓰는데 사용
없음 PrintStream 버퍼를 이용하여 추가적인 print관련 기능
PushbackInputStream 없음 버퍼를 이용해서 읽어 온 데이터를 다시 되돌리는 기능

(7) Java에서는 char형이 2byte이기 때문에 바이트기반 스트림으로 2byte인 문자를 처리하는 데 어려움이 있다. 따라서 문자 데이터를 입출력할 때는 바이트기반 스트림 대신 문자기반 스트림을 사용해야 한다.

바이트기반 스트림 문자기반 스트림
FileInputStream
FileOutputStream
FileReader
FileWriter
ByteArrayInputStream
ByteArrayOutputStream
CharArrayReader
CharArrayWriter
PipedInputStream
PipedOutputStream
PipedReader
PipedWriter
BufferedInputStream
BufferedOutputStream
BufferedReader
BufferedWriter
FilterInputStream
FilterOutputStream
FilterReader
FilterWriter
PrintStream PrintWriter
PushbackInputStream PushbackReader

(8) flush( )는 버퍼가 있는 출력스트림의 경우에만 의미가 있으며 OutputStream에 정의된 flush( )는 아무 일도 하지 않는다.

(9) 프로그램이 종료될 때 JVM이 스트림을 자동적으로 닫아 주기는 하지만 스트림을 사용해서 모든 작업을 마치고 난 후에는 close( )를 호출해서 반드시 닫아주어야한다.

(10) 문자기반 스트림은 여러 종류의 인코딩과 자바에서 사용하는 유니코드(UTF-16)간의 변환을 자동적으로 처리해준다. Reader는 특정 인코딩을 읽어서 유니코드로 변환하고 Writer는 유니코드를 특정 인코딩으로 변환하여 저장한다.

(11) BufferedReader/BuffererWriter는 버퍼를 이용하여 입출력의 효율을 높이므로 사용하는 것이 좋다. readLine( )을 사용하면 데이터를 라인 단위로 읽을 수 있고 newLine( )은 줄바꿈해주는 메서드이다.

(12) InputStreamReader/OutputStreamWriter는 바이트기반 스트림을 문자기반 스트림으로 연결시켜주는 역할을 한다. 인코딩을 지정해주지 않는다면 OS에서 사용하는 인코딩을 사용해서 파일을 해석해 보여주기 때문에 인코딩의 문자데이터로 변환하는 작업이 필요하다.

2. 표준입출력

(1) 자바에서는 표준 입출력을 위해 System.in, System.out, System.err를 제공한다.

(2) 자바 어플리케이션의 실행과 동시에 사용할 수 있게 자동적으로 생성되기 때문에 개발자가 별도로 스트림을 생성하는 코드를 작성하지 않고도 사용이 가능하다.

(3) System.in, System.out, System.err의 입출력대상은 콘솔화면이지만 setIn( ), setOut( ), setErr( )를 사용하면 입출력을 콘솔 이외에 다른 입출력 대상으로 변경하는 것이 가능하다.

3. File

(1) 자바에서는 File클래스를 통해 파일과 디렉토리를 모두 다룰 수 있도록 하고 있다.

(2) File인스턴스는 파일일 수도 있고 디렉토리일 수도 있다.

(3) File인스턴스의 생성은 파일의 경로를 입력하여 생성된다. 주로 경로를 포함해서 지정해주지만 파일 이름만 사용해도 가능하다. 이 경우 프로그램이 실행되는 위치가 경로로 간주된다.

(4) 파일명이나 디렉토리명으로 지정된 문자열이 유효하지 않더라도 컴파일 에러나 예외를 발생시키지 않는다.

(5) File인스턴스를 생성했다고 해서 파일이나 디렉토리가 생성되는 것은 아니다. 새로운 파일을 생성하기 위해서는 File인스턴스를 생성한 다음, 출력스트림을 생성하거나 createNewFile( )을 호출해야한다.

File (Java Platform SE 8 ) (oracle.com)

 

File (Java Platform SE 8 )

Returns the absolute pathname string of this abstract pathname. If this abstract pathname is already absolute, then the pathname string is simply returned as if by the getPath() method. If this abstract pathname is the empty abstract pathname then the path

docs.oracle.com

4. 직렬화

(1) 직렬화란 객체를 데이터 스트림으로 만드는 것을 뜻한다. 객체에 저장된 데이터를 스트림에 쓰기 위해 연속적인 데이터로 변환한다.

(2) 객체는 인스턴스 변수만으로 메모리 공간을 사용한다. 클래스 변수나 메서드는 인스턴스마다 다른 값을 가질 수 없기 때문.

(3) 직렬화에는 ObjectInputStream을 사용하고 역직렬화에는 ObjectInputStream을 사용한다. 두 스트림은 각각 InputStream과 OutputStream을 직접 상속받지만 보조스트림이다. 객체를 생성할 때 입출력할 스트림을 지정해주어야 한다.

(4) 직렬화가 가능한 클래스를 만드려면 java.io.Serializable 인터페이스를 구현하도록 만들어야 한다. Serializable 인터페이스는 아무런 내용도 없는 빈 인터페이스이지만 직렬화를 고려하여 작성한 클래스인지 판단하는 기준이 된다.

(5) 직렬화된 객체는 정의된 멤버들의 정보를 이용해서 serialVersionUID라는 클래스의 버전을 자동생성해서 직렬화 내용에 포함시킨다. 역직렬화할 때 클래스의 버전을 비교함으로써 직렬화할 때의 클래스의 버전과 일치하는지 확인할 수 있다. 만약 클래스의 내용이 변경된 경우 역직렬화는 실패하며 예외를 발생시킨다.