학습목표
· Serializable이란?
· 객체 저장하기
· 객체 읽기
· serialVersionUID 사용하기
· transient
Serializable이란?
· 구현한 객체를 바이트 스트림으로 변환하여 저장하거나 전송할 수 있도록하는 인터페이스
· 선언된 변수와 메소드가 없음
· 용도: 생성한 객체를 파일로 저장, 저장한 객체 읽기, 객체를 다른 서버로 전송, 다른 서버에서 생성한 객체 읽기
▶ java-io 패키지에 선언된 Serializable 인터페이스
package java.io;
public interface Serializable {
}
· Serializable 인터페이스 구현 후 serialVersionUID 값을 지정해 주는 것을 권장
- serialVersionUID: 해당 객체의 버전을 명시하는 데 사용
- 반드시 static final long으로 선언
객체 저장하기
· 자바에서는 ObjectOutputStream 클래스를 사용하여 객체 저장
public class SerialDTO implements Serializable {
private String bookName;
private int bookOrder;
private boolean bestSeller;
private long soldPerDay;
public SerialDTO(String bookName, int bookOrder, boolean bestSeller, long soldPerDay){
this.bookName = bookName;
this.bookOrder = bookOrder;
this.bestSeller = bestSeller;
this.soldPerDay = soldPerDay;
}
@Override
public String toString() {
return "SerialDTO{" +
"bookName='" + bookName + '\'' +
", bookOrder=" + bookOrder +
", bestSeller=" + bestSeller +
", soldPerDay=" + soldPerDay +
'}';
}
}
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import static java.io.File.separator;
public class SerializableDemo {
public static void main(String[] args){
//저장될 경로 + 파일이름 설정
String fullPath = separator + "Users" + separator + "ted.sc" + separator + "Desktop" + separator + "git" +
separator + "javaStudy" + separator + "src" +separator + "SerializableAndNio" + separator + "serial.obj";
SerialDTO dto = new SerialDTO("GodOfJavaBook", 1, true, 100);
saveObject(fullPath, dto);
}
static void saveObject(String fullPath, SerialDTO dto){
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try{
fos = new FileOutputStream(fullPath); //FileOutputStream 객체 생성
oos = new ObjectOutputStream(fos); //ObjectOutputStream 객체를 생성하고, 이때 fos 객체를 매개변수로 넘김. 이렇게 하면 해당 객체가 파일에 저장.
oos.writeObject(dto); // 메서드의 매개변수로 넘어온 dto 객체 저장
}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();
}
}
}
}
· 위 코드를 실행하면, 다음과 같이 객체가 파일로 저장된다.
· 해당 객체는 바이너리로 저장
· 이때 SerialDTO 클래스에서 Serializable 인터페이스를 implements 하지 않으면, NotSerializableException이 발생
객체 읽기
· 자바에서는 ObjectInputStream 클래스를 사용하여 객체를 읽음
public class SerializableDemo {
public static void main(String[] args){
String fullPath = separator + "Users" + separator + "ted.sc" + separator + "Desktop" + separator + "git" +
separator + "javaStudy" + separator + "src" +separator + "SerializableAndNio" + separator + "serial.obj";
loadObject(fullPath);
}
static 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에 구현한 toString() 메서드로 인해 읽어 들인 객체의 내용이 출력된다.
serialVersionUID 사용하기
· 이때 Serializable 객체를 변경하여 저장했던 객체와 다르게 만들면 오류가 발생한다.
public class SerialDTO implements Serializable {
private String bookType="IT";
//이후 생략
· 객체가 변경되어 컴파일시 serialVersionUID가 다시 생성되어 InvalidClassException 발생
java.io.InvalidClassException: SerializableAndNio.SerialDTO; local class incompatible:
stream classdesc serialVersionUID = -2328603707165096545, local class serialVersionUID = 8853932105027462604
at java.base/java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:722)
at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2022)
at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1872)
at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2179)
at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1689)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:495)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:453)
at SerializableAndNio.SerializableDemo.loadObject(SerializableDemo.java:22)
at SerializableAndNio.SerializableDemo.main(SerializableDemo.java:13)
· SerialVersionUID를 사용하면, 객체가 수정되었을 때 이것과 다르게 작동한다.
· SerialDTO에 bookType 변수를 제거하고, serialVersionUID을 선언한 후 객체를 다시 파일로 저장한다.
public class SerialDTO implements Serializable {
static final long serialVersionUID=1L;
private String bookName;
private int bookOrder;
private boolean bestSeller;
private long soldPerDay;
public SerialDTO(String bookName, int bookOrder, boolean bestSeller, long soldPerDay){
this.bookName = bookName;
this.bookOrder = bookOrder;
this.bestSeller = bestSeller;
this.soldPerDay = soldPerDay;
}
@Override
public String toString() {
return "SerialDTO{" +
"bookName='" + bookName + '\'' +
", bookOrder=" + bookOrder +
", bestSeller=" + bestSeller +
", soldPerDay=" + soldPerDay +
'}';
}
}
· 객체를 파일로 저장했다면 bookType 변수를 다시 추가하고, 해당 변수를 위해 toString() 메서드도 추가한다.
public class SerialDTO implements Serializable {
static final long serialVersionUID=1L;
private String bookType="IT";
private String bookName;
private int bookOrder;
private boolean bestSeller;
private long soldPerDay;
public SerialDTO(String bookName, int bookOrder, boolean bestSeller, long soldPerDay){
this.bookName = bookName;
this.bookOrder = bookOrder;
this.bestSeller = bestSeller;
this.soldPerDay = soldPerDay;
}
@Override
public String toString() {
return "SerialDTO{" +
"bookType='" + bookType + '\'' +
", bookName='" + bookName + '\'' +
", bookOrder=" + bookOrder +
", bestSeller=" + bestSeller +
", soldPerDay=" + soldPerDay +
'}';
}
}
· 파일을 읽어들이면, 다음과 같이 InvalidClassException이 발생하지 않고 변경되어 인식 할 수 없는 부분(bookType)이 null로 처리된다.
· serializable한 객체 내용이 바뀌었는데 아무런 예외가 발생하지 않으므로 운영 상황에서 데이터가 꼬일 수 있으므로 권장 x
· 데이터가 바뀌면 serialVersionUID를 변경하는 습관을 갖기
transient
· 선언한 변수를 Serializable 대상에서 제외하는 예약어
· 용도: 보안상 중요한 변수, 꼭 저장할 필요가 없는 변수에 사용
ex) 패스워드
▶ SerialDTO의 boorOrder 변수 앞에 transient 선언
public class SerialDTO implements Serializable {
static final long serialVersionUID=1L;
private String bookName;
transient private int bookOrder;
private boolean bestSeller;
private long soldPerDay;
public SerialDTO(String bookName, int bookOrder, boolean bestSeller, long soldPerDay){
this.bookName = bookName;
this.bookOrder = bookOrder;
this.bestSeller = bestSeller;
this.soldPerDay = soldPerDay;
}
@Override
public String toString() {
return "SerialDTO{" +
", bookName='" + bookName + '\'' +
", bookOrder=" + bookOrder +
", bestSeller=" + bestSeller +
", soldPerDay=" + soldPerDay +
'}';
}
}
· 위와 같이 SerialDTO를 변경하고, 객체를 읽으면 bookOrder가 Serializable 대상에서 제외되어 값을 1로 지정했지만 0이 출력된다.
'자바' 카테고리의 다른 글
[Java] 자바 String을 효율적으로 사용하는 방법 (StringBuilder, StringBuffer, StringJoiner, Collectors.joining) (0) | 2021.09.29 |
---|---|
[Java] NIO2란? (0) | 2021.09.24 |
[Java] 자바의 Input과 Output (입출력, i/o) (0) | 2021.09.10 |
[Java] 자바의 Scanner 클래스 사용하기 (0) | 2021.09.10 |
[Java] 자바 7의 새로운 기능 (0) | 2021.09.10 |
댓글