입출력 I/O 유진석
1.1 입출력 이란? I/O는 입,출력을 뜻하며 입,출력 이란 Input, Output의 약자로 입력과 출 력을 얘기한다. 입,출력은 컴퓨터 내부 또는 외부의 장치와 프로 그램간의 데이터를 주고 받는것을 말한다.
1.2 스트림(stream) 자바에서 입,출력을 수행하려면 두 대상을 연결하고 데이 터를 전송할 수 있는 무언가가 필요한데 이것을 스트림 (stream)이라고 한다. 스트림은 연속적인 데이터의 흐름을 물에 비유해서 붙여 진 이름인데, 물이 단 방향으로만 흐르는 것과 같이 스트림 은 단 방향 통신만 가능하기 때문에 하나의 스트림으로 입, 출력을 동시에 할 수 없다. 입력과 출력을 동시에 하려면 입력 스트림(input stream) 과 출력 스트림(output stream), 총 2개의 스트림이 필요 하다. 먼저 보낸 데이터를 먼저 받게 되어있어, 중간에 건너뜀 없 이 연속적으로 데이터를 주고 받는다. FIFO구조를 생각하 면 이해하기 쉽다.
1.3 바이트기반 스트림 (InputStream, OutputStream) 스트림은 바이트단위로 데이터를 전송하며 입,출력 대상에 따라 다음과 같은 입,출력 스트림이 있다. 위와 같이 여러 종류의 입,출력 스트림이 있으며, 어떠 한 대상에 대해서 작업을 할 것인지, 입력을 할 것인지 출력을 할 것인지에 따라서 해당 스트림을 선택해서 사용하면 된다. 입력 스트림 출력 스트림 입,출력 대상의 종류 FileInputStream FileOutputStream 파일 ByteArrayInputStream ByteArrayOutputStream 메모리(byte 배열) PipedInputStream PipedOutputStream 프로세스(프로세스간의 통신) AudioInputStream AudioOutputStream 오디오장치
1.3 바이트기반 스트림 (InputStream, OutputStream) 자바에서는 java.io 패키지를 통해서 다양한 입,출력 관련 클래스들을 제공하며, 표준화된 방법으로 입, 출력 대상이 달라져도 동일한 방법으로 입,출력이 가능하다. read()의 반환타입이 byte가 아니라 int인 이유는 반환값이 0~255와 -1이기 떼문이다 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 )
1.4 보조 스트림 보조 스트림은 실제 데이터를 주고받는 스트림이 아니기 때문에 데이터를 입출력 할 수 있는 기능은 없지만 스트림 의 기능을 향상시키거나 새로운 기능을 추가할 수 있다. 위 코드는 test.txt라는 파일을 읽기위해 FileInputStream 을 사용할 시, 입력 성능을 향상시키기 위해 버퍼를 사용하 는 보조스트림인 BufferedInputStream을 사용하는 코드 다. // 먼저 기반스트림을 생성한다. FileInputStream fis = new FileInputStream( “test.txt” ); // 기반 스트림을 이용해서 보조 스트림을 생성한다. BufferedInputStream bis = new BufferedInputStream( fis ); bis.read(); // 보조스트림인 BufferedInputStream으로부터 데이터를 읽는다. 버퍼를 사용할 때와 사용하지 않을 때의 성능차이가 나기 때문에 대부분 버퍼를 사용한다.
1.4 보조 스트림 BufferedInputStream, DataInputStream, DigestInputStream, LineNumberInputStream, PushbackInputStream은 모두 FilterInputStream의 자손들이고, FilterInputStream은 InputStream의 자손이라서 결국 모든 보조스트림 역시 Input Stream과 OutputStream의 자손들이라 입,출력 방법은 같다. 입력 출력 설명 FilterInputStream FilterOutputStream 필터를 이용한 입,출력 처리 BufferedInputStream BufferedOutputStream 버퍼를 이용한 입,출력 성능 향상 DataInputStream DataOutputStream int, float와 같은 기본형 단위로 데이터처리 SequenceInputStream SequenceOutputStream 두 개의 스트림을 하나로 연결 LineNumberInputStream 없음 읽어온 데이터의 라인 번호를 카운트 (JDK 1.1부터 LineNumberReader로 대체) ObjectInputStream ObjectOutputStream 객체 단위로 읽고 쓰는데 사용. 주로 파일을 이용하며 객체 직렬화와 관련있음 PrintStream 버퍼를 이용하며, 추가적인 print관련 기능 PushbackInputStream 버퍼를 이용해서 읽어온 데이터를 되돌리는 기능
1.5 문자 기반 스트림 바이트 기반이라 함은 1byte라는 뜻이다. 자바에서는 한 문자를 의미하는 char형이 1byte가 아니라 2byte기 때문에 바이트 기반의 스트림으로는 어려움이 있다. 문자 데이터를 입출력 할 때는 문자기반 스트림을 사용하자 문자기반 스트림의 이름은 바이트 기반 스트림의 이름에서 InputStream을 Reader로, OutputStream을 Writer로 바꾸 면 된다. 단 ByteArrayInputStream에 대응하는 문자기반 스트림은 char배열을 사용하는 CharArrayReader이다. InputStream Reader OutputStream Writer
2. 바이트 기반 스트림 ByteArrayInputStream/ByteArrayOutputStream은 메 모리, 즉 바이트 배열에 데이터를 임시로 바이트배열에 담아서 변환 등의 작업에 사용된다. 바이트 배열은 사용하는 자원이 메모리 밖에 없으므로 gc에 의해 자동적으로 자원을 반환 하기에 close()를 하지 않아도 된다.
2. 바이트 기반 스트림 read()나 write()가 IOException 을 발생시킬 수 있기 때문에 try-catch문으로 감싸 주었다. available()은 블러킹 없이 읽어 올 수 있는 바이트의 수를 반 환 한다. 블러킹이란 데이터를 읽어 올 때 데이터를 기다리기 위해 멈춰있는 것을 뜻함.
2. 바이트 기반 스트림 FileInputStream/FileOutputStream은 파일에 입, 출력 하기 위한 스트림이다.
3. 바이트 기반 보조스트림 BufferedInputStream/BufferedOutputStream은 스 트림의 입,출력 효율을 높이기 위해 버퍼를 사용하는 보조스트림이다. 프로그램에서 입력소스로부터 데이터를 읽기 위해 처 음으로 read()를 호출하면, BufferedInputStream은 입력소스로부터 버퍼 크기만큼의 데이터를 읽어다 자 신의 내부 버퍼에 저장한다. 프로그램에서 버퍼에 저장된 모든 데이터를 다 읽고 그 다음 데이터를 읽기 위해 read()가 호출되면, BufferedInputStream은 입력 소스로부터 다시 버퍼 크기 만큼의 데이터를 읽어다 버퍼에 저장해 놓는다.
3. 바이트 기반 보조 스트림 BufferedOutputStream에서 write()를 이용해 출력하며, 버퍼가 가득 차면, 그 때 버퍼의 모든 내용을 출력소스 에 출력한다. 그리고는 버퍼를 비우고 다시 프로그램으 로부터의 출력을 저장할 준비를 한다.
3. 바이트 기반 보조 스트림 DataInputStream/DataOutputStream 은 데이터 를 읽고 쓰는데 있어서 byte단위가 아닌 8가지 기 본 자료 형의 단위로 읽고 쓸 수 있다. DataIutputStream이 출력하는 형식은 각 기본 자 료형 값을 16진수로 표현하여 저장한다. 각 자료 형의 크기가 다르므로, 출력한 데이터를 다시 읽어 올 때는 출력했을 때의 순서를 염두해 두어야 한다.
3. 바이트 기반 보조 스트림
4. 문자기반 스트림 문자데이터를 다루는데 사용된 다는 것을 제외하 고는 바이트기반 스트림과 문자기반 스트림의 사 용방법은 거의 같다. 바이트기반 스트림의 조상이 InputStream/OutputStream인 것과 같이 문자 기 반의 스트림에서는 Reader/Writer가 그의 같은 역할을 한다. 단, byte 대신 char 배열을 사용한다.
4. 문자기반 스트림 FileReader/FileWriter는 파일로부터 텍스트데이터를 읽고, 파일에 쓰는데 사용된다. 사용방법은 FileInputStream/FileOutputStream과 다 르지 않다.
4. 문자기반 스트림 PipedReader/PipedWriter는 쓰레드 간에 데이터 를 주고받을 때 사용된다. 다른 스트림과는 달리 입력과 출력 스트림을 하나의 스트림으로 연결해 서 데이터를 주고받는다. 스트림을 생성한 다음 어느 한쪽 쓰레드에서 connect()를 호출해 입력스트림과 출력스트림을 연결한다. 입,출력을 마친 후에는 어느 한쪽 스트림만 닫아 도 나머지 스트림은 자동으로 닫힌다.
4. 문자기반 스트림
5. 문자기반 보조스트림 BufferedReader의 readLine()을 사용하면 데이터를 라인단 위로 읽어 올 수 있다는 장점이 있다. BufferedWriter는 newLine()이란 줄바꿈 해주는 메서드를 가지고 있다.
5. 문자기반 보조스트림 InputStreamReader/OutputStreamWriter는 이 름에서 알 수 있듯이 바이트기반 스트림을 문자기 반 스트림으로 연결시켜주는 역할을 한다. 바이트 기반 스트림의 데이터를 지정된 인코딩의 문자데이터로 변환하는 작업을 한다.
5. 문자기반 보조스트림
6. 표준 입,출력과 File 표준 입,출력은 콘솔을 통한 데이터 입력과 콘솔 로의 데이터 출력을 의미한다. 자바에서는 표준 입,출력을 위해 3가지 입,출력 스 트림인 System.in, System.out, System.err을 제 공하는데, 이 들은 자바 어플리케이션의 실행과 동시에 사용할 수 있게 자동적으로 생성되기 때문 에 개발자가 별로도 스트림을 생성하는 코드를 작 성하지 않고도 사용 가능하다.
6. 표준 입,출력과 File 기본적으로 System.in, System.out, System.err의 입,출 력 대상이 콘솔이지만 setIn(), setOut(), setErr()을 사용 하면 입,출력을 콘솔 이외에 다른 입,출력 대상으로 변 경할 수 있다.
6. 표준 입,출력과 File 자바에서는 입력과 출력이 분리되어 작업을 하도록 되어 잇는데 RandomAccessFile만은 하나의 클래스 로 파일에 대한 입력과 출력을 모두 할 수 있도록 되어 있다. RandomAccessFile는 DataInput, DataOutput 인터페 이스를 모두 구현했기 때문에 기본 자료형 단위로 데 이터를 읽고 쓸 수 있다. RandomAccessFile의 가장 큰 장점은 파일의 어느 위 치에나 읽기/쓰기가 가능하다는 것이다. 이것을 가능하게 하기 위해서 내부적으로 파일 포인 터를 사용한다.
6. 표준 입,출력과 File
6. 표준 입,출력과 File File은 기본적이면서도 가장 많이 사용되는 입,출 력 대상이기 때문에 중요하다.
6. 표준 입,출력과 File
6. 표준 입,출력과 File
6. 표준 입,출력과 File
7. 직렬화 직렬화(serialization)란 객체를 데이터 스트림으 로 만드는 것을 뜻한다. 객체에 저장된 데이터를 스트림에 쓰기(write)위해 연속적인(serial) 데이 터로 변환하는 것을 말한다. 반대로 스트림으로부터 데이터를 읽어서 객체를 만드는 것을 역직렬화라고 한다.(deserialization) 객체를 직렬화 한다는 것은 객체의 모든 인스턴스 변수만을 저장한다는 것을 뜻한다. 클래스 변수나 메서드 등은 포함되지 않는다.
7. 직렬화 직렬화에는 ObjectOutputStream, 역직렬화에는 ObjectInputStream을 각각 사용한다. 둘 다 InputStream, OutputStream을 직접 상속 받지만 기반스트림을 필요로 하는 보조 스트림이 다. 직렬화하여 저장하려면 writeObject(Object obj) 를 사용해서 저장, 역직렬화 할 때는 readObject() 를 사용한다. 단 readObject()는 리턴타입이 Object이기 때문에 원래의 타입으로 형 변환을 해줘야 한다.
7. 직렬화 직렬화가 가능한 클래스를 만드는 방법은 Serializable 인터페이스를 구현하면 된다. 하지만 부모 클래스는 구현하지 않고 자식 클래스 만 구현 하였다면 부모 클래스에 속하는 변수는 직렬화 대상에서 제외된다.
7. 직렬화
7. 직렬화
7. 직렬화 직렬화된 객체를 역직렬화 할 때는 직렬화 했을 때와 같은 클래스를 사용해야 한다. 그러나 클래 스의 이름이 같더라도 클래스의 내용이 변경된 경 우 역직렬화는 실패한다. 객체가 직렬화될 때는 클래스에 정의된 멤버들의 정보를 이용해서 serialVersionUID라는 클래스의 버전을 자동생성해서 직렬화 내용에 포함 된다. 그래서 역직렬화 할 때 클래스의 버전을 비교함으 로써 버전이 일치하는지 확인 할 수 있는 것이다.