코딩 테스트 연습할 때를 제외하면 Java의 스트림을 이용하여 직접 입출력 코드를 작성하는 경우가 드물었던 것 같습니다. 그런만큼 Java의 스트림을 통한 데이터 입출력에 대해 리마인드하고 부족했던 부분이 있었다면 채워넣을 겸 정리하는 시간을 가져보려 합니다.
입출력 스트림
자바에서 데이터 입출력은 스트림을 통해서 이루어집니다. 스트림(stream)은 단방향으로 데이터가 흐르며 키보드, 파일, 프로그램 등 다양한 출발지로부터 입력되고 모니터, 파일, 프로그램 등 다양한 도착지로 출력됩니다.
프로그램을 기준으로 데이터가 들어오는 것을 입력 스트림(InputStream), 데이터가 나가는 것을 출력 스트림(OutputStream)이라 합니다. 프로그램이 다른 프로그램과 데이터를 교환하기 위해서는 입력, 출력 스트림이 모두 필요합니다.
자바는 입출력과 관련된 라이브러리를 java.io
패키지에서 제공하고 있습니다. 입출력되는 데이터에 따라 바이트 스트림과 문자 스트림으로 구분할 수 있습니다.
바이트 스트림
바이트 스트림은 그림, 멀티미디어, 문자 등 모든 종류의 데이터를 입출력할 때 사용하는 스트림입니다. 최상위 클래스로 InputStream
과 OutpuStream
이 있으며 이를 상속하는 자식 클래스는 접미사로 InputStream
, OutputStream
이 붙습니다.
바이트 출력 스트림
write(int b)
write(int b)
메서드는 인자로 전달된 int(4byte)에서 끝 1byte만 출력합니다.
OutputStream output = new FileOutputStream("출력 경로");
byte a = 10;
output.write(a);
output.flush();
output.close();
출력 스트림은 내부에 작은 버퍼(buffer)를 가지고 있습니다. write()
메서드가 호출되면 버퍼에 바이트를 저장하고, 버퍼가 차면 순서대로 바이트를 출력합니다. flush()
메서드는 내부 버퍼에 잔류하고 있는 모든 바이트를 출력하고 버퍼를 비우는 역할을 합니다.
write(byte[] b)
write(byte[] b)
메서드는 인자로 주어진 배열의 모든 배열을 출력합니다.
OutputStream output = new FileOutputStream("출력 경로");
byte[] b = {10, 20, 30};
output.write(b);
output.flush();
output.close();
바이트 입력 스트림
read()
read()
메서드는 입력 스트림으로부터 1byte를 읽고 int(4byte) 타입으로 리턴합니다. 리턴된 4byte 중 끝 1byte에만 데이터가 들어 있습니다.
FileInputStream fileInputStream = new FileInputStream("input.txt");
while (true) {
int data = fileInputStream.read();
if(data == -1) {
break;
}
System.out.println(data);
}
fileInputStream.close();
read(byte[] b)
read(byte[] b)
메서드는 입력 스트림으로부터 주어진 배열의 길이만큼 바이트를 읽고 배열에 저장한 다음 읽은 바이트 수를 리턴합니다.
FileInputStream fileInputStream = new FileInputStream("output.txt");
while (true) {
byte[] data = new byte[100];
int read = fileInputStream.read(data);
if(read == -1) {
break;
}
System.out.println(new String(data));
}
fileInputStream.close();
문자 스트림
문자 스트림은 입출력되는 단위가 문자인 것을 제외하고는 바이트 입출력 스트림과 사용 방법이 동일합니다. 최상위 클래스로 Reader
, Writer
가 있으며 이를 상속하는 클래스는 Reader
, Writer
를 접미사로 가집니다.
문자 출력 스트림
write(String str)
write(String str)
메서드는 인수로 주어진 문자열을 출력합니다.
Writer writer = new FileWriter("input.txt");
writer.write("asdf")
writer.flush();
writer.close();
문자 입력 스트림
read()
read()
메서드는 1개의 문자(2byte)를 읽고 int(4byte) 타입으로 리턴합니다.
FileReader reader = new FileReader("output.txt");
while (true) {
int data = reader.read();
if(data == -1) {
break;
}
System.out.println((char)data);
}
reader.close();
read(char[] cbuf)
read(char[] cbuf)
메서드는 읽은 문자들은 문자 배열에 저장하고 읽은 문자 수를 리턴합니다.
FileReader reader = new FileReader("output.txt");
while (true) {
char[] data = new char[100];
int read = reader.read(data);
if(read == -1) {
break;
}
System.out.println(data);
}
reader.close();
보조 스트림
보조 스트림은 다른 스트림과 연결하여 여러 편리한 기능을 제공해주는 스트림을 말합니다. 보조 스트림은 그 자체만으로는 입출력을 수행할 수 없으며, 생성 시 입출력 스트림을 전달하여 보조 스트림과 연결합니다.
보조스트림 reader = new 보조스트림(입출력스트림);
문자 변환 스트림
문자 스트림은 문자로 바로 입출력하는 편리함과 문자셋의 종류를 지정할 수 있습니다. 때문에 바이트 스트림에서 입출력할 데이터가 문자라면 보조 스트림을 이용하여 문자 스트림으로 변환해서 사용할 수 있습니다.
InputStream is = System.in;
InputStreamReader isr = new InputStreamReader(is);
성능 향상 스트림
입출력에 의해 저하되는 프로그램의 성능을 향상시키기 위한 것이 메모리 버퍼(buffer)입니다.
출력 스트림의 경우 직접 하드 디스크에 데이터를 보내지 않고 버퍼에 데이터를 보내 출력 속도를 향상시킬 수 있습니다. 버퍼가 가득 차면 한꺼번에 하드 디스크로 보내 출력 횟수를 줄입니다. 입력 스트림 역시 하드 디스크로부터 직접 읽기보다 버퍼로부터 읽는 것이 더 빠릅니다.
이와 같이 메모리 버퍼를 통해 프로그램의 성능을 향상시키는 보조 스트림이 BufferedInput/OutputStream
, BufferedReader/Writer
입니다.
BufferedInputStream bis = new BufferedInputStream(바이트 입력 스트림);
BufferedOutputStream bos = new BufferedOutputStream(바이트 출력 스트림);
BufferedReader br = new BufferedReader(문자 입력 스트림);
BufferedWriter bw = new BufferedWriter(문자 출력 스트림)
특히 BufferedReader
의 경우 행 단위로 문자열을 읽을 수 있는 readLine()
메서드를 제공해 편리하게 입력을 읽어들일 수 있습니다. 이와 같은 이유에서 코딩 테스트 연습 시 성능 향상 스트림인 BufferedReader
를 다음과 같은 방식으로 사용하곤 합니다.
// 성능 향상 스트림, 문자 변환 스트림, 표준 입력 스트림
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str = br.readLine();
기본 타입 스트림
바이트 스트림에 DataInputStream
, DataOutputStream
을 연결하면 기본형으로 값을 입출력할 수 있습니다.
DataInputStream dis = new DataInputStream(바이트 입력 스트림);
boolean b = dis.readBoolean();
int i = dis.readInt();
프린트 스트림
PrintStream
, PrintWriter
는 프린터와 유사하게 출력하는 보조 스트림입니다. 콘솔에 메시지를 출력하기 위해 자주 사용하는 System.out
이 PrintStream
타입입니다.
PrintStream ps = System.out;
ps.println("hello");
객체 스트림
자바는 메모리에 생성된 객체를 파일 또는 네트워크로 출력할 수 있습니다. 이 때, 객체를 출력하기 위해 필드값을 일렬로 늘어선 바이트로 변경하는 것을 직렬화(serialization), 직렬화된 바이트를 객체의 필드값으로 변경하는 것을 역직렬화(deserialization)라고 합니다.
객체 출력 스트림인 ObjectOutputStream
은 직렬화를 위해 writeObject(Object obj)
를 사용합니다. 객체 입력 스트림인 ObjectInputStream
은 역직렬화를 위해 readObject()
를 사용합니다.
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.txt"));
Member member = new Member("member", 20);
oos.writeObject(member);
oos.flush();
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test.txt"));
Member readMember = (Member) ois.readObject();
ois.close();
Serializable
자바는 Serializable
인터페이스를 구현한 클래스만 직렬화할 수 있도록 제한합니다. 객체가 직렬화될 때 인스턴스 변수가 직렬화 대상이 되며 정적 변수나 transient
로 선언된 변수는 직렬화 대상에서 제외됩니다.
public class Member implements Serializable {
private String name;
private transient int age;
private static int count;
}
기본적으로 직렬화할 때 사용한 클래스와 역직렬화할 때 사용하는 클래스는 동일한 클래스여야 합니다. 단, 클래스가 가지고 있는 필드가 다르더라도 직렬화된 필드를 포함하고 있다면, 두 클래스가 동일한 serialVersionUID
를 가지고 있을 때는 역직렬화 할 수 있습니다.
public class Member implements Serializable {
@Serial
private static final long serialVersionUID = 1;
private String name;
private int age;
public Member(String name, int age) {
this.name = name;
this.age = age;
}
}
참고
이것이 자바다
'Java' 카테고리의 다른 글
[오브젝트] 객체지향 프로그래밍 (0) | 2023.07.26 |
---|---|
함수형 인터페이스와 람다 표현식 (0) | 2023.06.18 |
문자열 (0) | 2023.04.20 |