JAVA序列化

来源:互联网 发布:攻城狮和程序员的段子 编辑:程序博客网 时间:2024/06/04 00:59

序列化的一般过程:

  1. 将对象实例相关的类元数据输出
  2. 递归的输出类的超类描述直到不再有超类
  3. 从最顶层的超类开始输出对象实例的实际数据
  4. 从上至下递归输出实例的数据

通过以下例子来验证以上四个过程是否正确

class father {String nameFather = "nameFather";}class sun extends father implements Serializable{public String name = "nameSun";}class suner extends sun{}suner test;System.out.println(test.nameFather);
输出结果

nameFather
输出解释:

1.在序列化的过程中,不管是子类实现了序列化接口,还是父类实现了序列化接口,子类在序列化时,都会将父类与子类的属性写入流中。

2.查看序列化的文件内容可知,序列化时,从顶层超类到本类输出类描述,然后将各属性的值写入流(属性值按照从父类到子类的顺序进行)。

对象包含其他对象

class sun implements Serializable {public suner s = new suner();}class suner implements Serializable{public String name = "nameSunner";public int age = 10;}
序列化sun,如果suner 没有实现Serializable接口,会抛出NotSerializableException异常。

此外反序列化时,sun必须有一个无参的构造函数。因为在反序列化sun时,会先产生一个 sun s = new sun() 然后通过反射机制填充其属性值。


单例模式的Serializable

考虑到Singleton模式下,一个类只能有一个实例。可是,通过反序列化,可以创建多个不同的实例。为了防止这一点,可以在类中添加readResolve方法

import java.io.*;public class product {public static void main(String[] args) {try {//序列化test1ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("a.txt"));user test1 = user.getInstance();out.writeObject(test1);out.close();test1.name = "nameChange";ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("a.txt"));user test2 = (user) inputStream.readObject();inputStream.close();//判断是否是同一个对象System.out.println(test1 == test2);//输出应该是 nameChangeSystem.out.println(test2.name);} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}}}class user implements Serializable {private static final long serialVersionUID = -3458656943762485150L;public String name = "nameInit";public int age = 20;private static user u = new user();private user() {}public static user getInstance() {return u == null ? new user() : u;}private Object readResolve() throws ObjectStreamException {System.out.println("call readResolve");return u;}}
输出结果

call readResolvetruenameChange

输出解释:

1.反序化时,自动调用了readResolve方法。

2.因为test1和test3都是指向 user.u这个对象,因此,相等。

3.因为test1和test3都是指向 user.u这个对象,因此修改test1,就等同于在修改u。

综上所述,readResolve方法的确能保证Singleton模式的正确性。

Serializable的选择性序列化

将需要的属性序列化,不需要的则可以去掉,比如static变量(static在反序列化之后,使用的是jvm中当前的值,因此static属性的序列化在大多数时候没有用处)。

在不需要序列化的字段前加上transient。例如

transient public String name = "myname";

但是这种设计并不灵活,如果一个类被组合到多个其它的类中,而其他类需要序列化的字段都不相同,那只能呵呵了。

Externalizable是Serializable的子接口。一个类在实现Externalizable时,需要实现两个方法writeExternal和readExternal。

class sun implements Externalizable {public suner s = new suner();@Overridepublic void writeExternal(ObjectOutput out) throws IOException {// TODO Auto-generated method stubout.writeObject(s.name);}@Overridepublic void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {// TODO Auto-generated method stubs.name = (String) in.readObject();}}class suner implements Serializable{public String name = "nameSunner";public int age = 10;}

writeExternal中可以将需要的属性序列化到流中,此外还可以进行一个额外的操作,比如加密、回收内存等等。

综上所述,如果一个类需要在序列化时,进行一些额外的操作,或者不需要序列化所有属性时,可用Externalizable来完成所需要的功能。

Externalizable奇特的地方

Externalizable有一个奇怪的地方。在反序列化时,类必须具有一个public的无参构造函数。但是Serializable完全没有这种问题。

注:若一个类不是public,则默认的构造函数是frienly。

import java.io.*;public class product {public static void main(String[] args) {try {ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("a.txt"));sun test2 = (sun) inputStream.readObject();System.out.println(test2.name);inputStream.close();} catch (Exception e) {e.printStackTrace();}}}class sun implements Externalizable {public String name = "myname";@Overridepublic void writeExternal(ObjectOutput out) throws IOException {// TODO Auto-generated method stubout.writeObject(name);}@Overridepublic void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {// TODO Auto-generated method stubname = (String) in.readObject();}}
异常信息:

java.io.InvalidClassException: bishi.sun; bishi.sun; no valid constructorat java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:713)at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1733)at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)at bishi.product.main(product.java:13)Caused by: java.io.InvalidClassException: bishi.sun; no valid constructorat java.io.ObjectStreamClass.<init>(ObjectStreamClass.java:471)at java.io.ObjectStreamClass.lookup(ObjectStreamClass.java:310)at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1106)at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)at bishi.product.main(product.java:10)

如果添加public构造函数则正常输出。


0 0