1、Serializable接口Serializable接口在JDK源码中定义如下:/* * author unascribed * see java.io.ObjectOutputStream * see java.io.ObjectInputStream * see java.io.ObjectOutput * see java.io.ObjectInput * see java.io.Externalizable * since JDK1.1 */public interface Serializable Java从JDK1.1开始支持对象的序列化机制,有上图的源码可知,Serializab
2、le接口没有声明任何方法,实现该接口的Java类不需要对任何方法提供实现(默认情况下,定制序列化时除外),*因此,该接口仅仅是一个mark interface(标记接口)”,实现该接口意味着告知JVM该对象可以序列化。*Java序列化机制要求所有具备序列化的对象必须实现该接口,否则是不能被序列化的,如果对于没有实现该接口的对象进行序列化时,Java API会抛出异常,无法进行序列化。transient关键字瞬时变量,指的是被transient关键字修饰的变量,该关键字表示为瞬时的,即不做持久化处理的,以此来控制属性是否被包含进入序列化的字节流中。Serializable接 口提供了默认的序列化
3、行为,在默认情况下,开发人员只需实现该接口,无需进行其他额外的操作,即可实现的对象的序列化。默认只对对象中非静态的字段以及非瞬时的字段进行序列化,其他的字段是不允许被序列化的。这是因为,静态变量是类变量,属于整个类,并非专属于每个对象实例,因此,不序列化静态变量时合理的。瞬时变量,指的是被transient关键字修饰的变量,该关键字表示为瞬时的,即不做持久化处理的,以此来控制属性是否被包含进入序列化的字节流中。因此,在序列化时,排除transient关键字修饰的属性也是合理的。这种情况的具体表现就是,在序列化的有序字节流中没有保存不能被序列化的字段的状态,因此,在反序列化时,这些字段状态是不能
4、被重建的。但是有一点需要注 意的是,经过反序列化后的对象,除了对可被序列化的字段状态进行重建之外,其他的没有被序列化的字段作为对象属性的一部分,也在对象重建时得以初始化。但 是这些字段的状态是不被保存的,重建后的这些属性仅仅是系统赋予的默认值,而非保存了对象序列化之前的状态。实现Serializable接口除了提供默认的序列化方式之外,同样允许开发人员定制序列化,即通过实现以下相同签名的方法来实现:序列化方法:private void writeObject(java.io.ObjectOutputStream out) throws IOException反序列化方法:private voi
5、d readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;其中,writeObject方法用于定制序列化,readObject方法用于实现定制反序列化。serialVersionUID每个实现Serializable接口的对象都有一个serialVersionUID,长整型,64位,唯一标示了该序列化对象。在类定义中,可以显示的定义该静态变量,也可以不定义。在不定义的情况下,Java编译器会隐式的生成该变量。强烈建议显示定义。那么,该变量有什么用途呢?反序列化兼容控制,serial
6、VersionUID相同才能进行反序列化。例如:远程主机需要反序列化对象C,如果在本地和远程主机内的C对象持有的serialVersionUID不同,即使两个类其它部分完全一样,也是不能成功反序列化话的,会抛出异常。因此,如果对类做了修改,为了保证版本对序列化兼容,该变量的值保持不变。从另一个角度来讲,不期望不同版本的类对序列化兼容,则改变该变量值。序列化保存的数据那么,到底哪些数据被序列化到了有序字节流中呢?字节流数据包含了non-static和non-transient类型的成员数据、类名、类签名、可序列化的基类以及类中引用的其他对象。针对于父类,有几点原则:1. 如果基类实现了Seria
7、lizable接口,则其子类默认的是可序列化的,不必显示声明;2. 如果基类没有实现Serializable接口,在反序列化时,会调用基类的无参构造方法来重建基类对象,只不过不会保留基类对象状态。利用Serializable 实现序列化的实例。public class User implements Serializable private String userName; private String passWord; public User(String userName, String passWord) this.userName = userName; this.passWord
8、= passWord; public String getUserName() return userName; public void setUserName(String userName) public String getPassWord() return passWord; public void setPassWord(String passWord) Override public String toString() return User + userName= + userName + , passWord= + passWord + ;测试代码import java.io.
9、*;/* * author bridgepublic class Client public static void main(String args) /构造一个可以序列化对象 User user = new User(Bridge, 123456); /执行序列化 try ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream(User.dat); out.writeObject(user); catch (IOException e) e.printStackTrace(); /反序列化 ObjectIn
10、putStream in = new ObjectInputStream( new FileInputStream( User userRecover = (User) in.readObject(); System.out.println(userRecover); catch (ClassNotFoundException e) 运行结果UseruserName=Bridge123456Externalizable接口Externalizable接口继承成自Serializable接口,实现该接口意味着对象本身完全掌控自己的序列化方式。该接口JDK源码如下: public interfac
11、e Externalizable extends java.io.Serializable void writeExternal(ObjectOutput out) throws IOException; void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;Externalizable接口定义了两个方法:writeExternal(ObjectOutput out)和readExternal(ObjectInput in)。write方法用于实现定制序列化,read方法用于实现定制反序列化,定
12、制序列化大部分情况下,我们期望对类似于密码等这样的敏感信息进行加密处理,以密文的形式在网 络间传输,增强数据的安全性。但是,通过我们上述的方式进行序列化,默认的处理方式是不能保证密码的密文传输的。因此,针对此类问题,我们必须能够定制对 象的序列化和反序列化过程,只有这样才能将我们的业务逻辑加入其中,以满足实际应用的需要。基于Serializable接口的定制定制序列化和反序列化与上述序列化方式的不同在于:自定义类的实现。 首先,同样,自定义的类要实现Serializable接口,这是序列化处理的前提。不同的是,在定制序列化时,需要根据我们的实际需要,重写writeObject和readObject方法,完成序列化和反序列化的定制。示例代码如下:User.javaimport sun.misc.BASE64Decoder;import sun.misc.BASE64Encoder; private void writeObject(ObjectOutputStream out) throws IOException ObjectOutputStream.PutField putField = out.put
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1