序列化问题总结
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的作用就是进行序列化字段属性的校验,当序列化和反序列化的字段不一样时,禁止进行反序列化。