✔I/O
데이터의 입력(input)과 출력(output)
- 한쪽에서 주고 한쪽에서 받는 구조.
- 입력과 출력의 끝단 : 노드(Node)
- 두 노드를 연결하고 데이터를 전송할 수 있는 개념 : 스트림(Stream)
- 이전에 작성한 스트림과는 다른 개념
- 데이터를 받아 들이는 경우 : 입력 스트림(Input Stream)
- 데이터를 내보내는 경우 : 출력 스트림(Output Stream)
- 단방향 통신. (입력과 출력을 같이 처리할 수 없음.)
1. 노드 스트림
- 노드 스트림(Node Stream) : 노드에 연결되는 스트림. 직접적으로 데이터를 입력 받거나, 출력하는 역할.
데이터 타입에 따라 byte or char | 방향에 따라 입력 또는 출력 | 노드 타입에 따라 | 최종 노드 스트림 |
---|
XXStream(Byte) | InputStream | 키보드 | InputStream |
| OutputStream | 모니터 | OutputStream |
| InputStream OutputStream | File | FileInputStream,FileOutputStream |
| | ByteArray | ByteArrayInputStream, ByteArrayOutputStream |
| | Pipe | PipeInputStream, PipeOutputStream |
XXer(char) | Reader | 키보드 | Reader |
| Writer | 모니터 | Writer |
| Reader Writer | File | FileReader, FileWriter |
| | CharArray | CharArrayReader, CharArrayWriter |
| | String | StringReader, StringWriter |
| | Pipe | PipeReader, PipeWriter |
- InputStream의 주요 메서드
- read() : 데이터를 byte 단위로 읽음.
- read() : byte를 읽어 int 반환. 값이 없다면 -1 반환
- read(byte b[]) : 데이터를 일겅 b를 채우고 바이트의 개수 반환. 값이 없다면 0 반환. b의 크기 즉, buffer만큼 읽음.
- read(byte b[], int offset, int len) : 최대 len만큼 데이터를 읽어 b의 offset부터 저장 후 읽은 바이트 반환. len+offset은 b의 크기 이하여야 함.
- close() : 스트림 종료해서 자원 반납.
- UTF-8에서 한글은 3bytes이므로 기본 read()로 읽을 수 없음.
- 버퍼를 사용하면 되지만, 버퍼가 작다면 손상 발생 가능.
- char형을 읽고 쓰는 Reader, Writer 사용하여 해결.
- Reader의 주요 메서드
- read() : 데이터를 char 단위로 읽음.
- read() : char를 읽어 int 반환. 값이 없다면 -1 반환
- read(char cbuf[]) : 데이터를 읽어 cbuf를 채우고 읽은 문자 개수 반환. 값이 없다면 0 반환.
- read(char cbuf[], int off, int lne) : 최대 len 만큼 읽어 cbuf의 off부터 저장하고 읽은 문자 개수 반환. len+off는 cbuf의 크기 이하여야 함.
- read(java.nio.CharBuffer target) : 데이터를 읽어 target에 저장. target이 cbuf 대체.
- close()
- Reader는 유니코드에 대해 안전.
- OutputStream의 주요 메서드
- write() : 데이터를 byte로 출력.
- write(int b) : b의 내용 출력.
- write(byte b[]) : b의 내용 문자열로 변환해 출력.
- write(byte b[], int off, int len) : b의 off부터 off+len+1만큼을 문자열로 변환해 출력.
- close() : 스트림 종료. 내부적으로 flush() 호출
- flush() : 버퍼가 있는 스트림에서 버퍼의 내용 출력 후 비움.
- Writer의 주요 메서드
- write() : 데이터를 문자열로 출력.
- write(int c) : c의 내용 출력.
- write(char cbuff[]) : cbuf를 문자열로 변환해 출력.
- write(char cbuff[], int off, int len) : cbuff의 off부터 off+len+1만큼을 문자열로 변환해 출력.
- write(String str) : str 출력.
- write(String str, int off, int len) : str의 off부터 off+len+1만큼 출력.
- append() : 출력 함수.
- append(CharSequence csq) : csq 출력 후 Writer 반환.
- append(CharSequence csq, int start, int end) : csq의 start부터 end까지 출력하고 Writer 반환.
- close() : 스트림 종료. 내부적으로 flush() 호출.
- flush() : 버퍼가 있는 스트림에서 버퍼의 내용 출력 후 비움.
- File : 가장 기본적인 입출력 장치 중 하나로 파일과 디렉터리를 다루는 클래스.(내용에는 관여하지 못함!! 입력, 출력 스트림을 이용한다.)
- 주요 메서드가 너무 많아 링크로 대체..헤헤
- 파일 경로 입력시 운영체제마다 구분자가 다르므로
File.separator
를 사용하여 대체할 수 있다.
- File XXStream : byte로 데이터 처리.
FileInputStream(String name | File file)
: 문자열, 파일 객체를 매개변수로 입력 스트림 생성.
FileOutputStream([String name | File file], boolean append)
: 입력 스트림과 동일하게 출력 스트림 생성. append 옵션으로 기존 파일 뒤에 쓰거나 새로 생성할 수 있음.
- buffer의 크기를 조절해 속도를 조절할 수 있다.(일반적으로 버퍼가 클수록 속도가 빨라짐. 하지만 메모리가 많이 필요해짐.)
- File XXXer : 문자(char)로 데이터 처리.
FileReader
: 파일 읽기
FileWriter
: 파일 쓰기
2. 보조 스트림
- 보조 스트림(Filter Stream, Processing Stream) : 다른 스트림에 부가적인 기능을 제공하는 스트림.
스트림 체이닝(Stream Chaining)
: 필요에 따라 보조 스트림을 연결하여 사용 가능.
진행 과정
- 노드(입력) -> 노드 스트림 -> 보조 스트림 -> 프로그램 -> 보조 스트림 -> 노드 스트림 -> 노드(출력)
-
보조 스트림의 종류
기능 | byte 기반 | char 기반 |
---|
byte 스트림 -> char 스트림 변환 | InputStreamReader | |
| OutputStreamReader | |
버퍼링을 통한 속도 향상 | BufferedInputStream | BufferedReader |
| BufferedOutputStream | BufferedWriter |
객체 전송 | ObjectInputStream | |
| ObjectOutputStream | |
- 생성 : 이전 스트림을 생성자의 파라미터에 연결.
new BufferedReader(new InputStreamReader(System.in))
- 종료 : 보조 스트림의 close() 호출시 노드 스트림의 close()까지 호출.
- 사용할 스트림의 결정 과정
- 노드가 무엇인가?
- 타입은 문자열인가? 바이트인가?
- 방향이 무엇인가? (노드 스트림 구성.)
- 추가 기능이 필요한가? (보조 스트림 구성.)
- 노드 구성 예시
- 파일 이동 : File -> byte -> (읽기/쓰기) -> File(Input/Output)Stream -> Buffered(Input/Output)Stream
- 키보드에서 유니코드 읽기 : Keyboard -> byte -> 읽기 -> System.in(InputStream) -> InputStreamReader -> BufferedReader
- 객체 파일로 저장 : File -> byte -> 쓰기 -> FileOutputStream -> ObjectOutputStream
3. 보조 스트림 활용(1)
- InputStreamReader & OutputStreamWriter
- byte 기반 스트림을 char 기반으로 변경해주는 스트림.
- 문자열 관리를 위해서는 char 단위가 유리.
- 키보드 입력(byte stream)을 처리하는 경우 주로 사용.
- 변환시 encoding 지정 가능
- InputStreamReader(InputStream in, [String charsetName/Charset cs]) : 캐릭터 셋 생략시 기본(설정값), 입력시 해당 캐릭터 셋으로 InputStreamReader 생성.
- OutputStreamWriter(OutputStream out, [String charsetName/Charset cs]) : 캐릭터 셋 생략시 기본(설정값), 입력시 해당 캐릭터 셋으로 OutputStreamWriter 생성.
- Buffered 계열
- 일반적인 버퍼의 역할 : 데이터를 한 곳에서 다른 한 곳으로 전송하는 동안 일시적으로 그 데이터를 보관하는 메모리의 영역. 버퍼에 저장하여 필요한 처리를 거친 뒤 전송. [전송 -> 처리]로 진행할 경우 성능 문제가 발생하여 버퍼로 데이터를 임시 저장한 뒤 처리 진행.
- Buffered 계열 스트림 : 스트림의 입/출력 효율을 높이기 위해 버퍼를 사용.
- BufferedInputStream(InputStream in), BufferedOutputStream(OutputStream out) : byte 계열
- 기본적으로 8192 bytes 크기의 버퍼 사용.
- size를 매개변수로 전달하면 해당 size만큼의 버퍼 사용.
- BufferedReader(Reader in), BufferedWriter(Writer out) : 문자 계열
- 기본적으로 8192 char (16384 bytes) 크기의 버퍼 사용.
- size를 매개변수로 전달하면 해당 size만큼의 버퍼 사용.
- BufferedReader-readLine() : 줄 단위로 데이터를 읽어 들임.(개행 문자로 줄 인식.)
- Scanner와 BufferedReader
- char 형태의 데이터를 읽기위한 클래스들
Scanner
: 자동 형변환을 지원하는 등 간편함. 속도가 느림.
BufferedReader
: 직접 스트림을 구성해야 하는 등 번거로움. 속도가 빠름.
4. 보조 스트림 활용(2) - 객체 직렬화
- 객체 직렬화(Serialization) : 객체를 파일 등에 저장하거나 네트워크로 전송하기 위해 연속적인 데이터로 변환하는 것.(반대의 경우 역직렬화(Deserialization))
serialVersionUID
: 클래스의 변경 여부를 파악하기 위한 유일키.
- 직렬화, 역질화의 UID가 다를 경우 예외 발생.
- UID 미설정시 컴파일러가 자동 생성
- 멤버 변경(추가 혹은 삭제)으로 인한 컴파일 시마다 변경 -> InvalidClassException 초래.
- 직렬화되는 객체에 대해 serialVersionUID 설정 권장.
- 직렬화되기 위한 조건
- Serializable 인터페이스 구현.
- 클래스의 모든 멤버가 Serializable 인터페이스를 구현해야함.
- 직렬화에서 제외하려는 멤버는 transient 선언.
- 객체 저장
- ObjectOutputStream(OutputStream out) : out을 이용해 ObjectOutputStream 객체 생성.
- writeObject(Object obj) : obj 직렬화 후 출력(저장).
ClassName Test = new ClassName();
File target = new File();
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(target))){
oos.writeObject(Test);
}catch(IOException e){}
- 객체 읽기
- ObjectInputStream(InputStream int) : int을 이용해 ObjectInputStream 객체 생성.
- writeObject() : 직렬화된 데이터를 역직렬화하여 Object 반환.
File target = new File();
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(target))){
Object readed = ois.readObject();
if (readed != null && readed instanceof Person){
Person casted = (Person) readed;
}
}catch(IOException e){}