Java I/O全文摘要(十三)过滤流,对象序列化

1 对象序列化

ObjectInputStreamObjectOutputStream实现了  DataInputDataOutput

使用方法:

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


你可能感兴趣的:(JavaSE,JavaIO,Java)