美文网首页
序列化问题总结

序列化问题总结

作者: 定金喜 | 来源:发表于2020-01-28 15:26 被阅读0次

    1.序列化方式

    a.实现Serializable接口,该接口是一个空接口,标识该类是否可序列化,下面为一个序列化的简单例子:

    package com.dxc;
    
    import java.io.*;
    import java.util.Date;
    
    /**
     * @Author: ding
     * @Date: 2020-01-26 22:23
     */
    @Data
    public class Student implements Serializable {
    
        /**
         * 学号
         */
        private static Integer id;
    
        /**
         * 姓名
         */
        private String name;
    
        /**
         * 年级
         */
        private String grade;
    
        /**
         * 班级职务
         */
        private String position;
    
        /**
         * 入学时间
         */
        private Date entranceTime;
    
        public static Integer getId() {
            return id;
        }
    
        public static void setId(Integer id) {
            Student.id = id;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", grade='" + grade + '\'' +
                    ", position='" + position + '\'' +
                    ", entranceTime=" + entranceTime +
                    '}';
        }
    
        public static void main(String[] args) throws Exception {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dxc.txt"));
    
            //将对象序列化到文件
            Student student = new Student();
            Student.setId(123456);
            student.setGrade("高三二班");
            student.setName("张三");
            student.setPosition("班长");
            student.setEntranceTime(new Date());
            oos.writeObject(student);
    
            //反序列化
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dxc.txt"));
            Student student1 = (Student) ois.readObject();
            System.out.println(student1);
        }
    }
    

    执行结果:

    Student{id=123456, name='张三', grade='高三二班', position='班长', entranceTime=Sun Jan 26 23:20:30 CST 2020}
    

    b.实现接口Externalizable,该接口源码如下:

    /*
     * Copyright (c) 1996, 2004, Oracle and/or its affiliates. All rights reserved.
     * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
     *
     */
    package java.io;
    
    import java.io.ObjectOutput;
    import java.io.ObjectInput;
    
    /**
     * Only the identity of the class of an Externalizable instance is
     * written in the serialization stream and it is the responsibility
     * of the class to save and restore the contents of its instances.
     *
     * The writeExternal and readExternal methods of the Externalizable
     * interface are implemented by a class to give the class complete
     * control over the format and contents of the stream for an object
     * and its supertypes. These methods must explicitly
     * coordinate with the supertype to save its state. These methods supersede
     * customized implementations of writeObject and readObject methods.<br>
     *
     * Object Serialization uses the Serializable and Externalizable
     * interfaces.  Object persistence mechanisms can use them as well.  Each
     * object to be stored is tested for the Externalizable interface. If
     * the object supports Externalizable, the writeExternal method is called. If the
     * object does not support Externalizable and does implement
     * Serializable, the object is saved using
     * ObjectOutputStream. <br> When an Externalizable object is
     * reconstructed, an instance is created using the public no-arg
     * constructor, then the readExternal method called.  Serializable
     * objects are restored by reading them from an ObjectInputStream.<br>
     *
     * An Externalizable instance can designate a substitution object via
     * the writeReplace and readResolve methods documented in the Serializable
     * interface.<br>
     *
     * @author  unascribed
     * @see java.io.ObjectOutputStream
     * @see java.io.ObjectInputStream
     * @see java.io.ObjectOutput
     * @see java.io.ObjectInput
     * @see java.io.Serializable
     * @since   JDK1.1
     */
    public interface Externalizable extends java.io.Serializable {
        /**
         * The object implements the writeExternal method to save its contents
         * by calling the methods of DataOutput for its primitive values or
         * calling the writeObject method of ObjectOutput for objects, strings,
         * and arrays.
         *
         * @serialData Overriding methods should use this tag to describe
         *             the data layout of this Externalizable object.
         *             List the sequence of element types and, if possible,
         *             relate the element to a public/protected field and/or
         *             method of this Externalizable class.
         *
         * @param out the stream to write the object to
         * @exception IOException Includes any I/O exceptions that may occur
         */
        void writeExternal(ObjectOutput out) throws IOException;
    
        /**
         * The object implements the readExternal method to restore its
         * contents by calling the methods of DataInput for primitive
         * types and readObject for objects, strings and arrays.  The
         * readExternal method must read the values in the same sequence
         * and with the same types as were written by writeExternal.
         *
         * @param in the stream to read data from in order to restore the object
         * @exception IOException if I/O errors occur
         * @exception ClassNotFoundException If the class for an object being
         *              restored cannot be found.
         */
        void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
    }
    

    该接口继承Serializable,并且有两个接口方法writeExternal和readExternal,writeExternal接口定义哪些属性可以序列化,readExternal根据序列顺序挨个读取进行反序列化,这两个接口序列和反序列属性顺序必须一致,例子如下:

    package com.dxc;
    
    import java.io.*;
    import java.util.Date;
    
    /**
     * @Author: ding
     * @Date: 2020-01-26 23:24
     */
    @Data
    public class Teacher implements Externalizable {
    
        /**
         * 编号
         */
        private static Integer id;
    
        /**
         * 姓名
         */
        private String name;
    
        /**
         * 级别
         */
        private String level;
    
        /**
         * 入职时间
         */
        private Date employeeTime;
    
        public static Integer getId() {
            return id;
        }
    
        public static void setId(Integer id) {
            Student.id = id;
        }
    
        @Override
        public String toString() {
            return "Teacher{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", level='" + level + '\'' +
                    ", employeeTime=" + employeeTime +
                    '}';
        }
    
        @Override
        public void writeExternal(ObjectOutput out) throws IOException {
    
            out.writeObject(123456);
            out.writeObject("于巧萍");
            out.writeObject("特级");
            out.writeObject(new Date());
        }
    
        @Override
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            id = (Integer) in.readObject();
            name = (String)in.readObject();
            level = (String)in.readObject();
            employeeTime = (Date)in.readObject();
        }
    
        public static void main(String[] args) throws Exception{
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dxc.txt"));
    
            //将对象序列化到文件
            Teacher teacher = new Teacher();
            oos.writeObject(teacher);
    
            //反序列化
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dxc.txt"));
            Teacher teacher1 = (Teacher) ois.readObject();
            System.out.println(teacher1);
        }
    }
    

    执行结果:

    Teacher{id=123456, name='于巧萍', level='特级', employeeTime=Sun Jan 26 23:37:19 CST 2020}
    

    2.哪些字段不能序列化

    a.静态字段,验证例子如下:

    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dxc.txt"));
    
            //将对象序列化到文件
            Student student = new Student();
            Student.setId(123456);
            student.setGrade("高三二班");
            student.setName("张三");
            student.setPosition("班长");
            student.setEntranceTime(new Date());
            oos.writeObject(student);
    
            Student.setId(500021);
    
            //反序列化
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dxc.txt"));
            Student student1 = (Student) ois.readObject();
            System.out.println(student1);
    

    执行结果:

    Student{id=500021, name='张三', grade='高三二班', position='班长', entranceTime=Sun Jan 26 23:45:05 CST 2020}
    

    b.被transient修饰的字段
    Student类id字段增加transient修饰,而且设置为非static

    package com.dxc;
    
    import lombok.Data;
    
    import java.io.*;
    import java.util.Date;
    
    /**
     * @Author: ding
     * @Date: 2020-01-26 22:23
     */
    @Data
    public class Student implements Serializable {
    
        /**
         * 学号
         */
        private transient Integer id;
    
        /**
         * 姓名
         */
        private String name;
    
        /**
         * 年级
         */
        private String grade;
    
        /**
         * 班级职务
         */
        private String position;
    
        /**
         * 入学时间
         */
        private Date entranceTime;
    
    
        @Override
        public String toString() {
            return "Student{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", grade='" + grade + '\'' +
                    ", position='" + position + '\'' +
                    ", entranceTime=" + entranceTime +
                    '}';
        }
    
        public static void main(String[] args) throws Exception {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dxc.txt"));
    
            //将对象序列化到文件
            Student student = new Student();
            student.setId(123456);
            student.setGrade("高三二班");
            student.setName("张三");
            student.setPosition("班长");
            student.setEntranceTime(new Date());
            oos.writeObject(student);
    
            //反序列化
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dxc.txt"));
            Student student1 = (Student) ois.readObject();
            System.out.println(student1);
        }
    }
    

    执行结果:

    Student{id=null, name='张三', grade='高三二班', position='班长', entranceTime=Sun Jan 26 23:56:32 CST 2020}
    

    可以看到id字段未序列化成功

    对Teacher字段id增加transient

    package com.dxc;
    
    import lombok.Data;
    
    import java.io.*;
    import java.util.Date;
    
    /**
     * @Author: ding
     * @Date: 2020-01-26 23:24
     */
    @Data
    public class Teacher implements Externalizable {
    
        /**
         * 编号
         */
        private transient Integer id;
    
        /**
         * 姓名
         */
        private String name;
    
        /**
         * 级别
         */
        private String level;
    
        /**
         * 入职时间
         */
        private Date employeeTime;
    
        @Override
        public String toString() {
            return "Teacher{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", level='" + level + '\'' +
                    ", employeeTime=" + employeeTime +
                    '}';
        }
    
        @Override
        public void writeExternal(ObjectOutput out) throws IOException {
    
            out.writeObject(123456);
            out.writeObject("于巧萍");
            out.writeObject("特级");
            out.writeObject(new Date());
        }
    
        @Override
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            id = (Integer) in.readObject();
            name = (String)in.readObject();
            level = (String)in.readObject();
            employeeTime = (Date)in.readObject();
        }
    
        public static void main(String[] args) throws Exception{
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dxc.txt"));
    
            //将对象序列化到文件
            Teacher teacher = new Teacher();
            oos.writeObject(teacher);
    
            //反序列化
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dxc.txt"));
            Teacher teacher1 = (Teacher) ois.readObject();
            System.out.println(teacher1);
        }
    }
    

    执行结果:

    Teacher{id=123456, name='于巧萍', level='特级', employeeTime=Mon Jan 27 00:01:48 CST 2020}
    

    执行结果不变,所以增加transient对第二种序列化方式无效

    3. serialVersionUID对序列化的作用

    如果不显示指定serialVersionUID的值,那么会根据所有属性值计算出一个非固定的serialVersionUID值,所以在增加字段时,反序列化就会报错,例子如下:

    package com.dxc;
    
    import lombok.Data;
    
    import java.io.*;
    import java.util.Date;
    
    /**
     * @Author: ding
     * @Date: 2020-01-26 22:23
     */
    @Data
    public class Student implements Serializable {
    
        /**
         * 学号
         */
        private Integer id;
    
        /**
         * 姓名
         */
        private String name;
    
        /**
         * 年级
         */
        private String grade;
    
        /**
         * 班级职务
         */
        private String position;
    
        /**
         * 入学时间
         */
        private Date entranceTime;
    
        /**
         * 数学老师姓名
         */
        private String mathTeacherName;
    
    
        @Override
        public String toString() {
            return "Student{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", grade='" + grade + '\'' +
                    ", position='" + position + '\'' +
                    ", entranceTime=" + entranceTime +
                    '}';
        }
    
        public static void main(String[] args) throws Exception {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dxc.txt"));
    
            //将对象序列化到文件,不增加mathTeacherName字段之前
            Student student = new Student();
            student.setId(123456);
            student.setGrade("高三二班");
            student.setName("张三");
            student.setPosition("班长");
            student.setEntranceTime(new Date());
            oos.writeObject(student);
    
            //反序列化,增加mathTeacherName字段之后
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dxc.txt"));
            Student student1 = (Student) ois.readObject();
            System.out.println(student1);
        }
    }
    

    Student类先不增加字段mathTeacherName进行序列化输出文件dxc.txt,然后增加字段后进行反序列化,执行结果为:

    
    Exception in thread "main" java.io.InvalidClassException: com.dxc.Student; local class incompatible: stream classdesc serialVersionUID = -379283038728503294, local class serialVersionUID = -77880049358684336
        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 com.dxc.Student.main(Student.java:71)
    

    如果指定一个固定的serialVersionUID值就会正常输出,一般idea自带serialVersionUID生成工具。所以serialVersionUID的作用就是进行序列化字段属性的校验,当序列化和反序列化的字段不一样时,禁止进行反序列化。

    相关文章

      网友评论

          本文标题:序列化问题总结

          本文链接:https://www.haomeiwen.com/subject/utssthtx.html