Serializable
Serializable
java.io 패키지의 Serializable Interface
선언된 변수나 메서드가 없음
클래스가 파일에 읽거나 쓸 수 있도록 하거나, 다른 서버로 보내거나 받을 수 있도록 하려면 해당 인터페이스를 구현(implements)해야 함 - 구현시 JVM에서 해당 객체를 저장하거나 다른 서버로 전송할 수 있도록 해줌
serialVersionUID
Serializable 인터페이스를 구현한 후, serialVersionUID 값을 지정해주는 것을 권장
별도로 지정하지 않으면, 컴파일시 자동 생성 됨
static final long serialVersionUID = 1L;
뒤에 지정하는 값 : 해당 객체의 버전을 명시하는 데 사용됨
: 해당 객체가 같은지 다른지를 확인할 수 있도록 관리하는 값
단, 같은 UID일지라도 변수의 개수나 타입 등이 다르면 다른 클래스로 인식함 유의
ObjectOutputStream & ObjectInputStream
ObjectOutputStream : 자바에서 해당 클래스를 사용시 객체를 저장할 수 있음
ObjectInputStream : 자바에서 해당 클래스를 사용하면 저장해 놓은 객체를 읽을 수 있음
객체 저장
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import static java.io.File.separator;
public class ManageObject {
public static void main(String[] args) {
ManageObject manager = new ManageObject();
String fullPath = "/Users/hs" + separator + "chicken.obj";
SerialDTO dto = new SerialDTO("ABChicken", 1, true, 100);
manager.saveObject(fullPath, dto);
}
public void saveObject(String fullPath, SerialDTO dto) {
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
// 파일 객체 생성
fos = new FileOutputStream(fullPath);
// ObjectOutputStream 객체, 생성시 파일객체를 넘김 > 파일에 저장됨
oos = new ObjectOutputStream(fos);
// 매개 변수로 넘어온 객체 저장
oos.writeObject(dto);
System.out.println("Write Success");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (oos != null) {
try {
oos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (fos!=null) {
try {
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
import java.io.Serializable;
public class SerialDTO implements Serializable {
private String chickenName;
private int chickenOrder;
private boolean bestChicken;
private long soldPerDay;
public SerialDTO(String chickenName, int chickenOrder, boolean bestChicken, long soldPerDay) {
super();
this.chickenName = chickenName;
this.chickenOrder = chickenOrder;
this.bestChicken = bestChicken;
this.soldPerDay = soldPerDay;
}
@Override
public String toString() {
return "SerialDTO [chickenName = " + chickenName + ", chickenOrder = " + chickenOrder
+ ", bestChicken = " + bestChicken + ", soldPerDay = " + soldPerDay + "]";
}
}
// Serializable 구현하지 않을 경우
java.io.NotSerializableException: SerialDTO
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at ManageObject.saveObject(ManageObject.java:23)
at ManageObject.main(ManageObject.java:11)
// 구현한 경우 - 파일에 들어감 (바이너리 형식..)
$ cat /Users/hs/chicken.obj
??sr SerialDTOJ,?>?Eo?Z
bestChickenI
chickenOrderJ
soldPerDayL
chickenNametLjava/lang/String;xpdt ABChicken
객체 읽기
import java.io.*;
import static java.io.File.separator;
public class ManageObject {
public static void main(String[] args) {
ManageObject manager = new ManageObject();
String fullPath = "/Users/hs" + separator + "chicken.obj";
manager.loadObject(fullPath);
}
public void loadObject(String fullPath) {
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream(fullPath);
ois = new ObjectInputStream(fis);
Object obj = ois.readObject();
SerialDTO dto = (SerialDTO) obj;
System.out.println(dto);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (ois != null) {
try {
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
// 내용 확인
SerialDTO [chickenName = ABChicken, chickenOrder = 1, bestChicken = true, soldPerDay = 100]
BUILD SUCCESSFUL in 0s
Serializable 객체의 형태가 변경되면
컴파일시 serialVersionUID가 재생성됨
즉, Serializable을 구현한 클래스에 클래스 변수를 추가한다면, serialVersionUID가 변경되므로 읽을 수 없게됨
// SerialDTO에 클래스 변수가 추가된다면 - 객체읽을경우
// private String chickenType = "A";
java.io.InvalidClassException: SerialDTO; local class incompatible: stream classdesc serialVersionUID = 5344876925460770704, local class serialVersionUID = -8689217447952664982
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1885)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
at ManageObject.loadObject(ManageObject.java:18)
at ManageObject.main(ManageObject.java:9)
그럴경우 serialVersionUID를 명시해주기
static final long serialVersionUID = 1L;
serialVersionUID를 지정해놓은 상태에서 저장되어 있는 객체와 읽는 객체가 다르면?
SerialDTO [chickenName=ABChicken, chickenOrder=2, bestChicken=true, soldPerDay=200, ChickenTypes=null]
-
변수의 이름이 바뀌거나 추가되는 등 하면, 객체에서 찾을 수 없으면 null로 처리
-
예외는 발생하지 않음
-
Serializable한 객체의 내용이 바뀌었는데도 예외가 발생하지 않으면 데이터가 꼬일 수 있음 = 권장하지 않음
-
데이터가 변경되면 serialVersionUID를 변경하는 습관을 가질 것 > 자동으로 해주는 IDE도 많음
transient 예약어
보안상 중요한 변수나 꼭 저장할 필요가 없는 변수에 대해 사용하는 예약어
객체를 저장하거나 타 JVM으로 보낼 때, transient라는 예약어를 사용하여 선언한 변수는 Serializable 대상에서 제외됨 (아예 무시 됨)
해당 객체를 생성한 JVM에서 사용할 때는 문제 없음
패스워드 등에서 사용