如果不指定 serialVersionUID ,当你添加或者修改类中的任何字段时,已序列化类将无法恢复。因为新类和旧序列化对象生成的serialVersionUID 不同,序列化的过程将依赖正确的序列化对象恢复状态的。否则会报错 java.io.InvalidClassException 。
初始代码: 指定了 serialVersionUID 的情况下
serialVersionUID import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class Person implements Serializable {
/**
* 不指定 serialVersionUID的后果是:当你添加或修改类中的任何字段时,已序列化类将无法恢复,因为新类和旧序列化对象生成的
* serialVersionUID 将有所不同。Java 序列化的过程是依赖于正确的序列化对象恢复状态的,并在序列化对象序列版本不匹配的情况下引发
* java.io.InvalidClassException 无效类异常。
*/
private static final long serialVersionUID = 1L;// 7552393449144317442L;
private String name;
private String password;
private int age;
public static int grade;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + ", grade=" + grade + '}';
}
}
public class WriteObject {
public static void main(String[] args) {
try {
// 序列化步骤,新建一个 ObjectOutputStream 对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
Person person = new Person("xiaoming", 19); // 序列化 写入对象
person.grade = 2;
oos.writeObject(person);
// 反序列化 新建一个ObjectInputStream
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
// 反序列化 读取
Person per = (Person) ois.readObject();
System.out.println(per);
} catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果
Person{name='xiaoming', age=19, grade=2}
serialVersionUID 不变,加个字段,看是否能够能够读出
package JavaBase.serializable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class Person implements Serializable {
/**
* 不指定 serialVersionUID的后果是:当你添加或修改类中的任何字段时,已序列化类将无法恢复,因为新类和旧序列化对象生成的
* serialVersionUID 将有所不同。Java 序列化的过程是依赖于正确的序列化对象恢复状态的,并在序列化对象序列版本不匹配的情况下引发
* java.io.InvalidClassException 无效类异常。
*/
private static final long serialVersionUID = 1L;// 7552393449144317442L;
private String name;
private String password;
private int age;
public static int grade;
public int i;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + ", grade=" + grade + '}';
}
}
public class WriteObject {
public static void main(String[] args) {
try {
// 序列化步骤,新建一个 ObjectOutputStream 对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
Person person = new Person("xiaoming", 19); // 序列化 写入对象
person.grade = 2;
oos.writeObject(person);
// 反序列化 新建一个ObjectInputStream
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
// 反序列化 读取
Person per = (Person) ois.readObject();
System.out.println(per);
} catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果:
Person{name='xiaoming', age=19, grade=2}
变量不变,改变 serialVersionUID = 2L 看是否能解析出, 答案是会报错
package JavaBase.serializable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class Person implements Serializable {
/**
* 不指定 serialVersionUID的后果是:当你添加或修改类中的任何字段时,已序列化类将无法恢复,因为新类和旧序列化对象生成的
* serialVersionUID 将有所不同。Java 序列化的过程是依赖于正确的序列化对象恢复状态的,并在序列化对象序列版本不匹配的情况下引发
* java.io.InvalidClassException 无效类异常。
*/
private static final long serialVersionUID = 2L;// 7552393449144317442L;
private String name;
private String password;
private int age;
public static int grade;
public int i;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + ", grade=" + grade + '}';
}
}
public class WriteObject {
public static void main(String[] args) {
try {
/* // 序列化步骤,新建一个 ObjectOutputStream 对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
Person person = new Person("xiaoming", 19); // 序列化 写入对象
person.grade = 2;
oos.writeObject(person);
*/
// 反序列化 新建一个ObjectInputStream
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
// 反序列化 读取
Person per = (Person) ois.readObject();
System.out.println(per);
} catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果:
java.io.InvalidClassException: JavaBase.serializable.Person; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
at java.io.ObjectStreamClass.initNonProxy(Unknown Source)
at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
at java.io.ObjectInputStream.readClassDesc(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at JavaBase.serializable.WriteObject.main(WriteObject.java:56)
静态变量的序列化
序列换保存的对象的状态,静态变量属于类的状态,序列化并不保存静态变量。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* 类说明 最后的输出是 10,对于无法理解的读者认为,打印的 staticVar 是从读取的对象里获得的,应该是保存时的状态才对。之所以打印 10
* 的原因在于序列化时,并不保存静态变量,这其实比较容易理解,序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。
*
*/
public class TestStaticSerializable implements Serializable {
private static final long serialVersionUID = 1L;
public static int staticVar = 5;
public static void main(String[] args) {
try {
// 初始时staticVar为5
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("result.obj"));
// 序列化后修改为10
TestStaticSerializable.staticVar = 10;
out.writeObject(new TestStaticSerializable());
out.close();
ObjectInputStream oin = new ObjectInputStream(new FileInputStream("result.obj"));
TestStaticSerializable t = (TestStaticSerializable) oin.readObject();
oin.close();
// 再读取,通过t.staticVar打印新的值
System.out.println(t.staticVar);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出的结果是 10 而不是 5 ,序列化保存的是对象的状态,并不保存(静态变量)类状态
父类序列化和 Transient 关键字
Transient 关键字的作用是控制变量的序列化,在关键字前加上该关键字,可以阻止变量被序列化到文件中,反序列化之后,trasnient 变量的值被设为初始值,int 类型的是 0 ,对象型的是 null。
一个子类实现了Serializable 接口,父类都没有实现 Serializable 接口,序列化子类对象,然后反序列化后输出父类定义的某个变量的值,改变量值与序列化时的数值是不同的。
父类:
public class Parent {
private String attr1;
private String attr2;
private String attr3;
public Parent() {
}
public Parent(String attr1, String attr2, String attr3) {
this.attr1 = attr1;
this.attr2 = attr2;
this.attr3 = attr3;
}
public String getAttr1() {
return attr1;
}
public void setAttr1(String attr1) {
this.attr1 = attr1;
}
public String getAttr2() {
return attr2;
}
public void setAttr2(String attr2) {
this.attr2 = attr2;
}
public String getAttr3() {
return attr3;
}
public void setAttr3(String attr3) {
this.attr3 = attr3;
}
}
子类:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
public class Child extends Parent implements Serializable {
private String attr4;
private transient String attr5;
public String getAttr4() {
return attr4;
}
public void setAttr4(String attr4) {
this.attr4 = attr4;
}
public String getAttr5() {
return attr5;
}
public void setAttr5(String attr5) {
this.attr5 = attr5;
}
public Child(String attr1, String attr2, String attr3, String attr4, String attr5) {
super(attr1, attr2, attr3);
this.attr4 = attr4;
this.attr5 = attr5;
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
File file = new File("D:" + File.separator + "s.txt");
OutputStream os = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(new Child("str1", "str2", "str3", "str4", "str5"));
oos.close();
InputStream is = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(is);
Child so = (Child) ois.readObject();
System.out.println("str1 = " + so.getAttr1());
System.out.println("str2 = " + so.getAttr2());
System.out.println("str3 = " + so.getAttr3());
System.out.println("str4 = " + so.getAttr4());
System.out.println("str5 = " + so.getAttr5());
ois.close();
}
}
执行结果:
str1 = null
str2 = null
str3 = null
str4 = str4
str5 = null