zoukankan      html  css  js  c++  java
  • java序列化

    一、概念:

    序列化时将 Java 对象相关的类信息、属性及属性值等等保存起来,反序列化时再根据这些信息构建出 Java 对象。Java 中进行序列化操作需要实现 Serializable Externalizable 接口。 Externalizable继承自Serializable接口。

    二、序列化和反序列化:

    序列化:

    在对某个对象进行写入时,它其实不仅仅序列化自己,还会去遍历寻找相关引用的其他对象,由自己和其他引用对象组成的一个完整的对象图关系都会被序列化。

    1 FileOutputStream f = new FileOutputStream("tmp.o");
    2 ObjectOutput s = new ObjectOutputStream(f);
    3 s.writeObject("test");
    4 s.writeObject(new ArrayList());
    5 s.flush();

    反序列化:

    1 FileInputStream in = new FileInputStream("tmp.o");
    2 ObjectInputStream s = new ObjectInputStream(in);
    3 String test = (String)s.readObject();
    4 List list = (ArrayList)s.readObject();

    反序列化除了会恢复对象自己之外还会遍历整个完整的对象图,创建整个对象图包含的所有对象。 

    三、serialVersionUID

    serialVersionUID主要用于验证版本一致性,每个类都拥有这么一个 ID,在序列化的时候会一起被写入流中,那么在反序列化的时候就被拿出来跟当前类的 serialVersionUID 值进行比较,两者相同则说明版本一致,可以序列化成功,而如果不同则序列化失败。

    一般情况下我们可以自己定义 serialVersionUID 的值或者 IDE 帮我们自动生成,而如果我们不显示定义 serialVersionUID 的话,这不代表不存在 serialVersionUID,而是由 JDK 帮我们生成,生成规则是会利用类名、类修饰符、接口名、字段、静态初始化信息、构造函数信息、方法名、方法修饰符、方法签名等组成的信息,经过 SHA 算法生成摘要即是最终的 serialVersionUID 值。

    serialVersionUID有两种显示的生成方式:

    1、一是默认的1L,比如:private static final long serialVersionUID = 1L;

    2、二是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如:

    private static final  long  serialVersionUID = xxxxL;

    四、父类序列化:

    如果一个子类实现了 Serializable 接口而父类没有实现该接口,则在序列化子类时,子类的属性状态会被写入而父类的属性状态将不被写入。所以如果想要父类属性状态也一起参与序列化,就要让它也实现 Serializable 接口。

    如果父类未实现 Serializable 接口则反序列化生成的对象会再次调用父类的构造函数,以此完成对父类的初始化。所以父类属性初始值一般都是类型的默认值。

    五、哪些字段会序列化:

    1、默认方式,Java对象中的非静态和非transient的字段都会被定义为需要序列的字段;

    2、另外一种方式是通过 ObjectStreamField 数组来声明类需要序列化的对象。

    ObjectStreamField 使用示例:

    1 public class A implements Serializable {
    2     String name;
    3     String password
    4     private static final ObjectStreamField[] serialPersistentFields
    5                  = {new ObjectStreamField("name", String.class)};
    6 }

    六、枚举类型序列化:

    枚举类型被编译后会变成一个继承 java.lang.Enum 的类,而且枚举里面的元素被声明成 static final ,另外生成一个静态代码块 static{},最后还会生成 values 和 valueOf 两个方法。Enum 类是一个抽象类,主要有 name 和 ordinal 两个属性,分别用于表示枚举元素的名称和枚举元素的位置索引。

    Enum 类型参与序列化时只会将枚举对象中的 name 属性写入,而其他的属性则不参与进来。在反序列化时,则是先读取 name 属性,然后再通过 java.lang.Enum 类的 valueOf 方法找到对应的枚举类型。

    不能自定义 Enum 类型的序列化,所以 writeObject, readObject, readObjectNoData, writeReplace 以及 readResolve 等方法在序列化时会被忽略,类似的,serialPersistentFields serialVersionUID 属性都会被忽略。

    七、Externalizable接口:

    Externalizable 接口主要就是提供给用户自己控制序列化内容,可以看到它其实继承了 Serializable 接口,提供了 writeExternal readExternal 两个方法,也就是在这两个方法内控制序列化和反序列化的内容。

    1 public interface Externalizable extends java.io.Serializable {
    2     void writeExternal(ObjectOutput out) throws IOException;
    3     void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
    4 }

    Externalizable 接口使用示例: 

     1 public class ExternalizableTest implements Externalizable {
     2     public String value = "test";
     3     public ExternalizableTest() {
     4     }
     5     public void writeExternal(ObjectOutput out) throws IOException {
     6         Date d = new Date();
     7         out.writeObject(d);
     8         out.writeObject(value);
     9     }
    10     public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    11         Date d = (Date) in.readObject();
    12         System.out.println(d);
    13         System.out.println((String) in.readObject());
    14     }
    15 }

    八、写入和读取时替换对象: 

    替换当前的对象而写入其他对象的话则可以通过 writeReplace 方法来实现;

     1 class Person implements Serializable {
     2     private String name;
     3     private int age;
     4     public Person(String name, int age) {
     5         this.name = name;
     6         this.age = age;
     7     }
     8     private Object writeReplace() throws ObjectStreamException {
     9         Object[] properties = new Object[2];
    10         properties[0] = name;
    11         properties[1] = age;
    12         return properties;
    13     }
    14 }
    15 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test.o"));
    16 Object[] properties = (Object[]) ois.readObject();

    同样读取时替换对象使用readResolve 方法来实现; 

     1 class Person implements Serializable {
     2     private String name;
     3     private int age;
     4     public Person(String name, int age) {
     5         this.name = name;
     6         this.age = age;
     7     }
     8     private Object readResolve() throws ObjectStreamException {
     9         return 2222;
    10     }
    11 }
    12 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test.o"));
    13 Object o = ois.readObject();
  • 相关阅读:
    .NET旋转PDF并保存旋转结果到文件
    C#的抽象类和接口的区别,在什么时候使用才合适?
    [转]SQL Server中多行多列连接成为单行单列
    VBS脚本COPY指定日期文件及文件夹
    eval同时绑定两个值:通过String.Format给超链接中的两个参数赋值
    How to recover SA password on Microsoft SQL Server 2008 R2
    [转]asp.net 前台绑定后台变量方法总结:<%= %> 和<%# %>的区别
    Js得到radiobuttonlist选中值的两种方式
    Ameriscan增加一个新的client:AP_CONSO
    命令行处理pdf的利器:PDFTK.exe
  • 原文地址:https://www.cnblogs.com/laoxia/p/10271542.html
Copyright © 2011-2022 走看看