Java에서 IO에 대한 부분을 알아보자.
우선 Java IO는 파일과 네트워크에 데이터를 읽고 쓸 수 있는 API를 제공하며, blocking으로 동작한다.
InputStream
우선 InputStream부터 살펴보자.
public abstract class InputStream implements Closeable{
public abstract int read() throws IOException;
public void close() throws IOException;
}
read는 stream으로 데이터를 읽고 읽은 값을 반환한다. 만약 -1을 반환한다면 파일의 끝인 EOF이다.
close는 stream을 닫고 더 이상 데이터를 읽지 않는다.
이 InputStream은 다양한 구현체가 존재한다.
파일을 읽어오는 FileInputStream, 바이트 배열을 읽어오는 ByteArrayInputStream, 별도의 버퍼를 만들어서 읽어오는 BufferedInputStream 등이 있다.
ByteArrayInputStream
byte array로부터 값을 읽을 수 있다.
@Slf4j
public class ByteArrayInputStreamAllExample {
public static void main(String[] args) throws IOException {
log.info("start");
var bytes = new byte[]{100, 101, 102, 103, 104, 105};
try(var byteArrayInputStream = new ByteArrayInputStream(bytes)){
var values = byteArrayInputStream.readAllBytes();
log.info("values: {}", values);
}
log.info("end");
}
}
해당 코드로 알아보도록 하자.

byteArray에서 ByteArrayInputStream을 통해 데이터를 모두 읽어오는 것을 볼 수 있다.
readAllBytes는 모두 읽어오는 메서드이고, read를 사용하면 하나씩 데이터를 가져오게 된다.
@Slf4j
public class ByteArrayInputStreamExample {
public static void main(String[] args) throws IOException {
log.info("start");
var bytes = new byte[]{100, 101, 102, 103, 104};
try(var byteArrayInputStream = new ByteArrayInputStream(bytes)){
var value = 0;
while((value = byteArrayInputStream.read()) != -1)
log.info("value: {}", value);
}
log.info("end");
}
}
read를 사용하면, 하나씩 읽어온다.

FileInputStream
file로부터 byte 단위로 값을 읽을 수 있다.
file을 읽어오는 동안에는 blocking이 일어난다.
@Slf4j
public class FileInputStreamExample {
public static void main(String[] args) throws IOException {
log.info("start main");
var file = new File("path");
try (var fis = new FileInputStream(file)) {
var value = 0;
while ((value = fis.read()) != -1) {
log.info("value: {}", (char)value);
}
}
log.info("end main");
}
}
이렇게 해당 파일을 읽어오게 된다.
읽을 때는 한 바이트 씩 읽어오게 된다.

BufferedInputStream
다른 inputStream과 조합해서 사용한다.
이름 그대로 읽어올 때마다, buffer의 사이즈만큼 미리 읽어오게 된다.
@Slf4j
public class BufferedInputStreamExample {
public static void main(String[] args) throws IOException {
var file = new File("path");
try(var fileInputStream = new FileInputStream(file)){
try(var bufferedInputStream = new BufferedInputStream(fileInputStream)){
var value = 0;
while((value = bufferedInputStream.read()) != -1){
log.info("value : {}", (char)value);
}
}
}
}
}
이렇게 항상 File에 접근하는 것이 아닌 buffer에 접근하여 데이터를 가져오는 것을 볼 수 있다.

SocketInputStream
SocketInputStream은 Public이 아니기 때문에 직접 접근이 불가능하다.
호출 시에 blocking이 발생한다.
간단한 서버를 만들어보았다.
@Slf4j
public class ServerSocketInputStreamExample {
@SneakyThrows
public static void main(String[] args) {
log.info("start");
ServerSocket serverSocket = new ServerSocket(8080);
Socket clientSocket = serverSocket.accept();
var inputStream = clientSocket.getInputStream();
var bufferedInputStream = new BufferedInputStream(inputStream);
byte[] buffer = new byte[1024];
int bytesRead = bufferedInputStream.read(buffer);
String inputLine = new String(buffer, 0, bytesRead);
log.info("bytes: {}", inputLine);
clientSocket.close();
serverSocket.close();
log.info("end");
}
}
서버와 클라이언트의 동작 방식은 네트워크 프로그래밍에서 배웠기 때문에 다루지 않도록 하겠다.
서버 소캣으로부터 inputStream을 만들어서 버퍼로 가져오는 코드이다.
하지만 현재는 클라이언트가 없어서 아마 아무 결과도 나오지 않을 것이다.
OutputStream
public abstract class OutputStream implements Closeable, Flushable{
public abstract void write(int b) throws IOException;
public void flush() throws IOException{}
public void close() throws IOException{}
}
inputStream과는 다르게 버퍼를 출력하고 비우는 Flush가 존재하는 것을 볼 수 있다.
당연히 OutputStream도 InputStream처럼 많은 구현체가 존재하는 것을 알 수 있다.
ByteArrayOutputStream
@Slf4j
public class ByteArrayOutputStreamExample {
public static void main(String[] args) throws IOException {
log.info("start");
try(var byteArrayOutputStream = new ByteArrayOutputStream()){
byteArrayOutputStream.write(100);
byteArrayOutputStream.write(101);
byteArrayOutputStream.write(102);
byteArrayOutputStream.write(103);
byteArrayOutputStream.write(104);
var bytes = byteArrayOutputStream.toByteArray();
log.info("bytes: {}", bytes);
}
log.info("end");
}
}
byteArrayOutputStream에 하나씩 작성하고 마지막에 byteArray로 변환하여 값을 출력한다.

ByteArrayOutputStream
file에 값을 쓰는 OutputStream이다.
해당 메서드도 blocking이 일어난다.
@Slf4j
public class FileOutputStreamExample {
public static void main(String[] args) throws IOException {
var file = new File("path");
try(var fileOutputStream = new FileOutputStream(file)){
var data = "Hi Seungkyu";
fileOutputStream.write(data.getBytes());
fileOutputStream.flush();
}
}
}
write 후에 flush까지 호출해 주도록 한다.

BufferedOutputStream
이것도 마찬가지로 다른 outputStream과 조합하여 사용한다.
write 호출하면 buffer에만 write 하고, 후에 Flush를 호출하여 한 번에 outputStream에 작성한다.
@Slf4j
public class BufferedOutputStreamExample {
public static void main(String[] args) throws IOException {
var file = new File("path");
var fileOutputStream = new FileOutputStream(file);
try(var bufferedOutputStream = new BufferedOutputStream(fileOutputStream)){
var data = "Hi seungkyu!!";
bufferedOutputStream.write(data.getBytes());
bufferedOutputStream.flush();
}
}
}
write 후에 작성을 위하여 flush를 사용해야 한다.

SocketOutputStream
SocketOutputStream도 Public이 아니기 때문에 직접 접근이 불가능하다.
호출 시에 blocking이 발생한다.
@Slf4j
public class ServerSocketOutputStreamExample {
@SneakyThrows
public static void main(String[] args) {
log.info("start");
ServerSocket serverSocket = new ServerSocket(8080);
Socket clientSocket = serverSocket.accept();
byte[] buffer = new byte[1024];
clientSocket.getInputStream().read(buffer);
var outputStream = clientSocket.getOutputStream();
var byteOutputStream = new BufferedOutputStream(outputStream);
var bytes = "Hi Seungkyu".getBytes();
byteOutputStream.write(bytes);
byteOutputStream.flush();
clientSocket.close();
serverSocket.close();
log.info("end");
}
}
이런 식으로 내용을 작성하게 된다.
Java IO Reader, Writer
Stream에서 character 단위로 읽고 쓸 수 있다.
문자 인코딩을 지원하여 바로 문자로 가져올 수 있다는 것이다.
blocking으로 동작하게 된다.
Reader
바로 인코딩을 지정하여 3바이트씩 가져오게 된다.
@Slf4j
public class FileReaderExample {
public static void main(String[] args) throws IOException {
var file = new File("path");
var charset = StandardCharsets.UTF_8;
try(var fileInputStream = new FileReader(file, charset)){
var value = 0;
while((value = fileInputStream.read()) != -1){
log.info("value: {}", (char)value);
}
}
}
}

Writer
@Slf4j
public class FileWriterExample {
public static void main(String[] args) throws IOException {
var file = new File("path");
var charset = StandardCharsets.UTF_8;
try(var fileInputStream = new FileWriter(file, charset)){
var data = "hihihihi";
fileInputStream.write(data);
}
}
}
바로 문자열을 write하게 된다.
'백엔드 > 리액티브 프로그래밍' 카테고리의 다른 글
Java IO Server 서버를 NIO로 변경 (0) | 2024.03.19 |
---|---|
Java NIO (0) | 2024.03.18 |
Mutiny (0) | 2024.03.14 |
RxJava Reactor (2) | 2024.03.14 |
Project reactor (0) | 2024.03.14 |