ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java - io 직렬화(Serialization)
    언어/JAVA 2023. 7. 24. 11:20

    - 직렬화란, 객체를 저장했다가 다시 꺼내쓰거나, 네이트워크를 통해 객체를 주고받을 수 있게 만드는 방법이다.

     

    1. 직렬화란?

     

    - 객체를 데이터 스트림으로 만드는 것을 뜻한다.

    - 객체에 저장된 데이터를 스트림에 쓰기 위해 연속적인 데이터로 변환하는 것을 말한다.

    - 반대의 의미로 역직렬화가 있다.

     

    출처: https://codedragon.tistory.com/5485

     

    - 자바 프로그램에 메모리 구조에서 살펴봤듯이, 인스턴스 메서드도 객체에 포함되지 않는다

       -> 동일 클래스에 여러 인스턴스 별로 다른 값을 가지지 않음 -> 힙 영역이 아닌 타 영역에 생성된 메서드 참조해서 사용

    - 객체를 저장한다는 것은 인스턴스 변수값을 저장한다는 의미이다. 

     

    2. 객체 스트림 

     

    - 직렬화와 역직렬화를 위한 스트림으로 ObjectInputStream, ObjectOutputStream이 있다.

    - 보조 스트림이므로, 기반 스트림 InputStream, OutputStream과 연결되어 사용된다. 

     

    ObjectInputStream ois = new ObjectInputStream(바이트 입력 스트림);// 역직렬화
    ObjectOutputStream oos = new ObjectOutputStream(바이트 출력 스트림);// 직렬화
    
    oos.writeObject(객체) //직렬화
    참조변수 변수 = (타입) ois.readObject(); // 역직렬화
    
    
    //FileInput,Output stream이 기반 스트림으로 자주 사용

     

    - readObject()와 wirteObject()외에도 다양한 메서드 제공한다.

    출처: https://developer-hm.tistory.com/73

     

    3. Serializable 인터페이스와 transient

     

    - 자바에서는 Serializable 인터페이스를 구현한 클래스만 직렬화할 수 있다. 

    - 위 인터페이스는 멤버가 없는 빈 인터페이스지만, 객체를 직렬화할 수 있다고 표시하는 역할을 수행한다.

    - transient로 선언된 필드는 직렬화에서 제외되므로 출력되지 않는다. (직렬화시 필드의 기본값으로 직렬화 된다.)

    - 직렬화 시에 필드로 포함된 각 객체들은 직렬화 가능한 객체면 직렬화되고, 아니면 안된다.

    public class UserInfo implements java.io.Serializable{
    
    
    }
    
    
    //인터페이스 구현한 클래스 상속받아도 직렬화 가능한 클래스가 됨
    // 단 자손 직렬화 시, 조상도 필드도 함께 직렬화 됨
    public class superUser implements java.io.Seializable{
     // field
    }
    
    public class userinfo extends superUser{
    
    // field 2
    Object obj = new Object(); // 직렬화에서 빠짐 Object는 Serializable을 구현하지 않음 
    Object obj2 = new String("s");// String은 직렬화 가능 (객체에서 항상 중요한 것은 참조변수가 아닌 실제 생성된 객체가 무엇인지 임)
    }

     

     

     

    4. SerialVersionUID필드

     

    - 객체가 직렬화 될 때 클래스에 정의된 멤버 정보를 이용해서 serialVersionUID라는 클래스 버전이 자동생성되어 직렬화 내용에 포함된다. (역직렬화시 클래스 버전을 비교한다)

    - 직렬화할 때 사용된 클래스와 역직렬화 시에 사용된 클래스는 기본적으로 동일한 클래스여야한다.

    - 만약 클래스 이름이 같더라도 내용이 다르면 역직렬화에 실패한다.(*단 직렬화에 포함되지 않는 필드는 상관 없다)

    - 클래스 내용이 다르다 할지라도 직렬화된 필드를 공통으로 포함하고 있다면, 역직렬화 할 수 있는 방법이 있다.

    - 두 클래스가 동일한 SerialVersionUID 상수값을 가지고 있으면 된다. 

     

    //이 상태로 직렬화됨 
    public class Member implements Serialiazable{
    
    	static final long serialVersionUID =1;
        int field1;
        int field2;
    
    }
    // 역직렬화 전에 새로운 필드 추가 되었지만, 역직렬화 가능 
    public class member implements Seializable {
    static final long serialVersionUID =1;
        int field1;
        int field2;
        int field3;
    
    }

    - serialVersionUID는 자동생성하는 기능을 제공하기 때문에 이를 이용하도록 하자.

     

    * 네트워크로 객체를 직렬화하여 전송하는 경우, 보내는 쪽과 받는 쪽이 모두 같은 버전의 클래스를 가지고 있어야한다.

    -> 클래스가 조금만 변경되어도 에러가 발생 (클래스에 내용이 추가되면 직렬화시 

    -> 이럴때 에러발생을 없애기 위해 수동으로 버전을 관리할 필요가 있다

         (버전을 명시해두고, 추후 변동사항이 생기더라도, 버전이 자동으로 바뀌지않도록 관리) -> serialVersionUID

    -> cmd를 이용해서도 버전을 알 수 있다 

     

     

    예제1

     

    -직렬화 and 역직렬화 

    public class Product implements Serializable {
        private static final long serialVersionUID = 1L;
        private String name;
        private int price;
    
        public Product(String name, int price){
            this.name = name;
            this.price = price;
        }
        @Override
        public String toString(){
            return name+":"+price;
        }
    }
    public class Member implements Serializable {
        private static final long serialVesrionUID = 1L;
        private String id;
        private String name;
    
        public Member(String id, String name){
            this.id = id;
            this.name = name;
        }
    
        @Override
        public String toString(){
            return id +":"+name;
        }
    }
    public static void main(String[] args) throws Exception{
    
            FileOutputStream fos = new FileOutputStream("C:\\Users\\won\\IdeaProjects\\untitled2\\src\\Seializableex"+ File.separator+"objects.bat");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            Member me1 = new Member("jj","j");
            Product p2 = new Product("놋북",1000);
            int[] arr1 = {1,2,3};
    
            oos.writeObject(me1);
            oos.writeObject(p2);
            oos.writeObject(arr1);
    
            oos.flush();oos.close();fos.close();
    
            //FileInputStream에 ObjectInputStream 보조스트림 연결
            FileInputStream fis = new FileInputStream("C:\\Users\\won\\IdeaProjects\\untitled2\\src\\Seializableex"+ File.separator+"objects.bat");
            ObjectInputStream ois = new ObjectInputStream(fis);
    
            Member m2 = (Member)ois.readObject();
            Product p22 = (Product) ois.readObject();
            int[] arr2 = (int[])ois.readObject();
    
            ois.close();fis.close();
    
            System.out.println(m2);
            System.out.println(p22);
            System.out.println(Arrays.toString(arr2));
        }

     

    -직렬화 역직렬화 예제 2

    ublic class UserInfo implements Serializable {
        String name;
        String pass;
        int age;
    
        public UserInfo(){
            this("Unknown","1111",0);
        }
        public UserInfo(String name ,String pass ,int age){
            this.name = name;
            this.pass = pass;
            this.age = age;
    
        }
        @Override
        public String toString(){
            return name+":"+pass+":"+age;
        }
    }
    public class SerialEx1 {
        public static void main(String[] args){
            try{
                String fileName = "UserInfo.ser";
                FileOutputStream fos = new FileOutputStream(fileName);
                BufferedOutputStream bos = new BufferedOutputStream(fos);
                ObjectOutputStream oos = new ObjectOutputStream(bos);
    
                UserInfo u1 = new UserInfo();
                UserInfo u2 = new UserInfo("ss","232",26);
    
                ArrayList<UserInfo> list =new ArrayList<>();
                list.add(u1);
                list.add(u2);
    
                oos.writeObject(u1);
                oos.writeObject(u2);
                oos.writeObject(list);
                oos.close();
                System.out.println("직렬화 완료");
    
    
            }catch (IOException e){
                e.printStackTrace();
            }
    
    
    
        }
    }
    public class SerialEx2 {
        public static void main(String[] args){
            try{
    
                String filename ="UserInfo.ser";
                FileInputStream fis = new FileInputStream(filename);
                BufferedInputStream bis = new BufferedInputStream(fis);
                ObjectInputStream ois = new ObjectInputStream(bis);
    
                //객체 읽는 순서는 출력순서와 동일
                UserInfo u1 = (UserInfo) ois.readObject();
                UserInfo u2 = (UserInfo) ois.readObject();
                ArrayList<UserInfo> list = (ArrayList<UserInfo>) ois.readObject();
    
    
                System.out.println(u1);
                System.out.println(u2);
                System.out.println(list);
                ois.close();
    
    
    
    
    
            }catch (IOException e){
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
    }

     

    +추가 상속 직렬화 역직렬화 

    //UserInfo에서 직렬화 인터페이스 제거 

    public class UserInfo {
        String name;
        String pass;
        int age;
    
        public UserInfo(){
            this("Unknown","1111",0);
        }
        public UserInfo(String name ,String pass ,int age){
            this.name = name;
            this.pass = pass;
            this.age = age;
    
        }
        @Override
        public String toString(){
            return name+":"+pass+":"+age;
        }
    }
    public class UserInfo2 extends UserInfo implements Serializable {
        String address;
        
        public UserInfo2(){
            this("Unknown","1111",0,"NAN");
        }
        public UserInfo2(String name, String pass ,int age,String address){
            super(name,pass,age);
            this.address = address;
        }
        
        
        private void writeObject(ObjectOutputStream out) throws IOException {
            out.writeUTF(name);
            out.writeUTF(pass);
            out.writeInt(age);
            out.defaultWriteObject(); //자신에게 정의된 address 직렬화 
        }
        private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException {
            name = in.readUTF();
            pass = in.readUTF();
            age = in.readInt();
            in.defaultReadObject(); //자신에게 정의된 address 역직렬화
        }
    
    }

    - 직렬화되지 않은 조상으로 부터 멤버를 받았을 때 이에 대한 직렬화 처리를 구현한 것이다.

     

Designed by Tistory.