java序列化:Java序列化技术

本文介绍Java序列化技术
1Java"对象序列化"
Java"对象序列化"能让你将个实现了Serializable接口对象转换成这样日后要用这个对象时候你就能把这些数据恢复出来并据此重新构建那个对象点甚至在跨网络环境下也是如此这就意味着序列化机制能自动补偿操作系统方面差异也就是说你可以在 Windows机器上创键个对象序列化的后再通过网络传到Unix机器上然后在那里进行重建你不用担心在区别平台上数据是怎样表示 顺序怎样或者别什么细节
对象序列化最聪明点是它不仅能保存对象副本而且还会跟踪对象里面reference把它所引用对象也保存起来然后再继续跟踪那些对象 reference以此类推这种情形常被称为"单个对象所联结'对象网'"这个机制所涵盖范围不仅包括对象成员数据而且还包含里面 reference如果你要自己实现对象序列化那么编写跟踪这些链接将会是件非常痛苦任务但是Java对象序列化就能精确无误地 做到这毫无疑问遍历算法是做过优化
2Object serialization定义
Object serialization 允许你将实现了Serializable接口对象转换为字节序列这些字节序列可以被完全存储以备以后重新生成原来对象
serialization不但可以在本机做而且可以经由网络操作(RMI)这个好处是很大----它自动屏蔽了操作系统差异字节顺序(用Unix下c开发过网络编程人应该知道这个概念)等比如在Window平台生成个对象并序列化的然后通过网络传到台Unix机器上 然后可以在这台Unix机器上正确地重构这个对象
Object serialization主要用来支持2种主要特性:
JavaRMI(remote method invocation).RMI允许象在本机上样操作远程机器上对象当发送消息给远程对象时就需要用到serializaiton机制来发送参数和接收返回直
JavaJavaBeansBean状态信息通常是在设计时配置Bean状态信息必须被存起来以便当运行时能恢复这些状态信息这也需要serializaiton机制
3般序列化例子
名称:SerializationDemo.java
主题:实现对象序列化和反序列化
介绍说明:该由例子化个MyClass类对象开始该对象有 3个例子变量类型分别为String、、double是希望存储和恢复信息
import java.io.Serializable;
MyClass implements Serializable {
String s;
i;
double d;

public MyClass(String s, i, double d) {
this.s = s;
this.i = i;
this.d = d;
}

public String toString {
"s=" + s + ";i=" + i + ";d=" + d;
}
}
要想序列化对象你必须先创建个OutputStream然后把它嵌进ObjectOutputStream这时你就能用writeObject ( )思路方法把对象写入OutputStream了时候你得把InputStream嵌到ObjectInputStream里面然后再 readObject( )思路方法
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public SerializationDemo {
public void (String args) {
FileInputStream in = null;
FileOutputStream out = null;
ObjectInputStream oin = null;
ObjectOutputStream oout = null;
MyClass object1 = MyClass("Hello", -7, 2.7e10);
.out.prln("object1:" + object1);
// Object serialization
try {
out = FileOutputStream("serial.txt");
oout = ObjectOutputStream(out);
oout.writeObject(object1);
oout.close;
} catch (Exception e) {
.out.prln("Exception during serialization:" + e);
.exit(0);
}

// Object deserialization
try {
MyClass object2;
in = FileInputStream("serial");
oin = ObjectInputStream(in);
object2 = (MyClass) oin.readObject;
oin.close;
.out.prln("object2:" + object2);
} catch (Exception e) {
.out.prln("Exception during deserialization:" + e);
.exit(0);
} finally {
// …此处省略
}
}
}
结果:
object1:s=Hello;i=-7;d=2.7E10
object2:s=Hello;i=-7;d=2.7E10
4.修改默认序列化机制
在序列化过程中有些数据字段我们不想将其序列化对于此类字段我们只需要在定义时给它加上transient关键字即可对于transient字段序列化机制会跳过不会将其写入文件当然也不可被恢复但有时我们想将某字段序列化但它在SDK中定义却是不可序列化类型这样话我们也必须把他标注为transient可是不能写入又如何恢复呢?好在序列化机制为包含这种特殊问题类提供了如下思路方法定义:
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;
private void writeObject(ObjectOutputStream out) throws IOException;
(注:这些思路方法定义时必须是私有不需要你显示序列化机制会自动)使用以上思路方法我们可以手动对那些你又想序列化又不可以被序列化数据字段进行写出和读入操作
下面是个典型例子java.awt.geom包中Po2D.Double类就是不可序列化该类没有实现Serializable接口在我例子中将把它当作LabeledPo类中个数据字段并演示如何将其序列化
import java.awt.geom.Po2D;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
LabeledPo implements Serializable {
private String label;
transient private Po2D.Double po;

public LabeledPo(String label, double x, double y) {
super;
this.label = label;
this.po = Po2D.Double(x, y);
}

private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject;// 先序列化对象
oos.writeDouble(po.getX);
oos.writeDouble(po.getY);
}

private void readObject(ObjectInputStream ois) throws IOException,
ClassNotFoundException {
ois.defaultReadObject;// 先反序列化对象
double x = ois.readDouble + 1.0;
double y = ois.readDouble + 1.0;
po = Po2D.Double(x, y);
}

public String toString {
getClass.getName + "[ Label = " + label
+ ", po.getX = " + po.getX + ",
po.getY = " + po.getY + "]";
}
}

public Test1 {
public void (String args) {
LabeledPo label = LabeledPo("Book", 5.0, 5.0);
try {
.out.prln("before:\n" + label);
ObjectOutputStream oos = ObjectOutputStream(
FileOutputStream("label.txt"));
oos.writeObject(label);
oos.close;

ObjectInputStream ois = ObjectInputStream(
FileInputStream("label.txt"));
LabeledPo label1 = (LabeledPo) ois.readObject;
ois.close;
.out.prln("after add 1.0:\n" + label1);
} catch (Exception e) {
e.prStackTrace;
}
}
}
结果:
before:
sample.LabeledPo[ Label = Book, po.getX = 5.0, po.getY = 5.0]
after add 1.0:
sample.LabeledPo[ Label = Book, po.getX = 6.0, po.getY = 6.0]
5.继承类序列化例子
个父类实现Serializable接口后子类都将自动实现序列化
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

SuperC implements Serializable {// 父类实现了序列化
supervalue;

public SuperC( supervalue) {
this.supervalue = supervalue;
}

public String toString {
"supervalue: " + supervalue;
}
}

SubC extends SuperC {// 子类
subvalue;

public SubC( supervalue, subvalue) {
super(supervalue);
this.subvalue = subvalue;
}

public String toString {
super.toString + " sub: " + subvalue;
}
}

public Test2 {
public void (String args) {
SubC subc = SubC(100, 200);
FileInputStream in = null;
FileOutputStream out = null;
ObjectInputStream oin = null;
ObjectOutputStream oout = null;
try {
out = FileOutputStream("Test1.txt");
oout = ObjectOutputStream(out);
oout.writeObject(subc); // 子类序列化
oout.close;
oout = null;

in = FileInputStream("Test1.txt");
oin = ObjectInputStream(in);
SubC subc2 = (SubC) oin.readObject;// 子类反序列化
.out.prln(subc2);
} catch (Exception ex) {
ex.prStackTrace;
} finally {
// …此处省略
}
}
}
结果:
supervalue: 100 sub: 200
可见子类成功序列化/反序列化了怎管让子类实现序列化看起来是件很简单事情但有时候往往我们不能够让父类实现Serializable接口原因是有时候父类是抽象(这并没有关系)并且父类不能够强制每个子类都拥有序列化能力换句话说父类设计仅仅是为了被继承
要为个没有实现Serializable接口父类编写个能够序列化子类要做两件事情:
、父类要有个无参constructor;
其 2、子类要负责序列化(反序列化)父类
  我们将SuperCSerializable接口去掉而给SubC加上Serializable接口运行后产生:
java.lang.Error: Unresolved compilation problem:
Serializable cannot be resolved or is not a valid supererface
at Serial.SubC.<init>(SubC.java:15)
at Serial.Test1.(Test1.java:19)
Exception in thread ""
我们改写这个例子:
abstract SuperC{
supervalue;

public SuperC( supervalue) {
this.supervalue = supervalue;
}

public SuperC{}//增加个无参constructor

public String toString {
"supervalue: " + supervalue;
}
}

SubC extends SuperC implements Serializable{// 子类
subvalue;

public SubC( supervalue, subvalue) {
super(supervalue);
this.subvalue = subvalue;
}

public String toString {
super.toString + " sub: " + subvalue;
}

private void writeObject(java.io.ObjectOutputStream out) throws IOException {
out.defaultWriteObject;// 先序列化对象
out.writeInt(supervalue);// 再序列化父类
}

private void readObject(java.io.ObjectInputStream in) throws IOException,
ClassNotFoundException {
in.defaultReadObject;// 先反序列化对象
supervalue = in.readInt;//再反序列化父类
}
}
运行结果证明了这种思路方法是正确在此处我们又用到了 writeObject/ readObject思路方法以代替默认行为我们在序列化时首先了 ObjectOutputStreamdefaultWriteObject它使用默认序列化行为然后序列化父类域;反序列化时候也
6.实现Externalizable接口
Externalizable接口继承自Serializable接口如果个类实现了Externalizable接口那么将完全由这个类控制自身序列化行为Externalizable接口声明了两个思路方法:
public void readExternal(ObjectInput in) throws IOException , ClassNotFoundException;
public void writeExternal(ObjectOutput out) throws IOException;
前者负责序列化操作后者负责反序列化操作
在对实现了Externalizable接口对象进行反序列化时会先不带参数构造思路方法(回忆前两个例子异曲同工)这是有别于默认反序列方式如果把类不带参数构造思路方法删除或者把该构造思路方法访问权限设置为private、默认或protected级别会抛出 java.io.InvalidException: no valid constructor异常
ExternalDemo implements Externalizable { // ExternalDemo类必须实现Externalizable接口
private String aString = "TEST";
private num = 0;

public ExternalDemo {}

public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(aString);
out.write(88); // 在序列化数据最后加个88
}

public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
aString = (String) in.readObject;
num = in.read; // 把数字88加进来
}

public String toString { // 测试
("String:"+aString + " :"+num);
}
}

public Test3 {
public void (String args) {
ExternalDemo eDemo = ExternalDemo;
try {
ObjectOutputStream oos = ObjectOutputStream(
FileOutputStream("test3.txt"));
oos.writeObject(eDemo); // writeExternal自动执行
oos.close;

ObjectInputStream ois = ObjectInputStream(
FileInputStream("test3.txt"));
ExternalDemo demo = (ExternalDemo) ois.readObject;// readExternal自动执行
.out.pr(demo);
ois.close;
} catch (Exception e) {
e.prStackTrace;
}
}
}
结果:
String:TEST :88
7.可序列化类区别版本序列化兼容性
凡是实现Serializable接口类都有个表示序列化版本标识符静态变量:
private final long serialVersionUID ;
以上serialVersionUID取值是Java运行时环境根据类内部细节自动生成如果对类源代码作了修改再重新编译新生成类文件serialVersionUID取值有可能也会发生变化
serialVersionUID默认值完全依赖于Java编译器实现对于同个类用区别Java编译器编译有可能会导致区别 serialVersionUID也有可能相同为了提高serialVersionUID独立性和确定性强烈建议在个可序列化类中显示定义serialVersionUID为它赋予
Tags:  什么是序列化 对象序列化 序列化 java序列化

延伸阅读

最新评论

发表评论