1 对象序列化
ObjectInputStream 和 ObjectOutputStream实现了 DataInput 和DataOutput
使用方法:
writeObject( ) 和 readObject( )
2 读取和写入对象
将对象的状态信息保存起来,使得之后可以在某一时刻恢复它们。
健壮,可信赖和安全的序列化使得工作变得复杂。
3 流
public class ObjectOutputStream extends OutputStream
implements ObjectOutput,
ObjectStreamConstants
public class ObjectInputStream extends InputStream
implements ObjectInput, ObjectStreamConstants
public final void writeObject(Object o) throws IOException
public final Object readObject( )
throws OptionalDataException, ClassNotFoundException, IOException
4 序列化如何工作
考虑一个问题:
如果你有一个类,里面有:
private double x; private double y;
你需要进行的保存工作就是:
DataOutputStream dout = new DataOutputStream(out);
dout.writeDouble(x);
dout.writeDouble(y);
DataInputStream din = new DataInputStream(in);
this.x = din.readDouble( );
this.y = din.readDouble( );
然而,如果一个对象包含另外一个对象,或者更可怕的是,有循环引用的时候,问题就变得复杂起来。
幸亏sun公司已经为你处理了这一切,将会保存对象的所有非静态非瞬时的字段,然后保存在特殊的文件里。
5 性能
序列化很方便,但是它很消耗性能,
如果能自己定义,最好自己定义格式。
其次,序列化将减慢或者组织垃圾收集
流可能持有对象的引用,而流可能没有关闭,这就导致对象一直被强引用持有者。
6 序列化接口
由于序列化将访问对象私有接口,这将导致不安全;
其次,序列化一个不合适的对象,例如:Socket对象,它被序列化保存,然后在另一个程序中反序列化,而连接已经不再,这将导致问题。
所以,不是所有的类都需要被序列化。
需要序列化的类应该实现
public interface Serializable接口。
需要注意的是,如果父类实现了这个接口,子类就无需再次声明了。
例如:
java.lang.Throwable
7 不应该序列化的对象
虽然java.lang.Throwable总是应该被序列化。但是有更多不应该被序列化的类。
例如:流,JavaBeans, 操作系统依赖的, 工具类, 反射。
对象不被序列化的7条理由:
1. 与natvie代码紧密联系
2. 对象状态与虚拟机或者运行时环境有关
3. 序列化会有潜在的安全风险
4. 类就只有一些静态方法
5. 类是非静态内部类。
6. 编写程序的程序员未考虑需要序列化
7. 有更好的方式替代序列化(例如存储到XML中)
8. 不被序列化异常
如果序列化一个未实现序列化接口的类,则会抛出
public class NotSerializableException extends ObjectStreamException
可能导致问题的原因:
1 引用了非序列化对象。
2 父类无参构造方法不存在
处理的方式:
try -catch
或者 transient 对应变量。
9 版本问题
由于序列化一个对象,而对象在反序列化时所处环境可能发生很大变化。例如字段增加删除修改,方法的改变,修饰符的变化等等。
值得一说的是,静态的变量修改不会影响序列化,transient的也不会,但是非transient的实例变量的修改会影响序列化。
所以需要考虑序列化随着版本的演进的问题。
10 兼容性和非兼容性改变
下面这些改动会导致不兼容:
Most changes to constructors and methods, whether instance or static. Serialization doesn't touch the methods of a class. The exceptions are those methods directly involved in the serialization process, particularlywriteObject( ) and readObject( ).
All changes to static fieldschanging their type, their names, adding or removing them, etc. Serialization ignores all static fields.
All changes to transient fieldschanging their type, their names, adding or removing them, etc. Serialization ignores all transient fields.
Adding or removing an interface (except the Serializable interface) from a class. Interfaces say nothing about the instance fields of a class.
Adding or removing inner classes.
Changing the access specifiers of a field. Serialization does not respect access protection.
Changing a field from static to nonstatic or transient to nontransient. This is the same as adding a field.
The following changes are incompatible and thus prevent deserialization of serialized objects:
Changing the name of a class.
Changing the type of an instance field.
Changing the superclass of a class. This may affect the inherited state of an object.
Changing the writeObject( ) or readObject( ) method (discussed later) in an incompatible fashion.
Changing a class from Serializable to Externalizable (discussed later) orExternalizable to Serializable.
为了检测是否是同一版本,为其生成了SUID。
它长这样:
serialVersionUID = 5590355372728923878,
11 自定义序列化格式
最简单的自定义方法就是决定哪些需要增加trasient
如果,你要做的更多,重写readObject( ) and writeObject( )
或者,你也可以实现java.io.Externalizable来实现对序列化完全的控制。
Externalizable提供了额外的两个方法:
public void writeExternal(ObjectOutput out) throws IOException
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException
提供了更加精准的控制。
12 合法性验证
保存值的状态远远不够,你还需要验证它。
13 封装对象
提供了对于序列化对象的加密
public class SealedObject extends Object implements Serializable