• Java序列化与反序列化



    Java序列化与反序列化


    Java提供了两种对象持久化的方式,分别为序列化和外部序列化

      • 序列化

          在分布式环境下,当进行远程通信时,无论是何种类型的数据,都会以二进制序列的形式在网络上传输。序列化是一种将对象以一连串的字节描述的过程,用于解决在对对象流进行读写操作时所引发的问题。序列化可以将对象的状态写在流里进行网络传输,或者保存到文件、数据库等系统中,并在需要时把该流读取出来重新构造一个相同的对象。

          所有实现序列化的类都必须实现Serializable接口,Serializable接口位于java.lang包中,没有任何实现方法,使用一个输出流(例如FileOutputStream)来构造一个ObjectOutputStream对象,紧接着使用该对象的writeObject(Object obj)方法就可以将obj对象写出,要恢复时可以使用其对应的输入流ObjectInputStream.

    序列化有以下两个特点:

      1. 如果一个类能被序列化,那么它的子类也能够被序列化。
      2. 由于static(静态)代表类的成员,transient(Java关键字,如果用transient声明一个实例变量,当对象存储时,它的值不需要维持)代表对象的临时数据,因此被声明为这两种类型的数据成员是不能够被序列化的。

          如下是序列化的代码,首先声明一个Student类继承Serializable接口,由代码中的输出(注释即为输出内容)可得静态变量SCHOOLNAME和SCHOOLID的值发生了变化,难道是静态变量也被序列化了吗?其实不是的,因为在当前的运行程序中,Student类的静态变量的值已经发生了变化,如果真的已经序列化了,那么我们将序列化的那个函数去掉,让程序从SourceFile/Student文件中反序列化,那么得到的SCHOOLNAME应该为HeBei University,SCHOOLID的值为2,然而当我们去掉序列化代码,直接从文件反序列化,输出SCHOOLNAME是Beijing University of Post and Telecommunications,SCHOOLID为1,说明static变量并没有被序列化。

    public class Student implements Serializable{
        /**
         * 
         */
        private static final long serialVersionUID = 1L;
        public String name;
        public int id;
        public static String SCHOOLNAME = "Beijing University of Post and Telecommunications";
        private static int SCHOOLID = 1;
        public Student(){
            name = "zhangsan";
            id = 2018111846;
        }
        public Student(String name, int id){
            this.name = name;
            this.id = id;
        }
        public static void setSchool(String school_name, int school_id){
            SCHOOLID = school_id;
            SCHOOLNAME = school_name;
        }
        public static int getSchoolID(){
            return SCHOOLID;
        }
    }
    
    
    public class Test {
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            Student student = new Student();
            student.setSchool("Hebei University",2);
            serialize(student);
            deserialize();
        }
        /**
         * 序列化student对象
         * @param student
         */
        private static void serialize(Student student){
            try {
                ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("SourceFile/Student"));
                oos.writeObject(student);
                oos.close();
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        /**
         * 从文件反序列化student对象
         */
        private static void deserialize(){
            try {
                FileInputStream fis = new FileInputStream("SourceFile/Student");
                Student student = (Student) new ObjectInputStream(fis).readObject();
                System.out.println(student.name);     //输出zhangsan
                System.out.println(student.id);       //输出2018111846
                System.out.println(student.SCHOOLNAME);    //输出HeBei University
                System.out.println(student.getSchoolID()); //输出2
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
      • 外部序列化

          Java语言还提供了另外一种方式来实现对象持久化,即外部序列化。外部序列化与序列化的主要区别在于序列化是内置的API,只需要实现Serializable接口,开发人员不需要编写任何代码就可以实现对象的序列化,而是用外部序列化时,Externalizable接口中的方法必须有开发人员实现。因此与实现Serializable接口的方法相比,使用Externalizable编写程序的难度更大,但是由于控制权交给了开发者,在编程时有更多的灵活性,对需要持久化的那些属性可以进行控制,可能提高程序的性能。

    如下是外部序列化的代码,当把序列化部分的代码注释到之后,发现薪金的输出还是10000,则可以确定当执行外部序列化时,static静态变量也被序列化了,而且方法中没有序列化id属性,则反序列化后发现id并没有发生变化:

    public class Teacher implements Externalizable{
        private String name;
        private int id;
        private static int salary = 5000;
        public Teacher(){
            name = "zhangsan";
            id = 182566;
        }
        public static void setSalary(int s){
            salary =s;
        }
        public int getID(){
            return id;
        }
        public void setID(int id){
            this.id = id;
        }
        @Override
        public void readExternal(ObjectInput in) throws IOException,
                ClassNotFoundException {
            // TODO Auto-generated method stub
            Date date = (Date)in.readObject();
            name = (String)in.readObject();
            salary = (Integer)in.readObject();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            System.out.println(sdf.format(date));
        }
    
        @Override
        public void writeExternal(ObjectOutput out) throws IOException {
            // TODO Auto-generated method stub
            Date date = new Date();
            out.writeObject(date);
            out.writeObject(name);
            out.writeObject(salary);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            System.out.println(sdf.format(date));
        }
        public String toString(){
            return "教师名称" + name + "  薪金" + salary;
        }
    }
    
    
    public class Test {
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            Teacher teacher = new Teacher();
            teacher.setSalary(10000);
            teacher.setID(111111);
            System.out.println(teacher.getID());        //输出111111
            serialize(teacher);
            Teacher teacher1 = deserialize();
            System.out.println(teacher1.toString());    //输出教师名称zhangsan  薪金10000
            System.out.println(teacher1.getID());       //输出182566
        }
        /**
         * 外部序列化teacher对象
         * @param teacher
         */
        private static void serialize(Teacher teacher){
            try {
                ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("SourceFile/Teacher"));
                oos.writeObject(teacher);
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        private static Teacher deserialize(){
            try {
                ObjectInputStream ois = new ObjectInputStream(new FileInputStream("SourceFile/Teacher"));
                return (Teacher)ois.readObject();
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return null;
        }
    }

    Java反序列化

    与序列化相对的是反序列化,它将流转换为对象,在序列化与反序列化的过程中,serialVersionUID起着重要的作用,每一个类都有一个特定的serialVersionUID,在反序列化的过程中通过serialVersionUID判定类的兼容性,自定义serialVersionUID主要由如下3个优点。

    1. 提高程序运行效率。如果在类中未显示声明serialVersionUID,那么在序列化时会通过计算得到一个serialVersionUID。通过显示声明serialVersionUID的方式省去了计算的过程,提高了程序效率。
    2. 提高程序不同平台上的兼容性。由于各个平台计算serialVersionUID的方式可能不同,通过显示的方式可以完全避免该问题。
    3. 增强程序各个版本的可兼容性。在默认情况下每个类都有唯一的一个serialVersionUID,因此当后期对类进行修改时,类的serialVersionUID值将会发生变化,这将会导致类在修改前对象的文件在修改后无法进行反序列化操作。同样通过显示声明serialVersionUID也会解决该问题。

    最后想说明,反序列化也是Java创建对象的一种方式,其他的还有new 类名()、通过clone()创建对象、通过反射机制创建对象

  • 相关阅读:
    redis数据类型
    golang的select实现原理剖析
    goroutine的设计与实现
    go语言的duck typing
    go语言的局部变量在堆上还是栈上?
    REDIS学习
    C++11右值引用
    C++自问
    go语言interface学习
    go语言学习(基本数据类型)
  • 原文地址:https://www.cnblogs.com/zhanglei93/p/5890212.html
Copyright © 2020-2023  润新知