객체의 직렬화라고 한다.
차례대로 읽고 쓰는 것이다.
객체를 바이트 단위로 일렬로 쭉 세울 수 있는 것이다.
→ 객체를 byte 단위로 쪼개서 File 에 전송해서 저장하고, 다시 내보내서 객체를 복구시킨다.
⇒ Serializable : 직렬화를 지원하는 애들만이 사용이 가능하고, 이를 분간해주는 마크인터페이스이다.
ObjectInputStream, ObjectOutputStream : 객체를 전부 읽고 쓰는 스트림이다.
→ 가장 상위의 Stream 이다.
→ byte 연산이다.
→ 객체의 직렬화를 기반으로 한다.
객체의 직렬화는 직렬화를 지원하는 객체만 읽고 쓰는 것이 가능하다.
대부분의 class들이 직렬화에 대해 대비하고 있지만 분간이 안될 때 Serializable을 이용해서 알 수 있다.
package home;
public class MyObjEx {
private String value;
private int num;
public MyObjEx(String value, int num) {
super();
this.value = value;
this.num = num;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
@Override
public String toString() {
return "MyObjEx [value=" + value + ", num=" + num + "]";
}
}
package home;
import java.io.Closeable;
import java.io.IOException;
public class IOUtils {
public static void closeAll(Closeable...c) {
for(Closeable temp : c) {
try {
temp.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
위와 같은 MyObjEx(직렬화를 위한 클래스) 와 IOUtils(자원 해제를 위한 클래스) 클래스를 일단 만든다.
package home;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class MyObjectIOEx1 {
private static String file = "myObj.dat";
public static void write() {
//Serializable 구현하지 않으면 에러 : NotSerializableException
MyObjEx obj = new MyObjEx("abc", 100);
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
fos = new FileOutputStream(file);
oos = new ObjectOutputStream(fos);
oos.writeObject(obj);
oos.flush();
oos.reset();
} catch (IOException e) {
e.printStackTrace();
}finally {
IOUtils.closeAll(oos,fos);
}
}
public static void read() {
}
public static void main(String[] args) {
write();
}
}
→ 위 코드를 실행하면 위 사진과 같은 오류가 발생한다.
이는 MyObjEx 클래스가 직렬화를 지원받고 있지 않기때문이다.
→ reset( ) : 하지 않으면 다른 객체를 써도 안써지고 이전에 쓰인게 계속 호출되기에 꼭 해줘야 한다.
⇒ flush( ) 와 reset( ) 은 꼭 해주기!
package home;
import java.io.Serializable;
public class MyObjEx implements Serializable{
private String value;
private int num;
public MyObjEx(String value, int num) {
super();
this.value = value;
this.num = num;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
@Override
public String toString() {
return "MyObjEx [value=" + value + ", num=" + num + "]";
}
}
→ 오류를 수정하기 위해서는 위 코드와 같이 Serializable을 지원받고 있으면 된다.
→ implements Serializable : 직렬화를 구현하게 되어 실행 가능하다.
<ObjectInputStream, ObjectOutputStream 의 주의사항>
: 파일 당 객체 1개만 사용해야한다.
여러개 써도 되지만 read 할때 제대로 된다는 복장이 없다.
→ 여러개를 쓰고 싶을 때는 Vector 에 넣어서 쓰면 된다.
(Vector 가 Serializable를 구현하고 있어서 문제는 없다.)
→ But 제네릭(원소)이 Serializable를 구현하고 있지 않으면 문제 생긴다.
( 즉, 모두 Serializable를 구현하고 있어야한다.)
package home;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Vector;
public class MyObjectIOEx1 {
private static String file = "myObj.dat";
public static void write() {
//Serializable 구현하지 않으면 에러 : NotSerializableException
Vector<MyObjEx> vec = new Vector<MyObjEx>();
vec.add(new MyObjEx("abc", 100));
vec.add(new MyObjEx("def", 100));
vec.add(new MyObjEx("ghi", 100));
vec.add(new MyObjEx("jkl", 100));
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
fos = new FileOutputStream(file);
oos = new ObjectOutputStream(fos);
oos.writeObject(vec);
oos.flush();
oos.reset();
} catch (IOException e) {
e.printStackTrace();
}finally {
IOUtils.closeAll(oos,fos);
}
}
public static void read() {
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream(file);
ois = new ObjectInputStream(fis);
//? : 아무것나 해라(Object의 의미를 가진다.)
//get 하면 Object로 나와서 형변환 해야함
Vector<?> o = (Vector<?>)ois.readObject();
System.out.println(o);
} catch(IOException e) {
e.printStackTrace();
} catch(ClassNotFoundException e) {
e.printStackTrace();
}
finally {
IOUtils.closeAll(ois, fis);
}
}
public static void main(String[] args) {
write();
//read();
}
}
→ file에는 멤버변수만 저장이 가능하다.
나머지는 class 파일에 저장되는데 class파일 없이 file을 복구하면 ClassNotFoundException이 발생한다.
→ class 파일 생성 당시의 serialVersionUID의 값과 file의 serialVersionUID의 값이 다르면 InvalidClassException이 발생한다.
→ 임의로 우리가 직접 지정하면 예외가 발생하지 않는다.