说明:本文为Serializable详解(1),最后两段内容在翻译上出现歧义(暂时未翻译),将在后续的Serializable(2)文中补充。
介绍:本文根据JDK英文文档翻译而成,本译文并非完全按照原文档字面文字直译,而是结合文档内容及个人经验翻译成更为清晰和易于理解的文字,并附加代码验证,帮助大家更好地理解Serializable。
性质:接口类
package java.io
public interface Serializable
Serializability of a class is enabled by the class implementing the java.io.Serializable interface.
通过实现java.io.Serializable interface接口来序列化一个类。
Classes that do not implement this interface will not have any of their state serialized or deserialized.
没有实现此接口的类任何状态都不会序列化或反序列化。
All subtypes of a serializable class are themselves serializable.
可序列化类的所有子类而本身都是可序列化的。
The serialization interface has no methods or fields and serves only to identify the semantics of being serializable.
序列化接口没有方法或字段域,它仅用来标识可序列化的语义。
(1)To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype’s public, protected, and (if accessible) package fields.
(2)The subtype may assume this responsibility only if the class it extends has an accessible no-arg constructor to initialize the class’s state. It is an error to declare a class Serializable if this is not the case. The error will be detected at runtime.
(3)During deserialization, the fields of non-serializable classes will be initialized using the public or protected no-arg constructor of the class. A no-arg constructor must be accessible to the subclass that is serializable. The fields of serializable subclasses will be restored from the stream.
(1)为了让非序列化类的子类可以被序列化,这个子类可以承担保存和恢复超类的pulic,protected,package字段(如果可访问的话)。
(2)只有当它拓展的类具有可访问无参构造函数来初始化类的状态时,子类才可以承担这样的责任。如果不是这种情况,就不能声明一个类是可序列化的。这个错误将在运行的时候被检测出来。
(3)在反序列化期间,非序列化类的字段将通过类的以public或者protected修饰的空参构造函数实例化。无参数构造函数必须可访问可序列化的子类。序列化子类的字段能够从字符流里被还原。
(1)(2)(3)三块主要说了三件事:
辅以A/B两套类型代码对比理解:
父类:Biology 类
package com.springboot.SpringBootDemo.serializable;
public class Biology {
public String type;
private int num;
public Biology(String type, int num) {
this.type = type;
this.num = num;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
子类:People 类
package com.springboot.SpringBootDemo.serializable;
import java.io.Serializable;
public class People extends Biology implements Serializable{
private static final long serialVersionUID = -6623611040000763479L;
public String name;
protected String gender;
private int age;
public People(String type, int num, String name ,String gender ,int age) {
super(type, num);
this.name = name;
this.gender = gender;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
测试类:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
People pp = new People("human",10000,"张三","男",25);
FileOutputStream fos = new FileOutputStream("test.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(pp);
oos.flush();
oos.close();
//反序列化
FileInputStream sfis = new FileInputStream("test.txt");
ObjectInputStream sois = new ObjectInputStream(sfis);
People p = (People) sois.readObject();
System.out.println(
p.getType() +" "+
p.getNum() +" "+
p.getName() +" "+
p.getGender() +" "+
p.getAge()
);
}
}
结果:
Exception in thread "main" java.io.InvalidClassException: com.springboot.SpringBootDemo.serializable.People; no valid constructor
at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(Unknown Source)
at java.io.ObjectStreamClass.checkDeserialize(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at com.springboot.SpringBootDemo.serializable.Test.main(Test.java:23)
结果说明:在序列化时未发生异常,而在反序列化readObject()时发生异常。也就是说,父类没有无参构造函数时,序列化正常进行,但反序列化时抛出newInvalidClassException异常。
父类:Person类
public class Person {
public String name;
public String gender;
public int age;
float height;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public float getHeight() {
return height;
}
public void setHeight(float height) {
this.height = height;
}
}
子类:Male类
import java.io.Serializable;
public class Male extends Person implements Serializable{
/**
*
*/
private static final long serialVersionUID = -7361904256653535728L;
public boolean beard;
protected String weight;
public boolean havaBeard(int age){
boolean flag = false;
if(age>=18){
flag = true;
}
return flag;
}
public boolean isBeard() {
return beard;
}
public void setBeard(boolean beard) {
this.beard = beard;
}
public String getWeight() {
return weight;
}
public void setWeight(String weight) {
this.weight = weight;
}
}
测试类:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SubTypeSerializable {
public static void main(String[] args) throws IOException, ClassNotFoundException{
/**Male继承父类Person,自身实现序列化接口,其父类Person没有实现序列化接口*/
FileOutputStream fos = new FileOutputStream("male.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
Male male = new Male();
/**
* 其父类的父类Person的属性
*
* public String name;
public String gender;
public int age;
float height;
* */
male.setName("张三");
male.setGender("男性");
male.setAge(25);
male.setHeight(175);
/**
* 其自身属性
* public boolean beard;
* */
male.setBeard(true);
oos.writeObject(male);
oos.flush();
oos.close();
//反序列化
FileInputStream fis = new FileInputStream("male.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
Male ml = (Male) ois.readObject();
System.out.println(ml.getName() +" "+ml.getGender()+" "+ml.getHeight() +" "+ml.getAge()+" "+male.isBeard());
}
}
结果:
ml.getName() == null
ml.getGender() == null
ml.getHeight() == 0.0
ml.getAge() == 0
male.isBeard() == true
public String name;
public String gender;
public int age;
float height;
其状态信息均未被记录;
public boolean beard;
其状态信息被记录
When traversing a graph, an object may be encountered that does not support the Serializable interface. In this case the NotSerializableException will be thrown and will identify the class of the non-serializable object.
当循环遍历一个数据结构图(数据结构图可理解为数据结构类型,比如二叉树)的时候,对象可能会遭遇到不支持实现序列化接口的情景。在这种情况下,将发生抛出NotSerializableException异常,并且该类被定义为不可序列化类。
Classes that require special handling during the serialization and deserialization process must implement special methods with these exact signatures:
在实现序列化和反序列化过程中,特殊处理的类需要实现这些特殊的方法:
private void writeObject(java.io.ObjectOutputStream out) throws IOException
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;
private void readObjectNoData() throws ObjectStreamException;
The writeObject method is responsible for writing the state of the object for its particular class so that the corresponding readObject method can restore it. The default mechanism for saving the Object’s fields can be invoked by calling out.defaultWriteObject. The method does not need to concern itself with the state belonging to its superclasses or subclasses. State is saved by writing the individual fields to the ObjectOutputStream using the writeObject method or by using the methods for primitive data types supported by DataOutput.
witeObject方法负责写入特定类的Object对象状态信息,readObject方法可以还原该Object对象的状态信息。保存该Object对象字段的默认机制是通过调用out.defaultWriteObject来实现。该方法不需要关注属于其超类或子类的状态。通过使用writeObject方法将各个字段写入ObjectOutputStream,或使用DataOutput支持的基本数据类型的方法来保存状态。
The readObject method is responsible for reading from the stream and restoring the classes fields. It may call in.defaultReadObject to invoke the default mechanism for restoring the object’s non-static and non-transient fields. The defaultReadObject method uses information in the stream to assign the fields of the object saved in the stream with the correspondingly named fields in the current object. This handles the case when the class has evolved to add new fields. The method does not need to concern itself with the state belonging to its superclasses or subclasses. State is saved by writing the individual fields to the ObjectOutputStream using the writeObject method or by using the methods for primitive data types supported by DataOutput.
readObject方法是负责读取数据流并恢复该类的字段。它可以通过调用in.defaultReadObject来恢复非static以及非transient修饰的字段。defaultReadObject方法通过数据流中的信息,把当前类保存在数据流中的字段信息分配到相对应的字段名(也就是说把字段的值分配给相对应的字段名)。这种处理方式也能处理该类新增字段的情况。该方法不需要关注属于其超类或子类的状态。通过使用writeObject方法将各个字段写入ObjectOutputStream,或使用DataOutput支持的基本数据类型的方法来保存状态。通过writeObject方法把对象Object的各个字段写入到ObjectOutputStream中,或者通过使用DataOutput支持的基本数据类型的方法来保存该Object对象的状态信息。
The readObjectNoData method is responsible for initializing the state of the object for its particular class in the event that the serialization stream does not list the given class as a superclass of the object being deserialized. This may occur in cases where the receiving party uses a different version of the deserialized instance’s class than the sending party, and the receiver’s version extends classes that are not extended by the sender’s version. This may also occur if the serialization stream has been tampered; hence, readObjectNoData is useful for initializing deserialized objects properly despite a “hostile” or incomplete source stream.
(该处翻译有些吃力,所以直接软件翻译,会后续进行代码验证体悟)
当出现反序列化与序列化类的版本不一致的情况时,readObjectNoData()标签方法负责初始化对象的字段值。这种情况可能发生在反序列化时,接收方使用了发送方对象的类的不同版本,或者接收方继承的类的版本与发送方继承的类的版本不一致。另外,当序列化流被篡改了,也会发生这种情况。因此,当出现类不一致或者反序列化流不完全的情况时,readObjectNoData初始化反序列化对象的字段就非常有用了。
public class Cat implements Serializable{
/**
*
*/
private static final long serialVersionUID = -5731096200028489933L;
public String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
改变之前测试类:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class CatFamilylTest {
public static void main(String[] args) throws Exception {
serializable();
deSerializable();
}
public static void serializable() throws Exception{
Cat cat = new Cat();
cat.setColor("white");
FileOutputStream fos = new FileOutputStream("catFamily.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(cat);
oos.flush();
oos.close();
}
public static void deSerializable() throws Exception{
FileInputStream sfis = new FileInputStream("catFamily.txt");
ObjectInputStream sois = new ObjectInputStream(sfis);
Cat cat = (Cat) sois.readObject();
System.out.println(cat.getColor());
}
}
结果:white
第一次改变之增加父类:
import java.io.Serializable;
public class CatFamily implements Serializable{
/**
*
*/
private static final long serialVersionUID = -7796480232179180594L;
public String catType;
public String getCatType() {
return catType;
}
public void setCatType(String catType) {
this.catType = catType;
}
private void readObjectNoData() {
this.catType = "tiger";
}
}
第一次改变之后之Cat类变化:
public class Cat extends CatFamily{
/**
*
*/
private static final long serialVersionUID = -5731096200028489933L;
public String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
第一次改变之读取已经存在的catFamily.txt文件:
public class CatFamilylTest {
public static void main(String[] args) throws Exception {
deSerializable();
}
public static void deSerializable() throws Exception{
FileInputStream sfis = new FileInputStream("catFamily.txt");
ObjectInputStream sois = new ObjectInputStream(sfis);
Cat cat = (Cat) sois.readObject();
System.out.println(cat.getColor()+" <---->"+cat.getCatType());
}
}
第一次改变之结果:white <---->tiger
第二次改变之父类:
public class CatFamily{
public String catType;
public String getCatType() {
return catType;
}
public void setCatType(String catType) {
this.catType = catType;
}
private void readObjectNoData() {
this.catType = "tiger";
}
}
第二次改变之Cat类:
import java.io.Serializable;
public class Cat extends CatFamily implements Serializable{
/**
*
*/
private static final long serialVersionUID = -5731096200028489933L;
public String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
第二次改变之测试类:
public class CatFamilylTest {
public static void main(String[] args) throws Exception {
deSerializable();
}
public static void deSerializable() throws Exception{
FileInputStream sfis = new FileInputStream("catFamily.txt");
ObjectInputStream sois = new ObjectInputStream(sfis);
Cat cat = (Cat) sois.readObject();
System.out.println(cat.getColor()+" <---->"+cat.getCatType());
}
}
第二次改变之结果:white <---->null
第三次改变之抛弃父类,且Cat类改变:
import java.io.Serializable;
public class Cat implements Serializable{
/**
*
*/
private static final long serialVersionUID = -5731096200028489933L;
public String type;
public String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
private void readObjectNoData() {
this.type = "hellokitty";
}
}
第三次改变之测试类:
public class CatFamilylTest {
public static void main(String[] args) throws Exception {
deSerializable();
}
public static void deSerializable() throws Exception{
FileInputStream sfis = new FileInputStream("catFamily.txt");
ObjectInputStream sois = new ObjectInputStream(sfis);
Cat cat = (Cat) sois.readObject();
System.out.println(cat.getColor()+" <---->"+cat.getType());
}
}
第三次改变之测试结果:white <---->null
描述:建立实现序列化接口的Cat类,以及对应的测试类生产文件catFamily.txt
两个目的:
结果:反序列化catFamily.txt文件,得出正常结果 write 。
改变之处:
目的:验证readObjectNoData()标签方法结果。
结果:反序列化catFamily.txt文件,得出结果 white <---->tiger。
总结:实现readObjectNoData()标签方法。
改变之处:
目的:验证父类未实现序列化Serializable接口时,readObjectNoData()方法是否继续有效。
结果:反序列化catFamily.txt文件,得出结果 white <---->null 。
总结:readObjectNoData()方法没有得到体现。
改变之处:
目的:测试readObjectNoData()方法的作用域。
结果:反序列化catFamily.txt文件,得出结果 white <---->null。
总结:readObjectNoData()方法作用域为写入catFamily.txt文件的对象Object的实体类的实现序列化Serializable接口的父类。
Serializable classes that need to designate an alternative object to be used when writing an object to the stream should implement this special method with the exact signature:ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
实现序列化的类,其Object对象被指定另外一个实现序列化非此类的对象进行替换的时候,在进行把该实体Object对象写入到数据流中时,需要实现Object writeReplace() throws ObjectStreamException;这个特殊的标签方法。
注意:替换类和被替换类都需要实现序列化接口,否则在写入(writeObject)时会抛出java.io.NotSerializableException异常,且被替换类为彻底被替换。
实体类:
import java.io.ObjectStreamException;
import java.io.Serializable;
public class Dog implements Serializable{
/**
*
*/
private static final long serialVersionUID = -4094903168892128473L;
private String type;
private String color;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
private Object writeReplace() throws ObjectStreamException {
Wolf wolf = new Wolf();
wolf.setType(type);
wolf.setColor(color);
return wolf;
}
}
class Wolf implements Serializable{
/**
*
*/
private static final long serialVersionUID = -1501152003733531169L;
private String type;
private String color;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
测试类:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class DogTest {
public static void serializable() throws IOException{
Dog dog = new Dog();
dog.setColor("white");
dog.setType("Chinese garden dog");
FileOutputStream fos = new FileOutputStream("dog.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(dog);
oos.flush();
oos.close();
}
public static void deSerializable() throws IOException,ClassNotFoundException{
FileInputStream sfis = new FileInputStream("dog.txt");
ObjectInputStream sois = new ObjectInputStream(sfis);
Wolf wolf = (Wolf) sois.readObject();
System.out.println(wolf.getType() +"<------->"+ wolf.getColor());
}
public static void main(String[] args) throws IOException ,ClassNotFoundException{
serializable();
deSerializable();
}
}
代码实现结果:Chinese garden dog<------->white。
代码说明:实体类不修改,只修改测试类的反序列化方法,在readObject()方法时由Wolf对象转变为Dog对象。
public static void deSerializable() throws IOException,ClassNotFoundException{
FileInputStream sfis = new FileInputStream("dog.txt");
ObjectInputStream sois = new ObjectInputStream(sfis);
Dog dog = (Dog) sois.readObject();
System.out.println(dog.getType() +"<------->"+ dog.getColor());
}
代码实现结果:
(
第25行:Dog dog = (Dog) sois.readObject();
第32行:deSerializable();
)
Exception in thread "main" java.lang.ClassCastException: com.springboot.SpringBootDemo.serializable.Wolf cannot be cast to com.springboot.SpringBootDemo.serializable.Dog
at com.springboot.SpringBootDemo.serializable.DogTest.deSerializable(DogTest.java:25)
at com.springboot.SpringBootDemo.serializable.DogTest.main(DogTest.java:32)
序列化对象为Dog对象,而反序列化依然通过Dog对象,结果发生异常,此时可知在序列化时Dog对象被Wolf对象给替换了。
This writeReplace method is invoked by serialization if the method exists and it would be accessible from a method defined within the class of the object being serialized. Thus, the method can have private,protected and package-private access. Subclass access to this method follows java accessibility rules.
在序列化对象时,其类的方法中如果有writeReplace标签方法存在的话,则该标签方法会在序列化写入时被调用。因此该方法可以具有private,protected和package-private访问。该类的子类访问该方法时会遵循java可访问性规则。
注意:
import java.io.ObjectStreamException;
import java.io.Serializable;
public class Dog implements Serializable{
/**
*
*/
private static final long serialVersionUID = -4094903168892128473L;
private String type;
private String color;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Object writeReplace() throws ObjectStreamException {
Wolf wolf = new Wolf();
wolf.setType(type);
wolf.setColor(color);
return wolf;
}
}
class ChineseGardenDog extends Dog {
private float height;
public float getHeight() {
return height;
}
public void setHeight(float height) {
this.height = height;
}
}
class Wolf implements Serializable{
/**
*
*/
private static final long serialVersionUID = -1501152003733531169L;
private String type;
private String color;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class DogTest {
public static void serializable() throws IOException{
ChineseGardenDog dog = new ChineseGardenDog();
dog.setColor("white");
dog.setType("Chinese garden dog");
dog.setHeight(55);
FileOutputStream fos = new FileOutputStream("dog.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(dog);
oos.flush();
oos.close();
}
public static void deSerializable() throws IOException,ClassNotFoundException{
FileInputStream sfis = new FileInputStream("dog.txt");
ObjectInputStream sois = new ObjectInputStream(sfis);
Wolf wolf = (Wolf) sois.readObject();
System.out.println(wolf.getType() +"<------->"+ wolf.getColor());
}
public static void main(String[] args) throws IOException ,ClassNotFoundException{
serializable();
deSerializable();
}
}
测试结果:Chinese garden dog<------->white。
Classes that need to designate a replacement when an instance of it is read from the stream should implement this special method with the exact signature.
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
This readResolve method follows the same invocation rules and accessibility rules as writeReplace.
当从数据流中读取一个实例的时候,指定替换的类需要实现此特殊方法。
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
readResolve标签方法遵循与writeReplace相同的调用规则和可访问性规则。
注:该方法的写入对象实例和读取对象实例为同一个对象(适用于单例模式)。
实体类:
import java.io.Serializable;
public class Mouse implements Serializable{
/**
*
*/
private static final long serialVersionUID = -8615238438948214201L;
private String name;
public static Mouse INSTANCE;
public static Mouse getInstance(){
if(INSTANCE == null){
INSTANCE = new Mouse();
}
return INSTANCE;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
测试类:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class MouseTest {
public static void serializable() throws IOException{
Mouse mouse= Mouse.getInstance();
mouse.setName("Jerry");
FileOutputStream fos = new FileOutputStream("mouse.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
System.out.println("写入对象hash值 = "+ mouse.hashCode());
oos.writeObject(mouse);
oos.flush();
oos.close();
}
public static void deSerializable() throws IOException,ClassNotFoundException{
FileInputStream sfis = new FileInputStream("mouse.txt");
ObjectInputStream sois = new ObjectInputStream(sfis);
Mouse mouse = (Mouse) sois.readObject();
System.out.println("读取对象hash值 = " +mouse.hashCode());
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
serializable();
deSerializable();
}
}
测试结果:
写入对象hash值 = 366712642
读取对象hash值 = 1096979270
实体类:
import java.io.ObjectStreamException;
import java.io.Serializable;
public class Mouse implements Serializable{
/**
*
*/
private static final long serialVersionUID = -8615238438948214201L;
private String name;
public static Mouse INSTANCE;
public static Mouse getInstance(){
if(INSTANCE == null){
INSTANCE = new Mouse();
}
return INSTANCE;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private Object readResolve() throws ObjectStreamException{
return INSTANCE;
}
}
测试结果:
写入对象hash值 = 366712642
读取对象hash值 = 366712642
推测:指定写入的对象实例和读取指定的对象实例为同一个。
The serialization runtime associates with each serializable class a version number, called a serialVersionUID, which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization. If the receiver has loaded a class for the object that has a different serialVersionUID than that of the corresponding sender’s class, then deserialization will result in an {@link InvalidClassException}. A serializable class can declare its own serialVersionUID explicitly by declaring a field named
“serialVersionUID”
that must be static, final, and of typelong
:ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculate a default serialVersionUID value for that class based on various aspects of the class, as described in the Java™ Object Serialization Specification. However, it is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected
InvalidClassException
s during deserialization. Therefore, to guarantee a consistent serialVersionUID value across different java compiler implementations, a serializable class must declare an explicit serialVersionUID value. It is also strongly advised that explicit serialVersionUID declarations use theprivate
modifier where possible, since such declarations apply only to the immediately declaring class–serialVersionUID fields are not useful as inherited members. Array classes cannot declare an explicit serialVersionUID, so they always have the default computed value, but the requirement for matching serialVersionUID values is waived for array classes.
父类:Person类
public class Person {
public String name;
public String gender;
public int age;
float height;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public float getHeight() {
return height;
}
public void setHeight(float height) {
this.height = height;
}
}
子类:Male类
import java.io.Serializable;
public class Male extends Person implements Serializable{
/**
*
*/
private static final long serialVersionUID = -7361904256653535728L;
public boolean beard;
public boolean havaBeard(int age){
boolean flag = false;
if(age>=18){
flag = true;
}
return flag;
}
public boolean isBeard() {
return beard;
}
public void setBeard(boolean beard) {
this.beard = beard;
}
}
三级子类:Students类
public class Students extends Male{
private static final long serialVersionUID = -6982821977091370834L;
public String stuCard;
private int grades;
public String getStuCard() {
return stuCard;
}
public void setStuCard(String stuCard) {
this.stuCard = stuCard;
}
public int getGrades() {
return grades;
}
public void setGrades(int grades) {
this.grades = grades;
}
}
类:Female类
import java.io.Serializable;
public class Female implements Serializable{
private static final long serialVersionUID = 6907419491408608648L;
public String name;
public String gender;
public int age;
float height;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public float getHeight() {
return height;
}
public void setHeight(float height) {
this.height = height;
}
}
测试类:SubTypeSerializable
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SubTypeSerializable {
public static void main(String[] args) throws IOException, ClassNotFoundException{
/**(一)、Person实体类,未实现序列化接口,无父类*/
FileOutputStream fo = new FileOutputStream("person.txt");
ObjectOutputStream ss = new ObjectOutputStream(fo);
Person person = new Person();
person.setAge(100);
person.setGender("性别");
person.setHeight(165);
person.setName("人类");
ss.writeObject(person);
ss.flush();
ss.close();
//反序列化
FileInputStream sfis = new FileInputStream("person.txt");
ObjectInputStream sois = new ObjectInputStream(sfis);
Person ps = (Person) sois.readObject();
System.out.println(ps.getName() +" "+ps.getGender()+" "+ps.getHeight() +" "+ps.getAge());
/**结果:
在执行writeObject(person)是发生异常
Exception in thread "main" java.io.NotSerializableException:
com.springboot.SpringBootDemo.serializable.Person
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.writeObject(Unknown Source)
at com.springboot.SpringBootDemo.serializable.SubTypeSerializable.main(SubTypeSerializable.java:21)
Exception in thread "main" java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: com.springboot.SpringBootDemo.serializable.Person
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at com.springboot.SpringBootDemo.serializable.SubTypeSerializable.main(SubTypeSerializable.java:28)
* */
System.out.println("<--------------------------------------------------------------------------->");
/**(二)、Male继承父类Person,自身实现序列化接口,其父类Person没有实现序列化接口*/
FileOutputStream fos = new FileOutputStream("male.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
Male male = new Male();
/**
* 其父类的父类Person的属性
*
* public String name;
public String gender;
public int age;
float height;
* */
male.setName("张三");
male.setGender("男性");
male.setAge(25);
male.setHeight(175);
/**
* 其自身属性
* public boolean beard;
* */
male.setBeard(true);
oos.writeObject(male);
oos.flush();
oos.close();
//反序列化
FileInputStream fis = new FileInputStream("male.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
Male ml = (Male) ois.readObject();
System.out.println(ml.getName() +" "+ml.getGender()+" "+ml.getHeight() +" "+ml.getAge()+" "+male.isBeard());
/**结果:
* 父类没有被序列化,唯独子类属性被序列化
*
* ml.getName() == null
* ml.getGender() == null
* ml.getHeight() == 0.0
* ml.getAge() == 0
* male.isBeard() == true
* 父类属性:
* public String name;
public String gender;
public int age;
float height;
均未实现序列化;
自身属性:
public boolean beard;
实现序列化
* */
System.out.println("<--------------------------------------------------------------------------->");
/**(三)、Female实现序列化接口,无父类*/
FileOutputStream ffos = new FileOutputStream("female.txt");
ObjectOutputStream foos = new ObjectOutputStream(ffos);
Female female = new Female();
/**
* 其自身的属性
* public String name;
public String gender;
public int age;
float height;
**/
female.setAge(25);
female.setGender("女性");
female.setHeight(165);
female.setName("张芳");
foos.writeObject(female);
foos.flush();
foos.close();
//反序列化
FileInputStream ffis = new FileInputStream("female.txt");
ObjectInputStream fois = new ObjectInputStream(ffis);
Female fm = (Female) fois.readObject();
System.out.println(fm.getName() +" "+fm.getGender()+" "+fm.getHeight() +" "+fm.getAge());
/**结果:
* 自身属性均实现序列化
*
* fm.getName() == 张芳
* fm.getGender() == 女性
* fm.getHeight() == 165.0
* fm.getAge() == 25
* 所有属性均实现序列化*/
System.out.println("<--------------------------------------------------------------------------->");
/**(四)、Students未实现序列化接口,继承父类Male,其父类继承父类Person,自身实现序列化接口,其父类Person没有实现序列化接口*/
FileOutputStream stufos = new FileOutputStream("students.txt");
ObjectOutputStream stuoos = new ObjectOutputStream(stufos);
Students students = new Students();
/**
* 其父类的父类Person的属性
*
* public String name;
public String gender;
public int age;
float height;
* */
students.setName("王小明");
students.setGender("男性");
students.setAge(15);
students.setHeight(160);
/**
* 其父类Male属性
* public boolean beard;
* */
students.setBeard(true);
/**
* 自身属性
* public String stuCard;
private int grades;
* */
students.setStuCard("1234567890987");
students.setGrades(300);
stuoos.writeObject(students);
stuoos.flush();
stuoos.close();
//反序列化
FileInputStream stufis = new FileInputStream("students.txt");
ObjectInputStream stuois = new ObjectInputStream(stufis);
Students st = (Students) stuois.readObject();
System.out.println(st.getName() +" "+st.getGender()+" "+st.getAge()+" "+st.getHeight()+" "+st.isBeard()+" "+st.getStuCard()+" "+st.getGrades());
/**结果:
* 父类的父类属性未实现序列化,父类实现序列化,自身实现序列化
* st.getName() == null
* st.getGender() == null
* st.getAge() == 0
* st.getHeight() == 0.0
* st.isBeard() == true
* st.getStuCard() == 1234567890987
* st.getGrades() == 300
* 自身public String stuCard;
private int grades;
实现序列化;
而父类Male属性
public boolean beard
实现序列化;
父类的父类Person
public String name;
public String gender;
public int age;
float height;
未实现序列化
* */
}
}
1)在使用ObjectInputStream、ObjectOutputStream对对象进行写入写出时,其写入的对象的类需要实现java.io.Serializable序列化接口,否则会报出 writeObject()异常:
Exception in thread "main" java.io.NotSerializableException: com.springboot.SpringBootDemo.serializable.Person
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.writeObject(Unknown Source)
at com.springboot.SpringBootDemo.serializable.SubTypeSerializable.main(SubTypeSerializable.java:21)
readObject()异常:
Exception in thread "main" java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: com.springboot.SpringBootDemo.serializable.Person
java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at com.springboot.SpringBootDemo.serializable.SubTypeSerializable.main(SubTypeSerializable.java:28)
2)父类未实现java.io.Serializable序列化接口,其子类依然可以进行序列化,但其子类进行对象序列化读写时,父类无法被序列化,只能自身实现序列化;
3)自身实现java.io.Serializable序列化接口,在进行对象读写时会被实现序列化;
4)父类实现java.io.Serializable序列化接口,其子类不需要再次申明实现序列化,子类在进行对象序列化读写时,父类和子类均被实现序列化。
首先,Serializable类是一个接口,所以对象的序列化并不是Serializable来实现的;
其次,Serializable是一个标签,各种序列化类在读取到这个标签的时候,会按照自己的方式进行序列化。
我们知道,当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等,而这些数据都会以二进制序列的形式在网络上传送。
那么当两个Java进程进行通信时,能否实现进程间的对象传送呢?答案是可以的!如何做到呢?这就需要Java序列化与反序列化了!
换句话说:一方面,发送方需要把这个Java对象转换为字节序列,然后在网络上传送;另一方面,接收方需要从字节序列中恢复出Java对象。
当我们明晰了为什么需要Java序列化和反序列化后,我们很自然地会想Java序列化的好处。
作者:忠胜
首发:「野指针」
来源:宜信技术学院