对象的序列化与Serializable 接口
1. 基本的序列化操作
一般来说,对象只存在与进程运行期间,进程中止后,你所创建的对象就灰飞烟灭。但是有的时候,你需要在程序中止后继续保留对象信息,这样下次运行时,你可以将对象重建恢复到程序上次运行时它所拥有的信息。
Java中提供了Serializable接口来标记可序列化的对象。
public interface Serializable {
}
对象的序列化可以实现轻量的持久化,所谓持久化就意味着一个对象的生存周期不再取决于程序是否正在运行,它可以生存在程序调用之间。
要序列化一个对象也很简单:
1. 对象实现Serializable接口
2. 创建 OutputStream对象,并将其封装到ObjectOutputStream对象内。
3. 使用 writeObject()方法将对象序列化
Java中的序列化特别聪明,他能自动追踪对象内所包含的所有引用并同时将这些引用的对象序列化保存起来。
恢复对象的操作也很简单。使用ObjectInputStream 的 readObject方法。
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;
import java.util.Random;
class Data implements Serializable {
private int n;
public Data(int n) {this.n = n;}
@Override
public String toString() {
return "" + n;
}
}
public class Worm implements Serializable{
private static Random rand = new Random(47);
private Data[] d = {
new Data(rand.nextInt(10)),
new Data(rand.nextInt(10)),
new Data(rand.nextInt(10)),
};
Worm next;
private char c;
public Worm(int i, char x) {
System.out.println("Worm Constructor " + i);
c = x;
if (--i > 0) {
next = new Worm(i,(char) (x + 1));
}
}
public String toString() {
StringBuilder rs = new StringBuilder(" : ");
rs.append(c);
rs.append("(");
for (Data data : d) {
rs.append(data);
}
rs.append(")");
if (next != null) {
rs.append(next);
}
return rs.toString();
}
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
Worm w = new Worm(6, 'a');
System.out.println("w = " + w);
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream("worm.out"));
oo.writeObject(w);
oo.close();
ObjectInputStream oi = new ObjectInputStream(new FileInputStream("worm.out"));
Worm w1 = (Worm)oi.readObject();
System.out.println("w1 = " + w1);
oi.close();
}
}
输出:
Worm Constructor 6
Worm Constructor 5
Worm Constructor 4
Worm Constructor 3
Worm Constructor 2
Worm Constructor 1
w = : a(853) : b(119) : c(802) : d(788) : e(199) : f(881)
w1 = : a(853) : b(119) : c(802) : d(788) : e(199) : f(881)
readObject的时候会抛出ClassNotFound错误,因此需要在虚拟中加载了相应的Class对象后才能恢复对象,否则将抛出错误。
2. triansient 关键字
带有transient标志的字段将不会被序列化。
3. serialVersionUID
Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。
类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的serialVersionUID,也有可能相同。为了提高serialVersionUID的独立性和确定性,强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值。显式地定义serialVersionUID有两种用途:
1)在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。
2)当你序列化了一个类实例后,希望更改一个字段或添加一个字段,不设置serialVersionUID,所做的任何更改都将导致无法反序化旧有实例,并在反序列化时抛出一个异常。如果你添加了serialVersionUID,在反序列旧有实例时,新添加或更改的字段值将设为初始化值(对象为null,基本类型为相应的初始默认值),字段被删除将不设置。