ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Serializable
    Java 2020. 2. 7. 09:35
    반응형

    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에서 사용할 때는 문제 없음

    패스워드 등에서 사용 

     

    반응형

    'Java' 카테고리의 다른 글

    Enum(열거형)  (0) 2020.02.08
    서버 통신 - Socket / UDP  (0) 2020.02.07
    Java NIO  (0) 2020.02.06
    JAVA I/O  (0) 2020.02.06
    Thread 관련 - Object, ThreadGroup, ThreadLocal, volatile  (0) 2020.01.16

    댓글

Designed by Tistory.