Java性能优化系列之五--JavaIO(一)

2014-11-23 23:56:40 · 作者: · 浏览: 0

1、关于Java序列化与反序列化:

(1)作用:

1、实现对象状态的保存到本地,以便下一次启动虚拟机的时候直接读取保存的序列化字节生成对象,而不是初始化对象;2、实现对象的网络传输(RMI分布对象);3、实现对象的深拷贝。

一:对象序列化可以实现分布式对象。主要应用例如:RMI要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样。

二:java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中,可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的"深复制",即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。

(2)基本方式:

  ObjectOutputStream只能对Serializable接口的类的对象进行序列化。默认情况下,ObjectOutputStream按照默认方式序列化,这种序列化方式仅仅对对象的非transient的实例变量进行序列化,而不会序列化对象的transient的实例变量,也不会序列化静态变量。

  当ObjectOutputStream按照默认方式反序列化时,具有如下特点:

  1) 如果在内存中对象所属的类还没有被加载,那么会先加载并初始化这个类。如果在classpath中不存在相应的类文件,那么会抛出ClassNotFoundException;

  2) 在反序列化时不会调用类的任何构造方法。

  如果用户希望控制类的序列化方式,可以在可序列化类中提供以下形式的writeObject()和readObject()方法。

  private void writeObject(java.io.ObjectOutputStream out) throws IOException

  private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;

  当ObjectOutputStream对一个Customer对象进行序列化时,如果该对象具有writeObject()方法,那么就会执行这一方法,否则就按默认方式序列化。在该对象的writeObjectt()方法中,可以先调用ObjectOutputStream的defaultWriteObject()方法,使得对象输出流先执行默认的序列化操作。同理可得出反序列化的情况,不过这次是defaultReadObject()方法。

  有些对象中包含一些敏感信息,这些信息不宜对外公开。如果按照默认方式对它们序列化,那么它们的序列化数据在网络上传输时,可能会被不法份子窃取。对于这类信息,可以对它们进行加密后再序列化,在反序列化时则需要解密,再恢复为原来的信息。

  默认的序列化方式会序列化整个对象图,这需要递归遍历对象图。如果对象图很复杂,递归遍历操作需要消耗很多的空间和时间,它的内部数据结构为双向列表。

  在应用时,如果对某些成员变量都改为transient类型,将节省空间和时间,提高序列化的性能。

|-1、实体对象实现seriable接口以及自定义seriousid。

|-2、 ObjectOutputStream out= new ObjectOutputStream(baos);

out.writeObject(new PersonDemo("rollen", 20));

out.close();

|-3、ByteArrayInputStream bais=new ByteArrayInputStream(baos.toByteArray());

ObjectInputStream input=new ObjectInputStream(bais);

Object obj =input.readObject();

input.close();

(3)Java自定义序列化反序列化:复写实现了seriliable的实体类的readObject()和writeObject()的方法的原因:

有些对象中包含一些敏感信息,这些信息不宜对外公开。如果按照默认方式对它们序列化,那么它们的序列化数据在网络上传输时,可能会被不法份子窃取。对于这类信息,可以对它们进行加密后再序列化,在反序列化时则需要解密,再恢复为原来的信息。此时便不能使用默认的readObject和writeObject()方法。

private void writeObject(java.io.ObjectOutputStream out) throws IOException{

out.defaultWriteObject();

out.writeUTF(name);

}

private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{

in.defaultReadObject();

name=in.readUTF();

}

一般情况直接实现Serializable接口就可以实现序列化的要求,但是有些情况需要对序列化做一些特殊的要求。

(4)Transits关键字的作用:屏蔽一些不想进行序列化的成员变量,解屏蔽的方法可以用(3)

(5)Externalize的作用:

Externalizable接口继承自Serializable接口,如果一个类实现了Externalizable接口,那么将完全由这个类控制自身的序列化行为。Externalizable接口声明了两个方法:

public void writeExternal(ObjectOutput out) throws IOException

public void readExternal(ObjectInput in) throws IOException , ClassNotFoundException

前者负责序列化操作,后者负责反序列化操作。

在对实现了Externalizable接口的类的对象进行反序列化时,会先调用类的不带参数的构造方法,这是有别于默认反序列方式的。如果把类的不带参数的构造方法删除,或者把该构造方法的访问权限设置为private、默认或protected级别,会抛出java.io.InvalidException: no valid constructor异常。

(6)与Java构造函数的关系:

实现了Externalizable接口的类的对象进行反序列化时,会先调用类的不带参数的构造方法;而实现了Serializable接口的类的对象进行反序列化时,不会调用任何构造方法。仅仅是根据所保存的对象的状态信息,在内存中重新构建对象!

(7)注意事项:

1)、序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。为它赋予明确的值。显式地定义serialVersionUID有两种用途:

在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;

在某些场合,不希望类的不同版本对序列化兼容,因此需要确