序列化问题总结

2020-01-28  本文已影响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的作用就是进行序列化字段属性的校验,当序列化和反序列化的字段不一样时,禁止进行反序列化。

上一篇下一篇

猜你喜欢

热点阅读