자바 프로세스가 가지고 있는 데이터를 밖으로 보내려면 출력 스트림을 사용하면 되고, 반대로 외부 데이터를 자바 프로세스 안으로 가져오려면 입력 스트림을 사용하면 된다.
입력한 순서대로 잘 출력되는 것을 확인할 수 있다. 마지막은 파일의 끝에 도달해서 -1이 출력된다
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("temp/hello.dat");
fos.write(65);
fos.write(66);
fos.write(67);
fos.close();
FileInputStream fis = new FileInputStream("temp/hello.dat");
System.out.println(fis.read());
System.out.println(fis.read());
System.out.println(fis.read());
System.out.println(fis.read());
fis.close();
}
===
// 원하는 크기 만큼
public class StreamStartMain3 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("temp/hello.dat");
byte[] input = {65, 66, 67};
fos.write(input);
fos.close();
FileInputStream fis = new FileInputStream("temp/hello.dat");
byte[] buffer = new byte[10];
int readCount = fis.read(buffer, 0, 10); // offset, len
// byte[] readBytes = fis.readAllBytes();
System.out.println("readCount = " + readCount);
System.out.println(Arrays.toString(buffer));
fis.close();
}
}
// 한번에 읽기
public class StreamStartMain4 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("temp/hello.dat");
byte[] input = {65, 66, 67};
fos.write(input);
fos.close();
FileInputStream fis = new FileInputStream("temp/hello.dat");
byte[] readBytes = fis.readAllBytes();
System.out.println(Arrays.toString(readBytes));
fis.close();
}
}
메모리와 콘솔에 사용하는 스트림을 사용해보자
이 클래스들은 OutputStream , InputStream 을 상속받았기 때문에 부모의 기능을 모두 사용할 수 있다.
// 메모리 스트림
public class ByteArrayStreamMain {
public static void main(String[] args) throws IOException {
byte[] input = {1, 2, 3};
// 메모리에 쓰기
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(input);
// 메모리에서 읽기
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
byte[] bytes = bais.readAllBytes();
System.out.println(Arrays.toString(bytes));
}
}
// 콘솔 스트림
public class PrintStreamMain {
public static void main(String[] args) throws IOException {
PrintStream printStream = System.out;
byte[] bytes = "Hello!\\n".getBytes(UTF_8);
printStream.write(bytes);
printStream.println("Print!");
}
}
BufferedOutputStream 과 같이 단독으로 사용할 수 없고, 보조 기능을 제공하는 스트림을 보조 스트림이라 한다.
일반적인 상황이라면 이 정도 성능은 크게 문제가 되지는 않기 때문에 싱글 스레드여도 BufferedXxx 를 사용하면 충분하다.
물론 매우 큰 데이터를 다루어야 하고, 성능 최적화가 중요하다면 예제 2와 같이 직접 버퍼를 다루는 방법을 고려하자.
public class CreateFileV2 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream(FILE_NAME);
long startTime = System.currentTimeMillis();
byte[] buffer = new byte[BUFFER_SIZE];
int bufferIndex = 0;
for (int i = 0; i < FILE_SIZE; i++) {
buffer[bufferIndex++] = 1;
// 버퍼가 가득 차면 쓰고, 버퍼를 비운다.
if (bufferIndex == BUFFER_SIZE) {
fos.write(buffer);
bufferIndex = 0;
}
}
// 끝 부분에 오면 버퍼가 가득차지 않고, 남아있을 수 다. 버퍼에 남은 부분 쓰기
if (bufferIndex > 0) {
fos.write(buffer, 0, bufferIndex);
}
fos.close();
long endTime = System.currentTimeMillis();
System.out.println("File created: " + FILE_NAME);
System.out.println("File size: " + FILE_SIZE / 1024 / 1024 + "MB");
System.out.println("Time taken: " + (endTime - startTime) + "ms");
}
...
byte[] buffer = new byte[BUFFER_SIZE];
int fileSize = 0;
int size;
while ((size = fis.read(buffer)) != -1) {
fileSize += size;
}
fis.close();
}
===
public class CreateFileV3 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream(FILE_NAME);
BufferedOutputStream bos = new BufferedOutputStream(fos, BUFFER_SIZE);
long startTime = System.currentTimeMillis();
for (int i = 0; i < FILE_SIZE; i++) {
bos.write(1);
}
bos.close();
long endTime = System.currentTimeMillis();
System.out.println("File created: " + FILE_NAME);
System.out.println("File size: " + FILE_SIZE / 1024 / 1024 + "MB");
System.out.println("Time taken: " + (endTime - startTime) + "ms");
}
}
public class ReadFileV3 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream(FILE_NAME);
BufferedInputStream bis = new BufferedInputStream(fis, BUFFER_SIZE);
long startTime = System.currentTimeMillis();
int fileSize = 0;
int data;
while ((data = bis.read()) != -1) {
fileSize++;
}
bis.close();
long endTime = System.currentTimeMillis();
System.out.println("File name: " + FILE_NAME);
System.out.println("File size: " + fileSize / 1024 / 1024 + "MB");
System.out.println("Time taken: " + (endTime - startTime) + "ms");
}
}
===
// 파일의 크기가 크지 않다면 한번에 쓰기
public class CreateFileV4 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream(FILE_NAME);
long startTime = System.currentTimeMillis();
byte[] buffer = new byte[FILE_SIZE];
for (int i = 0; i < FILE_SIZE; i++) {
buffer[i] = 1;
}
fos.write(buffer);
fos.close();
long endTime = System.currentTimeMillis();
System.out.println("File created: " + FILE_NAME);
System.out.println("File size: " + FILE_SIZE / 1024 / 1024 + "MB");
System.out.println("Time taken: " + (endTime - startTime) + "ms");
}
}
public class ReadFileV4 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream(FILE_NAME);
long startTime = System.currentTimeMillis();
byte[] bytes = fis.readAllBytes();
fis.close();
long endTime = System.currentTimeMillis();
System.out.println("File name: " + FILE_NAME);
System.out.println("File size: " + bytes.length / 1024 / 1024 + "MB");
System.out.println("Time taken: " + (endTime - startTime) + "ms");
}
}
// String 문자를 스트림을 통한 파일 저장
// String + 문자 집합 byte[]
// byte[] + 문자 집합 String
public static void main(String[] args) throws IOException {
String writeString = "ABC";
// 문자 - byte UTF-8 인코딩
byte[] writeBytes = writeString.getBytes(UTF_8);
System.out.println("write String: " + writeString);
System.out.println("write bytes: " + Arrays.toString(writeBytes));
// 파일에 쓰기
FileOutputStream fos = new FileOutputStream(FILE_NAME);
fos.write(writeBytes);
fos.close();
// 파일에서 읽기
FileInputStream fis = new FileInputStream(FILE_NAME);
byte[] readBytes = fis.readAllBytes();
fis.close();
// byte -> String UTF-8 디코딩
String readString = new String(readBytes, UTF_8);
System.out.println("read bytes: " + Arrays.toString(readBytes));
System.out.println("read String: " + readString);
}
}
===
// 스트렘에 byte 대신에 문자를 저장
public class ReaderWriterMainV2 {
public static void main(String[] args) throws IOException {
String writeString = "abc";
System.out.println("write String: " + writeString);
// 파일에 쓰기
FileOutputStream fos = new FileOutputStream(FILE_NAME);
OutputStreamWriter osw = new OutputStreamWriter(fos, UTF_8);
osw.write(writeString);
osw.close();
// 파일에서 읽기
FileInputStream fis = new FileInputStream(FILE_NAME);
InputStreamReader isr = new InputStreamReader(fis, UTF_8);
StringBuilder content = new StringBuilder();
int ch;
while ((ch = isr.read()) != -1) {
content.append((char) ch);
}
isr.close();
System.out.println("read String: " + content);
}
}
자바는 byte를 다루는 I/O 클래스와 문자를 다루는 I/O 클래스를 둘로 나누어두었다.
byte를 다루는 클래스는 OutputStream , InputStream 의 자식이다
문자를 다루는 클래스는 Writer , Reader 의 자식이다.