DataInputStream, DataOutputStream
DataInputStream과 DataOutputStream은 기본형 타입과 문자열을 읽고 쓸 수 있는 객체이다.
다음은 다양한 타입을 파일에 저장하고, 읽어들이는 코드이다.
package theory.io;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
public class DataInputExam {
public static void main(String[] args) throws Exception {
// /tmp/score.dat 파일에 저장한다.
String name = "kim";
int kor = 90;
int eng = 60;
int math = 70;
double total = kor + eng + math;
double avg = total / 3.0;
// 다양한 타입을 저장할 때 사용하는 객체 -> DataOutputStream
DataOutputStream out = new DataOutputStream(new FileOutputStream("/tmp/score.dat"));
out.writeUTF(name);
out.writeInt(kor);
out.writeInt(eng);
out.writeInt(math);
out.writeDouble(total);
out.writeDouble(avg);
out.close();
}
}
package theory.io;
import java.io.DataInputStream;
import java.io.FileInputStream;
public class DataInputExam {
public static void main(String[] args) throws Exception {
DataInputStream in = new DataInputStream(new FileInputStream("/tmp/score.dat"));
String name = in.readUTF();
int kor = in.readInt();
int eng = in.readInt();
int math = in.readInt();
double total = in.readDouble();
double avg = in.readDouble();
in.close();
System.out.println("name = " + name);
System.out.println("kor = " + kor);
System.out.println("eng = " + eng);
System.out.println("math = " + math);
System.out.println("total = " + total);
System.out.println("avg = " + avg);
}
}
ByteArrayInputStream, ByteArrayOutputStream
byte[]에 데이터를 읽고 쓰는 클래스이다.
ByteArrayOutputStream에 데이터를 저장하면 메모리 상에 저장된다. 그리고 toByteArray를 호출하면 메모리 상에 저장된 바이트 배열을 반환해준다.
package theory.io;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class ByteArrayOutputExam {
public static void main(String[] args) throws IOException {
int data1 = 1;
int data2 = 2;
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(data1);
out.write(data2);
out.close();
byte[] array = out.toByteArray();
System.out.println("array.length = " + array.length);
System.out.println("array[0] = " + array[0]);
System.out.println("array[1] = " + array[1]);
}
}
package theory.io;
import java.io.ByteArrayInputStream;
public class ByteArrayInputExam {
public static void main(String[] args) throws Exception {
byte[] array = new byte[2];
array[0] = (byte) 1;
array[1] = (byte) 2;
ByteArrayInputStream in = new ByteArrayInputStream(array);
int read1 = in.read(); // 첫번째 바이트 값
int read2 = in.read(); // 두번째 바이트 값
int read3 = in.read(); // EOF
in.close();
}
}
즉, 간혹가다가 메모리 상에 무언가를 써주고 싶을때가 있는데 그때는 ByteArrayOutputStream을 사용하면 된다.
CharArrayReader, CharArrayWriter
char[]에 데이터를 읽고 쓴다.
StringReader, StringWriter
문자열을 읽고 쓴다.
// 생성자 아무것도 안 받아들이는 IO 객체는 메모리에 쓴다.
StringWriter out = new StringWriter();
out.write("hello");
out.write("world");
out.close();
String str = out.toString();
System.out.println("str = " + str);
결과
str = helloworld
StringReader in = new StringReader("helloworld");
int ch = -1;
while ((ch = in.read()) != -1) {
System.out.print((char) ch);
}
in.close();
ObjectInputStream, ObjectOutputStream
아무 객체나 읽고 쓸 수 있는 것이 아니라 직렬화 가능한 객체만 읽고 쓸 수 있다. 직렬화 가능한 대상은 기본형 타입 혹은 java.io.Serializable 인터페이스를 구현하고 있는 객체이다. 이때, Serializable 인터페이스는 구현할 메서드가 없는 mark Interface라고 한다.
다음 코드는 파일을 통한 직렬화, 역직렬화 예제이다.
User user = new User("hong@example.com", "홍길동", 2020);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("/tmp/user.dat"));
out.writeObject(user); // user를 객체 직렬화시켜서 파일에 저장
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream("/tmp/user.dat"));
User user = (User) in.readObject(); // 파일에 있는 데이터를 읽어들여서 역직렬화
in.close();
System.out.println("user = " + user);
그리고 다음은 ArrayList 자체도 직렬화 대상임을 보여주는 예제이다.
User user1 = new User("hong@example.com", "홍길동", 2020);
User user2 = new User("kim@example.com", "김철수", 1990);
User user3 = new User("lee@example.com", "이영희", 2000);
ArrayList<User> list = new ArrayList<>(); // ArrayList 자체도 직렬화 가능
list.add(user1);
list.add(user2);
list.add(user3);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("/tmp/userlist.dat"));
out.writeObject(list); // user를 객체 직렬화시켜서 파일에 저장
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream("/tmp/userlist.dat"));
ArrayList<User> list = (ArrayList) in.readObject(); // 파일에 있는 데이터를 읽어들여서 역직렬화
in.close();
for (User user : list) {
System.out.println("user = " + user);
}
user = User{email='hong@example.com', name='홍길동', birthYear=2020}
user = User{email='kim@example.com', name='김철수', birthYear=1990}
user = User{email='lee@example.com', name='이영희', birthYear=2000}
얕은 복사를 피하기위한 객체 직렬화
아래 코드를 보면 list를 writeObject 객체 직렬화 메서드에 넣어 직렬화시켰다. 그러면 list가 참조하고 있는 직렬화가능한 모든 것이 byte의 흐름으로 변해서 저장이 된다. 즉, 메모리상의 byte[]로 형태로 바뀐 것이다. 그리고 readObject 하는 순간 바이트배열에 있는 내용이 인스턴스가 새로 만들어지고, list2가 참조하게 된다.
따라서 메모리에 상에 그 내용이 그대로 복사된 내용이 만들어지고, 이를 깊은 복사라고 한다. 자바에서 이 기술을 이용해서 어떤 객체를 임시적으로 디스크에 저장 후 읽어들이거나, 어떤 객체들을 메모리 상에 복제를 한다던지가 가능해진다.
package theory.serialization;
import java.io.*;
import java.util.ArrayList;
public class ObjectIOExam {
public static void main(String[] args) throws Exception {
User user1 = new User("hong@example.com", "홍길동", 2020);
User user2 = new User("kim@example.com", "김철수", 1990);
User user3 = new User("lee@example.com", "이영희", 2000);
ArrayList<User> list = new ArrayList<>(); // ArrayList 자체도 직렬화 가능
list.add(user1);
list.add(user2);
list.add(user3);
// 기존 list를 받아들여서 ArrayList로 copy하는 메서드
ArrayList<User> list2 = copy(list);
for (User user : list2) {
System.out.println("user = " + user);
}
}
private static ArrayList<User> copy(ArrayList<User> list) throws IOException, ClassNotFoundException {
// Object output stream 에 쓴 것이 바이트 배열 메모리에 저장된다.
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bout);
out.writeObject(list);
out.close();
bout.close();
byte[] array = bout.toByteArray(); // list가 직렬화되어서 byte 배열이 됨
// 역직렬화
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(array));
ArrayList<User> list2 = (ArrayList) in.readObject();
in.close();
return list2;
}
}
인용
'Language > Java' 카테고리의 다른 글
싱글쓰레드와 멀티쓰레드, 싱글코어와 멀티코어 (0) | 2023.09.21 |
---|---|
Context Switching, Thread (0) | 2023.09.20 |
I/O - Decorator Pattern (0) | 2023.09.18 |
Java I/O - IO Stream (0) | 2023.09.18 |
I/O - File 클래스 (0) | 2023.09.18 |