Object Stream
객체 입출력을 위한 스트림이다. 사용법은 다른 Filter Stream(Buffered I/O, Data I/O)과 유사하다.
Object 스트림의 입출력 대상이 되는 클래스는 Serializable 인터페이스를 구현한다.
만약 클래스의 일부 멤버 변수를 Serialization(직렬화) 대상에서 제외시키려면 transient 키워드를 사용하면 된다.
상속 관계
java.lang.Object
└ java.io.InputStream
└ java.io.ObjectInputStream
java.lang.Object
└ java.io.OutputStream
└ java.io.ObjectOutputStream
💻 예제 1
📝 소스 코드
- Member
Object 스트림의 입출력 대상이 되는 클래스이다.
import java.io.Serializable;
public class Member implements Serializable {
private static final long serialVersionUID = /* Serializable implements 시 생성 */;
private String id;
private String pw;
transient private int num;
transient private boolean isExist;
public Member() {}
public Member(String id, String pw) {
this.id = id;
this.pw = pw;
this.num = 123;
this.isExist = true;
}
public void displayInfo() {
System.out.println("----- 회원 정보 -----");
System.out.println("아이디 : " + id);
System.out.println("비밀번호 : " + pw);
System.out.println("번호 : " + num);
System.out.println("Exist? " + isExist);
System.out.println();
}
}
처음 Serializable implements 시 Warning이 뜨는데 해당 경고를 클릭하면 아래와 같은 내용을 볼 수 있다.
여기서 두 번째 Add Generated serial version ID를 클릭하면 자동으로 serialVersionUID를 생성해준다.
num과 isExist는 transient로 선언해주어서 직렬화 대상에서 제외되어 파일에 write 되지 않는다.
de-serialization(역직렬화, 파일에서 읽기)를 할 때는 transient로 선언된 변수가 해당 타입의 기본 값(0, false, null)으로 초기화된다.
- Main
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
public class Main {
public static final String FILEPATH = "temp/member.dat";
public static void main(String[] args) {
try (OutputStream out = new FileOutputStream(FILEPATH);
ObjectOutputStream oout = new ObjectOutputStream(out);
InputStream in = new FileInputStream(FILEPATH);
ObjectInputStream oin = new ObjectInputStream(in);) {
Member m1 = new Member("root", "root1234");
oout.writeObject(m1);
Member m2 = new Member("guest", "guest");
oout.writeObject(m2);
Member m3 = new Member("admin", "admin12456");
oout.writeObject(m3);
Member dataRead;
/* 방법 1 : 매번 readObject() 호출
dataRead = (Member) oin.readObject();
dataRead.displayInfo();
dataRead = (Member) oin.readObject();
dataRead.displayInfo();
dataRead = (Member) oin.readObject();
dataRead.displayInfo();
*/
/* 방법 2 : 무한 루프로 readObject() 호출하고, EOFException으로 종료
EOFException으로 끝까지 read한 것을 체크한다. */
while (true) {
dataRead = (Member) oin.readObject();
dataRead.displayInfo();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (EOFException e) {
System.out.println("파일 끝");
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
파일 읽는 방법을 두 가지로 서술했다.
첫 번째 방법으로 하는 경우, 매번 readObject()를 호출해야하므로 코드가 길어지고 작성이 번거로워진다. 그래서 while 무한 루프를 이용해 EOF(End Of File)까지 파일을 읽게 하는 두 번째 방법을 많이 사용한다.
📄 실행 결과
앞서 num과 isExist는 transient로 선언해주었기 때문에 두 변수 값 모두 기본 값으로 초기화되었다.
Object Filter Stream
ArrayList<>와 같은 Collection에서 모든 데이터들이 Serializable(직렬화)되어 있으면 ObjectInputStream과 ObjectOutputStream으로 객체를 이용해 파일을 읽고 쓸 수 있다.
💻 예제 2
📝 소스 코드
- Member
예제 1에서 사용했던 코드와 동일하다.
- Main
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static final String FILEPATH = "temp/member2.dat";
public static void main(String[] args) {
try (OutputStream out = new FileOutputStream(FILEPATH);
ObjectOutputStream oout = new ObjectOutputStream(out);
InputStream in = new FileInputStream(FILEPATH);
ObjectInputStream oin = new ObjectInputStream(in);) {
List<Member> list = new ArrayList<Member>();
// 파일에 쓸 데이터 객체 생성
Member m1 = new Member("root", "root");
Member m2 = new Member("guest", "guest1234");
Member m3 = new Member("admin", "admin5678");
list.add(m1);
list.add(m2);
list.add(m3);
oout.writeObject(list); // List를 파일에 한 번에 저장
list = null; // list 비우기
list = (ArrayList<Member>) oin.readObject(); // 파일에서 읽어오기
for (Member m : list) // console에 출력
m.displayInfo();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Member 객체를 저장하는 List인 list를 생성한 뒤, 파일에 쓸 데이터 객체를 list에 저장한다.
ObjectOutputStream의 writeObject() 메소드를 이용해 list를 한 번에 파일에 쓸 수 있다.
파일에 저장하였으니 list를 null로 하고, 파일에 저장된 내용을 readObject() 메소드를 통해 읽어와 list에 저장하여 출력한다.
다음 실행 결과를 통해 데이터 객체가 잘 써지고 읽어진 것을 확인할 수 있다.
📄 실행 결과