序列化和反序列化是通过ObjectInputStream和ObjectOutputStream的readObject()和writeObject()实现的,序列化的过程是一个对象流状态保存的过程,这里什么叫对象流,可以理解为一系列的对象,因为本身一个对象的内部的字段都是一个个对象,实际上是通过“级联”的方式,保存跟此对象所有关联的对象的状态,实际上保存了跟此对象有关系的一张“对象网”。
反序列化是还原对象状态的过程,这种还原的过程可能在同一个应用中,可能在不同的应用中,可能在不同的主机上,还原的过程不是读出原来对象的字段值然后调用构造函数重新new一个对象,而是“直接地”反序列化为一个Object对象,并没有调用该类的构造函数,jvm也没有加载该类到方法区,还原后的对象若在不同的主机,想通过反射获得更多改对象的描述信息,必须保证JVM能在本地类路径或者因特网的其他什么地方找到相关的.class文件。
序列化也可以自己控制,使用Externalizable接口,此接口继承自Serializable接口,和Serializable不同的是,使用Externalizable接口,在恢复对象的时候是调用的该类的无参构造方法,若无参构造方法不是public的,在恢复对象的时候会抛出异常,下面的代码节选自Think in java
package externalizable;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
public class Blip1 implements Externalizable {
//public的构造方法,在回复对象的时候被调用。
public Blip1() {
System.out.println("Blip1 Constructor");
}
//在writeObject()方法的时候,会调用此方法
public void writeExternal(ObjectOutput out) throws IOException {
System.out.println("Blip1.writeExternal");
}
//在readObject()方法的时候,会调用此方法
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
System.out.println("Blip1.readExternal");
}
}
package externalizable;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
public class Blip2 implements Externalizable {
//此构造方法不是public的
Blip2() {
System.out.println("Blip2 Constructor");
}
public void writeExternal(ObjectOutput out) throws IOException {
System.out.println("Blip2.writeExternal");
}
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
System.out.println("Blip2.readExternal");
}
}
package externalizable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Blips {
public static void main(String[] args) {
System.out.println("Constructing objects:");
Blip1 b1 = new Blip1();
Blip2 b2 = new Blip2();
try {
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(
"Blips.out"));
System.out.println("Saving objects:");
o.writeObject(b1);
o.writeObject(b2);
o.close();
// Now get them back:
ObjectInputStream in = new ObjectInputStream(new FileInputStream(
"Blips.out"));
System.out.println("Recovering b1:");
b1 = (Blip1) in.readObject();
// OOPS! Throws an exception:
// !System.out.println("Recovering b2:");
// !b2 = (Blip2) in.readObject();
} catch (Exception e) {
e.printStackTrace();
}
}
}
输入的结果为:
Constructing objects:
Blip1 Constructor
Blip2 Constructor
Saving objects:
Blip1.writeExternal
Blip2.writeExternal
Recovering b1:
Blip1 Constructor
Blip1.readExternal
package externalizable;
import java.io.Externalizable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
public class Blip3 implements Externalizable {
int i;
String s; // No initialization
public Blip3() {
System.out.println("Blip3 Constructor");
// s, i not initialized
}
public Blip3(String x, int a) {
System.out.println("Blip3(String x, int a)");
s = x;
i = a;
// s & i initialized only in non-default
// constructor.
}
public String toString() {
return s + i;
}
public void writeExternal(ObjectOutput out) throws IOException {
System.out.println("Blip3.writeExternal");
// You must do this:
out.writeObject(s);
out.writeInt(i);
}
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
System.out.println("Blip3.readExternal");
// You must do this:
// 此顺序必须和前面相同
s = (String) in.readObject();
i = in.readInt();
}
public static void main(String[] args) {
System.out.println("Constructing objects:");
Blip3 b3 = new Blip3("A String ", 47);
System.out.println(b3.toString());
try {
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(
"Blip3.out"));
System.out.println("Saving object:");
o.writeObject(b3);
o.close();
// Now get it back:
ObjectInputStream in = new ObjectInputStream(new FileInputStream(
"Blip3.out"));
System.out.println("Recovering b3:");
b3 = (Blip3) in.readObject();
System.out.println(b3.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出结果:
Constructing objects:
Blip3(String x, int a)
A String 47
Saving object:
Blip3.writeExternal
Recovering b3:
Blip3 Constructor
Blip3.readExternal
A String 47
控制序列化的过程的另一个途径是在一个类中定义自己的writeObject和readObject方法,方法原型如下:
private void writeObject(ObjectOutputStream oos){.....}
private void readObject(ObjectInputStream ois){.......}
在序列化一个对象时,该对象的类中某字段使用transient关键字,则该字段不会被序列化。
使一个类的某字段不被序列化还有其他的方法。若有两个类A和B,A继承了B,并实现了Serializable接口,但是父类B没有实现Serializable接口,这个时候在反序列化A对象的时候,若要访问父类B中的字段,会是什么值呢?是序列化A类对象时,对应父类字段的值么?不是!由于B类没有实现Serializable接口,所以序列化A类对象的时候不会序列化父类B中对应的字段,在反序列化A类对象的时候,当然也不会得到原来对象对应父类B中字段的值,实际上,这个时候会调用B类的无参构造函数先构造B对象,所以对应B类中对应字段的值会是无参构造函数中设置的默认值,若没有设置,引用类型为null,布尔类型为false,int、doule等为0。
所以若想在一个类中不想序列化某些字段,可以把这个字段放到另一个类(A)中,然后去继承这个类(A),这个类(A)所有子类中的对应字段都不会被序列化。
类的静态字段不会被序列化。
引起反序列化失败的可能原因有哪些?
1.在反序列化的一端jvm是否允许反序列化。
2.反序列化一端是否有对应的类
3.反序列化一端对应类的UID是否一致
序列化使用eclipse自动生成的 serialVersionUID有什么作用?
eclipse中serialVersionUID可以有两种生成策略,一种是默认的1L,另一中是生成一串长整型的值,这种方法生成的值是根据类的元信息(如类名、接口名、成员方法及属性等)生成64位的hash值。
在序列化中serialVersionUID的作用是控制类的版本变化,以保持一定的兼容。考虑这样的一个场景,客户端通过一个类的对象跟服务器端交互,若这个对象由客户端创建,对于服务器端来说,不太好控制这个对象可以调用的方法已取得服务器端的服务。一种好的思路是,服务器端序列化该类的对象实例传到客户端,客户端反序列化该对象,通过该对象调用类的方法和服务器端交互,这样服务器端可以控制客户端取得的服务。若该类的实现在服务器端被改变,若改变后的类实现和之前是不兼容的,我们可以手动的改变类中serialVersionUID的值,如将1L改为2L,这样将迫使客户端必须要升级改类的版本,否则将无法取得服务器端的服务。
对于自动生成的serialVersionUID,只要类的元信息有一点点变化(如多一个字段,字段名变化),自动生成的serialVersionUID值就会不同,但是郁闷的是当类改变的时候,eclipse不会提示你serialVersionUID已经变得陈旧,你还是得手动改原来的serialVersionUID,如删除原来的serialVersionUID,然后从新生成新的serialVersionUID,所以从这点上看,自动生成的serialVersionUID没有任何特别之处。还不如使用默认的1L那样好,看上去舒服点,呵呵。
对于同一个对象,两次序列化到一个文件中,不会使该文件的长度变为写一次的两倍,实际上写第二次的时候,没有再次把该对象的所有属性值以及一些其他的信息再写一遍,而只是写入一个引用。从文件中读出这两个对象是==为true的,实际上是同一个对象的两个不同引用。
因为对于同一个对象,第二次写只会保留前一次写的引用,所以若在一次写后改变对象的状态(改变属性值),然后再次写入同一个文件,这时候不会将改变状态后的对象再写一遍,而只是写前一个对象的引用,所以从文件中读出来的对象还是第一次写时对象的状态的值。
测试代码:
import java.io.File;
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;
public class Test implements Serializable {
private static final long serialVersionUID = 1L;
int i;
public static void main(String[] args) {
Test test = new Test();
test.i = 1;
try {
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(new File("out.txt")));
oos.writeObject(test);
// 改变对象test的状态
test.i = 2;
oos.writeObject(test);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
new File("out.txt")));
Test test2 = (Test) ois.readObject();
Test test3 = (Test) ois.readObject();
System.out.println(test2.i); // 1
System.out.println(test3.i); // 1
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出结果:
参考文章:
http://blog.csdn.net/tangjian0921/article/details/6966416
http://www.oschina.net/question/12_17367
http://blog.csdn.net/morethinkmoretry/article/details/5929345
http://zengxiankang2011.blog.163.com/blog/static/1783603192011594938588/?fromdm&isFromSearchEngine=yes