1. 자바에서의 입출력
1.1 입출력이란?
컴퓨터 내부 또는 외부의 장치와 프로그램간의 데이터를 주고받는 것
1.2 스트림(stream)
자바에서 입출력을 수행하기 위해 두 대상을 연결하고 데이터를 운반하는데 사용되는 연결통로
먼저 보낸 데이터를 먼저 받게 되어 있으며 중간에 건너뜀 없이 연속적으로 데이터를 주고 받는다
1.3 바이트기반 스트림 - InputStream, OutputStream
스트림은 바이트단위로 데이터를 전송하며 입출력 대상에 따라 다음과 같은 입출력스트림이 있다.
1.4 보조스트림
실제 데이터를 주고받는 스트림이 아니기 때문에 데이터를 입출력할 수 있는 기능은 없지만,
스트림의 기능을 향상시키거나 새로운 기능을 추가할 수 있다
// 먼저 기반스트림을 생성한다.
FileInputStream fis = new FileInpustStream("test.txt");
// 기반스트림을 이용해서 보조스트림을 생성한다.
BufferedInputStream bis = new BufferedInputStream(fis);
bis.read(); // 보조스트림인 BufferedInputStream으로부터 데이터를 읽는다.
입력기능은 BufferedInputStream과 연결된 FileInputStream이 수행하고, 보조 스트립인 BufferedInputStream은 버퍼만을 제공한다.
버퍼를 사용한 입출력과 사용하지 않은 입출력간의 성능차이 때문에 대부분의 경우에 버퍼를 이용한 보조스트림을 사용한다.
1.5 문자기반 스트림 - Reader, Writer
Java에서는 한 문자를 의미하는 char형이 1byte가 아니라 2byte이기 때문에
바이트기반의 스트림으로 2byte인 문자를 처리하는 데는 어려움이 있다.
이 점을 보완하기 위해서 문자기반의 스트림이 제공된다.
2. 바이트기반 스트림
2.1 InputStream과 OutputStream
스트림의 종류에 따라서 mark()와 reset()을 사용하여 이미 읽은 데이터를 되돌려서 다시 읽을 수 있다.
이 기능을 지원하는 스트림인지 확인하는 markSupported()를 통해서 알 수 있다.
flush()는 버퍼가 있는 출력스트림의 경우에만 의미가 있으며, OutputSteream에 정의된 flush()는 아무런 일도 하지 않는다.
프로그램이 종료될 때, 사용하고 닫지 않은 스트림을 JVM이 자동적으로 닫아 주기는 하지만,
스트림을 사용해서 모든 작업을 마치고 난 후에는 close()를 호출해서 반드시 닫아주어야 한다.
그러나 ByteArrayInputStream과 같이 메모리를 사용하는 스트림과
System.in, System.out과 같은 표준 입출력 스트림은 닫아 주지 않아도 된다.
2.2 ByteArrayInputStream과 ByteArrayOutputStream
메모리, 즉 바이트배열에 데이터를 입출력 하는데 사용되는 스트림이다.
2.3 FileInputStream과 FileOutputStream
파일에 입출력을 하기 위한 스트림
3. 바이트기반의 보조스트림
3.1 FilterInputStream과 FilterOutputStream
생성자 FilterInputStream(InputStream in)는 접근 제어자가 protected이기 때문에
FilterInputStream의 인스턴스를 생성해서 사용할 수 없고 상속을 통해서 오버라이딩되어야 한다.
FilterInputStrem/FilterOutputStream을 상속받아서 기반스트림에 보조기능을 추가한 보조스트림 클래스는 다음과 같다.
FilterInputStream의 자손 - BufferedInputStream, DataInputStream, PushbackInputStream 등
FilterOutputStream의 자손 - BufferedInputStream, DataOutputStream, PrintStream 등
3.2 BufferedInputStream과 BufferedOutputStream
스트림의 입출력 효율을 높이기 위해 버퍼를 사용하는 보조스트림으로 한 바이트씩 입출력하는 것보다는 버퍼(바이트 배열)를 이용해서 한 번에 여러 바이트를 입출력하는 것이 빠르기 때문에 대부분의 입출력 작업에 사용된다.
BufferedInputStream은 입력소스로 부터 버퍼 크기만큼의 데이터를 읽어다 자신의 내부 버퍼에 저장하고 프로그램에서는 버퍼에 저장된 데이터를 읽으며, 다 읽고 그 다음 데이터를 읽기 위해 read메서드가 호출되면, BufferedInputStream은 입력소스로부터 다시 버퍼크기 만큼의 데이터를 읽어다 버퍼에 저장하는 작업을 반복한다.
BufferedOutputStream 역시 버퍼를 이용해서 출력소스와 작업을 하게 되는데 프로그램에서 write메서드를 이용한 출력이 BufferedOutputStream의 버퍼에 저장된다.
버퍼가 가득 차면, 그 때 버퍼의 모든 내용을 출력소스에 출력한다.
버퍼가 가득 찼을 때만 출력소스에 출력을 하기 때문에, 마지막 출력부분이 출력소스에 쓰이지 못하고 BufferedOutputStream의 버퍼에 남아있는 채로 프로그램이 종료될 수 있다는 점을 주의해야 한다.
그래서 프로그램에서 모든 출력작업을 마친 후 BufferedOutputStream에 close()나 flush()를 호출해서 마지막에 버퍼에 있는 모든 내용이 출력소스에 출력되도록 해야 한다.
3.3 DataInputStream과 DataOutputStream
데이터를 읽고 쓰는데 있어서 byte단위가 아닌, 8가지 기본 자료형의 단위로 읽고 쓰기 가능
3.4 SequenceInputStream
여러 개의 입력스트림을 연속적으로 연결해서 하나의 스트림으로부터 데이터를 읽는 것과 같이 처리할 수 있도록 도와준다.
3.5 PrintStream
데이터를 기반스트림에 다양한 형태로 출력할 수 있는 print, println, printf와 같은 메서드를 오버로딩하여 제공
4. 문자기반 스트림
4.1 Reader와 Writer
Char 배열을 사용한 문자기반의 스트림 제공. Text의 읽기/쓰기에 특화되어있다.
4.2 FileReader와 FileWriter
File로부터 Text 데이터를 읽고 쓰는데 사용된다.
4.3 PipedReader와 PipedWriter
주로 쓰레드 간에 데이터를 주고 받을 때 사용된다.
4.4 StringReader와 StringWriter
StringReader/Writer는 입출력 대상이 메모리인 스트림이다. 내부의 StringBuffer에 저장된다.
StringReader는 문자열 데이터를 Reader 형태로 변환해준다.
문자열 데이터를 오직 Reader를 기반으로 접근하게 변환이 필요할 경우 유용한다.
5. 문자기반의 보조스트림
5.1 BufferedReader와 BufferedWriter
BufferedReader/BufferedWriter 버퍼를 이용해서 입출력의 효율을 높일 수 있게 해주는 역할을 한다.
Buffer Size : bufferedReader의 버퍼사이즈를 정할 수 있다.
5.2 InputStreamReader와 OutputStreamWriter
Byte 기반 스트림을 문자기반 스트림으로 연결시켜주는 역할을 한다.
주로 파일이나 네트워트 연결로부터 Byte로 제공되는 Text를 문자기반(char)로 읽고 쓰기를 할 경우에 많이 쓰인다.
Read() : char 값의 정수를 리턴한다.
6. 표준입출력과 File
6.1 표준입출력 - System.in, System.out, System.err
위의 세 스트림은 JVM이 시작할때 초기화가 되므로 직접 초기화를 할 필요가 없다.
System.in System.in 은 콘솔의 키보드 인풋에 연결된 inputStream이다.
System.out System.out은 PrintStream이다. 콘솔로 데이터를 내보는 역할을 한다.
System.err System.err PrintStream이다. System.out과 비슷하지만 일반적으로 에러메세지를 내보낼때 사용된다.
System.out과 System.err 사용비교
try {
InputStream input = new FileInputStream("test.txt");
System.out.println("File opened...");
} catch (IOException e){
System.err.println("File opening failed:");
e.printStackTrace();
}
6.2 표준입출력의 대상변경 - setOut(), setErr(), setIn()
InputStream으로 System.in, OutputStream으로 System.out, System.err의 읽기/쓰기를 새로우 스트림으로 변경할 수 있다.
System Stream 설정
OutputStream output = new FileOutputStream("system.out.txt");
PrintStream printOut = new PrintStream(output);
System.setOut(printOut);
6.3 RandomAccessFile
입력과 출력을 하나의 클래스로 파일에 대한 입력/출력을 모두 할 수 있게 설계된 클래스.
가장 큰 장점은 파일의 어느 위치에나 읽기/쓰기가 가능하다는 점이다.
//RandomAccessFile 생성,
//생성자의 두번째 값 'rw'는 RandomAccessFile의 mode 값이다. rw는 Read/Write를 뜻한다.
RandomAccessFile file = new RandomAccessFile("file.txt", "rw");
//RandomAccessFile 이동
RandomAccessFile file = new RandomAccessFile("file.txt", "rw");
//단위 : Byte
file.seek(200);
long pointer = file.getFilePointer();
file.close();
//RandomAccessFile 읽기/쓰기
//읽기 사용법
RandomAccessFile file = new RandomAccessFile("file.txt", "rw");
int aByte = file.read();
file.close();
//쓰기 사용법
RandomAccessFile file = new RandomAccessFile("file.txt", "rw");
file.write("Hello World".getBytes());
file.close();
6.4 File
File클래스는 파일과 파일의 메타데이터의 접근하는 기능만 제공한다.
만약 파일의 내용을 읽기/쓰기 기능을 원한다면 file Stream을 이용해야한다.
만약 NIO를 사용한다면 파일에 대한 접근은 java.nio.FileChannel를 사용해야한다.
//File 초기화
File file = new File("input-file.txt");
//존재여부 확인 (객체 생성시점에 실제 파일이 존재하지 않아도 예외가 발생되지 않는다)
boolean fileExists = file.exists();
//디렉토리 생성
//mkdir() / mkdirs()
File file = new File("c:\\users\\javastudy\\newdir");
boolean dirCreated = file.mkdir();
//파일 길이
long length = file.length();
//Rename
boolean success = file.renameTo(new File("c:\\data\\new-file.txt"));
//파일삭제
boolean success = file.delete();
//디렉토리확인
boolean isDirectory = file.isDirectory();
//파일리스트
File file = new File("c:\\data");
String[] fileNames = file.list();
File[] files = file.listFiles();
7. 직렬화(Serialization)
7.1 직렬화란?
직렬화란 객체를 데이터 스트림으로 만드는 것을 뜻한다.
객체에 저장된 데이터를 스트림에 쓰기위해 연속적인(serial) 데이터로 변환하는 것을 말한다.
7.2 ObjectInputStream과 ObjectOutputStream
ObjectInputStream/ObjectOutputStream은 raw 한 byte를 읽는 대신,
InputStream/OutputStream을 통해 자바 오브젝트를 읽을 수 있도록 해준다.
7.3 직렬화가 가능한 클래스 만들기 - Serializable, transient
직렬화가 가능한 클래스를 만드는 방법은 Serializable 인터페이스를 원하는 클래스에 구현(선언)하면 된다.
password와 같은 직렬화하지 않을 멤버변수 앞에 transient 를 붙이면 된다.
7.4 직렬화가능한 클래스의 버전관리
serialVersionUID
- 직렬화된 객체를 역직렬화할 떄는 직렬화 했을 떄와 같은 클래스를 사용해야한다.
- 하지만 클래스의 내용이 변경된 경우 역직렬화에 실패한다.
static 변수나 transient가 붙은 인스턴스변수는 직렬화에 영향을 미치지 않는다.
'Programming > Java \ Spring' 카테고리의 다른 글
🔥자바스터디🔥 자바의 정석 CH16 네트워킹 (0) | 2021.08.01 |
---|---|
🔥자바스터디🔥 자바의 정석 CH14 람다와 스트림 (0) | 2021.07.18 |
🔥자바스터디🔥 자바의 정석 CH13 쓰레드 (0) | 2021.07.11 |
🔥자바스터디🔥 자바의 정석 CH12 지네릭스, 열거형, 어노테이션 (0) | 2021.07.04 |
🔥자바스터디🔥 자바의 정석 CH11 컬렉션프레임웍 (0) | 2021.06.27 |