Java Standard - 입출력(IO)
...
스트림(stream)
입출력이란? 컴퓨터 내부 또는 외부의 장치와 프로그램간의 데이터를 주고받는 것
- 의미: 데이터 전달을 위해 대상을 연결하고 데이터를 전송하는 것, 데이터를 운반하는데 사용되는 연결 통로
- 이전에 배운 스트림과 이 스트림은 같은 용어지만, 다른 개념
- 특징
- 단방향 통신만 가능 → 입력 스트림, 출력 스트림 각각 따로 존재
- 중간에 건너 뜀 없이 연속적으로 데이터를 주고 받음
- First In First Out 구조
- 구조
- 스트림
- 바이트기반 스트림
- FileInputStream/FileOutputStream
- ByteArrayInputStream/ByteArrayOutputStream
- PipedInputStream/PipedOutputStream
- AudioInputStream/AudioOutputStream
- 바이트기반 보조 스트림
- 문자기반 스트림
- 문자기반 보조 스트림
- 바이트기반 스트림
- 스트림
바이트기반 스트림 - InputStream, OutputStream
- 의미: 바이트 단위로 데이터를 전송, 입출력을 처리할 수 있는 표준화된 방법을 제공
- 메서드
read()
/write()
: 추상 메서드, 입출력 대상에 따라 방법이 달라지기 때문read(byte[] b)
/write(byte[] b)
:read()
/write()
를 이용하기 때문에, 이를 구현하지 않으면 의미가 없음
⇒
read()
/write()
는 반드시 구현되어야하는 핵심 메서드
보조스트림
- 의미: 스트림의 기능을 보완하기 위한 스트림
- 특징
- 실제 데이터를 주고받는 스트림이 아님 → 스트림을 생성한 뒤 이를 이용해 보조스트림을 생성해야함
- 스트림의 기능을 향상시키거나 새로운 기능 추가 가능
ex) 보조스트림
BufferedInputStream
은 기반 스트림인FileInputStream
에게 버퍼라는 기능만 제공하고, 실제 데이터 입출력은FileInputStream
이 수행함
문자기반 스트림 - Reader, Writer
- 의미: 문자 데이터를 입출력하는 스트림
- 기존에는 바이트단위(=입출력 단위가 1byte) 이기 때문에 문자(2byte)를 다루기에 어려움이 있었는데, 이를 보완하기 위함
- 특징
- 자식 클래스의 이름만 조금 다를 뿐, 대부분이 같음
바이트기반 스트림
InputStream과 OutputStream
- 의미: 모든 바이트기반 스트림의 조상
- 메서드
ByteArrayInputStream과 ByteArrayOutputStream
- 의미: 바이트배열에 데이터를 입출력하는데 사용되는 스트림
- 다른 곳에 입출력하기 전에 데이터를 임시로 바이트배열에 담아 변환등의 작업을 할 때 사용함
- 특징
- 사용하는 자원이 메모리밖에 없어 가비지컬렉터에 의해 자동적으로 자원을 반환 →
close()
로 닫지 않아도 됨
- 사용하는 자원이 메모리밖에 없어 가비지컬렉터에 의해 자동적으로 자원을 반환 →
- 메서드
read(byte[] b, int off, int len)
/write(byte[] b, int off, int len)
: 한번에 옮기는 데이터의 크기를byte[] b
만큼으로 조정함 → 작업의 효율 증가read()
/write()
:IOException
발생시킬수 있어서try-catch
문으로 감싸줘야함available()
:blocking
없이 읽어올 수 있는 바이트의 수 반환
blocking
블락킹이란? 데이터를 읽어올 때, 데이터를 기다리기 위해 멈춰 있는 것 ex) 사용자가 데이터를 입력하기 전까지 기다리고 있을때 = 블락킹 상태
FileInputStream과 FileOutputStream
- 의미: 파일에 입출력을 하기 위한 스트림
- 메서드
- 생성자
- 파일 이름으로 생성: 실제 파일과 연결된 스트림 생성, String(=파일이름)으로 생성하는 것
File
인스턴스로 생성: 이전과 같음,File
인스턴스로 생성파일 디스크립터로 생성
파일디스크립터?
- 메서드
read()
: 반환값이 int형인 이유는 1byte씩 데이터를 읽지만, 데이터의 범위가 십진수로 0~255범위의 정수 값이고, 값이 없음을 알리는 -1까지 필요했기 때문
- 생성자
바이트기반의 보조스트림
FileInputStream과 FilterOutputStream
- 의미: 모든 보조스트림의 조상
- 특징
- 모든 보조스트림은 자체적으로 입출력을 수행할 수 없음
- 보조스트림은 기반 스트림을 필요로함 ⇒ 생성자로 표현
- 기반 스트림의 메서드를 그대로 호출함
- 생성자는 접근제한자가 protected이기 때문에 인스턴스 생성이 아니라 상속을 통해 오버라이딩 되어야함
- 모든 보조스트림은 자체적으로 입출력을 수행할 수 없음
BufferedInputStream과 BufferedOutputStream
- 의미: 입출력 효율을 높이기 위해 버퍼를 사용하는 스트림
- 특징
- 적당한 버퍼의 크기는 버퍼의 크기변경한 후 테스트를 통해 알 수 있음
- 동작 방식
- 데이터 읽기
- 입력소스로 부터 버퍼의 크기만큼 데이터를 읽음
- 읽은 데이터는 내부 버퍼에 저장
- 프로그램에서는
BufferedInputStream
의 내부 버퍼에 저장된 데이터를 읽음 - 프로그램에서 버퍼의 모든 내용을 다 읽으면, 그 다음 데이터를 읽기 위해
read()
를 호출 - 입력소스에서 버퍼의 크기만큼 데이터를 가져오는 1번 과정으로 돌아가 끝까지 다 읽을때까지 반복
- 데이터 읽기
- 데이터 쓰기
- 프로그램에서
write()
를 이용한 데이터 출력이BufferedInputStream
의 내부 버퍼에 저장 - 버퍼가 가득 차면, 버퍼의 모든 내용을 출력소스에 출력
- 버퍼를 비움
write()
를 통한 데이터 출력 내용이 버퍼에 저장되는 1번 과정으로 돌아가 출력의 끝까지 반복
- 프로그램에서
flush()
를 쓰는 이유 버퍼가 가득 찼을때만 출력소스에 출력을 하기 때문에, 마지막 부분이 출력이 되지 않을 수 있음 이를 방지하기 위해서flush()
가 있음
보조스트림의
close()
flush()
호출 → 기반 스트림의close()
호출때문에 보조스트림 이용시 보조스트림의
close()
를 호출 하는 것이 좋음
DataInputStream과 DataOutputStream
- 의미: 데이터를 읽고 쓸 때 1byte씩 읽고 쓰는 것이 아닌, 8가지 기본 자료형의 단위로 읽고 쓸 수 있음
- 특징
- 출력 형식이 각 기본 자료형의 값을 16진수로 표현해 저장 →데이터를 변환하지 않아도됨
- 각 자료형의 크기가 다르므로, 출력한 데이터를 읽어 올 때 출력했던 순서를 염두해야함
데이터를 읽을때 파일의 끝에 도달하면
EOFException
, I/O 에러시IOException
발생→
try-catch문
으로 감싸주는 방식으로 코드를 짜야함- 스트림의 참조변수가 null인지 확인 후
close()
호출
- 메서드
SequenceInputStream
- 의미: 여러개의 입력스트림을 연속적으로 연결해서 하나의 스트림으로부터 데이터를 읽는 것과 같이 처리할 수 있도록 함
- 큰 파일을여러개의 작은 파일로 나누거나, 하나의 파일로 합치는 작업시 유용
- 특징
- 다른 보조스트림과 달리
FileInputStream
을 상속 한 것이 아니라,InputStream
을 직접 상속함
- 다른 보조스트림과 달리
스트림 연결 방법
- 2개가 넘는 스트림 연결시
Enumeration
에 저장해야함Enumeration
에 저장된 순서대로 스트림이 이어짐
- 2개가 넘는 스트림 연결시
PrintStream
- 의미
- 기반스트림에 다양한 형태로 출력할 수 있는 메서드(
print
,printf
,println
등)를 오버로딩해서 제공 - 문자기반 스트림의 역할을 수행함
- 기반스트림에 다양한 형태로 출력할 수 있는 메서드(
- 특징
- 예외를 던지지 않고, 내부에서 처리함 → 매우 자주 사용되기 때문에
PrintWriter
:PrintStream
보다 향상된 스트림- System.out이
PrintStream
이라 둘 다 사용하게 됨 - 다양한 언어의 문자를 처리하는데 적함
- System.out이
메서드
- printf의 옵션
문자기반 스트림
문자데이터를 다루는데 사용된다는 것만 바이트기반 스트림과 다름
Reader와 Writer
- 의미: 문자기반 스트림의 조상
- 특징
- 2byte로 스트림을 처리함
- 인코딩 처리
- 유니코드(UTF-16)간의 변환을 자동적으로 처리, 특정 인코딩을 읽어서 유니코드로 변환하거나 반대의 일을 함
- 메서드
FileReader와 FileWriter
- 의미: 파일로부터 텍스트 데이터를 읽고 파일에 쓰는데 사용됨
- 특징
FileInputStream
을 이용해 한글 출력시 한글이 깨짐
PipedReader와 PipedWriter
- 의미: 쓰레드간에 데이터를 주고 받을 때 사용됨
- 특징
- 입출력 스트림이 나눠지지 않고, 하나의 스트림
- 한쪽 쓰레드에서
connect()
호출로 연결이 됨 - 한쪽 쓰레드에서만 스트림을 닫아도 닫힘
- 쓰레드 시작 전에 연결해야함
- 내부적으로
String Buffer
를 가지고 있어서 이를 가지고 처리함
- 동작 방법: 스트림 생성 → 한쪽 쓰레드에서 연결(쓰레드 시작전) → 쓰레드 시작 → 한쪽에서 스트림 종료
StringReader와 STringWriter
- 의미: 입출력 대상이 메모리인 스트림, 내부의 StringBuffer를 소유하고 있음
- 메서드
StringBuffer getBuffer()
: 출력 데이터가 저장된StringBuffer
를 얻음String toString()
: 출력된(=StringBuffer
에 저장된) 문자열 반환
문자기반의 보조스트림
BufferedReader와 BufferedWriter
- 의미: 버퍼를 이용해서 입출력의 효율을 높이는 보조스트림
- 특징
- 데이터를 라인 단위로 읽을 수 있음
InputStreamReader와 OutputStreamWriter
- 의미: 바이트기반 스트림을 문자기반 스트림으로 연결시켜주고, 바이트기발 스트림의 데이터를 지정된 인코딩의 문자 데이터로 변환하는 작업을 수행하는 보조스트림
- 특징
- 인코딩을 지정하지 않으면 OS의 기본 인코딩으로 데이터를 저장함
- 메서드
표준입출력과 File
표준입출력 - System.in, System.out, System.err
- 의미: 콘솔을 통한 데이터 입력과 콘솔로의 데이터 출력을 의미
- 특징
자바에서는 3가지 표준 입출력 스트림을 제공
System.out
- 출력,System.err
- 출력,System.in
- 입력- 표준 입출력 스트림은 애플리케이션 실행과 동시에 사용할 수 있도록 자동으로 생성됨
- 표준 입출력 스트림의 타입은
InputStrem
과PrintStream
이지만, 실제로는BufferedInputStream
과BufferedOutputStream
의 인스턴스를 이용함
에디터의 콘솔 출력 콘솔로의 출력을 중간에 가로채서 에디터에 뿌려주는 것
- 콘솔입력은 버퍼를 가지고 있기 때문에 한번에 버퍼만큼 입력이 가능
표준입출력의 대상변경 - setOut(), setErr(), setIn()
- 의미: 입출력을 콘솔이 아닌 다른 입출력 대상으로 변경하는 것이 가능
커맨드라인에서의 변경
1 2
C:\jdk1.8\work> java StandardIOEx3 > output.txt # 파일에 출력, 내용이 있으면 덮어씀 C:\jdk1.8\work> java StandardIOEx3 >> output.txt # 파일에 출력, 내용이 있으면 추가
RandomAccessFile
- 의미: 하나의 클래스로 파일에 대한 입력과 출력을 모두 할 수있도록 되어있음
- 특징
- DataInput 인터페이스와 DataOutput인터페이스 모두 구현→ DataInputStream과 DataOutputStream과같이 기본자료형 단위로 데이터를 읽고 쓸 수 있음
- 파일의 어느 위치에서나 읽기/쓰기가 가능함(기존에는 순차적으로 읽기/쓰기 해야함)
- 내부적으로 파일 포인터 사용(기존에도 사용하지만 포인터의 위치를 옮길수 없었음)
메서드
File
- 의미: 파일과 디렉토리를 다룰 수 있는 클래스
- 특징
- OS 독립립적으로 프로그램을 작성하기 위해서는 관련 변수를 이용
- 유효하지 않더라도 컴파일 에러나 예외를 발생시키지 않음
메서드
- 경로
- 절대경로: 파일 시스템의 루트로부터 시작하는 파일의 전체경로를 의미
- 정규경로: 기호나 링크 등을 포함하지 않은 유일한 경로
직렬화(Serialization)
객체를 컴퓨터에 저장했다가 다음에 다시 꺼내 쓰는 방법이나 네트웍을 통해 컴퓨터간에 서로 객체를 주고 받을 수 없을까에 대한 해답을 제공
직렬화란?
- 의미
- 객체를 데이터 스트림으로 만드는 것
객체에 저장된 데이터를 스트림에 쓰기위해 연속적인 데이터로 변환하는 것
객체를 저장한다는 의미: 객체의 모든 인스턴스변수의 값을 저장한다는 것
인스턴스변수의 타입이 참조형이면, 참조하고 있는 내용을 모두 저장
객체의 구조 클래스변수나 메서드가 포함되지 않고, 오직 인스턴스 변수들로만 구성되어있음 인스턴스변수는 인스턴스마다 다른 값을 가질 수 있어야하지만, 메서드는 그렇지 않기 때문에 인스턴스마다 메서드의 내용을 포함시킬 이유는 없음
- 특징
- 직렬화/역직렬화를 할 수 있는
ObjectInputStream
과ObjectOutputStream
을 사용하는 방법만 알면됨
- 직렬화/역직렬화를 할 수 있는
ObjectInputStream과 ObjectOutputStream
- 의미
- 직렬화에는
ObjectInputStream
를 사용 - 역직렬화에는
ObjectOutputStream
를 사용 InputStream
과OutputStream
을 직접 상속받지만, 기반스트림을 필요로하는 보조스트림
- 직렬화에는
- 동작 방식
- 직렬화: 기반스트림 생성 → 보조스트림생성 → 객체 출력
역직렬화: 기반스트림 생성 → 보조스트림생성 → 읽기 → 형변환
Object타입으로 읽히기 때문에, 객체의 원래 타입으로 형변환 해야함
- 특징
기본적으로
readObject()
와writeObject()
를 사용해서 직렬화와 역직렬화를 함편하지만, 느리기 때문에 속도를 원한다면 오버라이딩 해야함
메서드
직렬화가 가능한 클래스 만들기 - Serializable, transient
Serializable
- 직렬화가 가능한 클래스를 만드는 방법 :
java.io.Serializable
를 구현하면됨java.io.Serializable
: 아무런 내용이 없는 빈 인터페이스, 직렬화를 고려한 클래스인지 판단하는 기준이 됨
- 특징
조상클래스가
java.io.Serializable
를 구현했다면, 자식 클래스도 자동으로 직렬화가 됨→ 때문에
Object
클래스는 구현하고 있지 않음- 조상클래스는 구현하지 않고, 자식클래스만 구현했다면 자식클래스에만 있는 인스턴스 변수만 직렬화의 대상이 됨
- 인스턴스 변수 타입이 아닌, 실제로 연결된 객체의 종류에 의해 직렬화의 여부가 결정됨
객채의 직렬화와 역직렬화는 순서가 일치 해야함
→ 각각의 객체를 직렬화 하는 것보다 직렬화 할 객체들을
ArrayList
와 같은 곳에 담아 이것만 직렬화를 하는 방법이 더 나을 수 있음
transient
- 의미: 직렬화의 대상에서 제외될수 있도록함
- 특징
- 실제로 그 값이 직렬화가 되지 않는 것이 아니라, 해당 자료형의 기본형으로 직렬화가 됨
직렬화가 가능한 클래스의 버전관리
의미: 클래스의 내용이 변경된 경우 역직렬화 실패, 예외 발생
→ 직렬화가 가능한 클래스의 버전 관리 가 필요함
serialVersionUID
- 클래스의 버전으 자동생성해 직렬화 내용에 포함
- 역직렬화시 클래스의 버전을 비교함으로써 직렬화할 때의 클래스 버전과 일치하는지 확인
- 클래스의 버전을 수동으로 관리할 때 사용함