shuaihhx的小屋

一个小博客....

JAVA序列化

shuaihhx shuaihhx
2019-01-03 16:27
10
0

java序列化是一个常用的手段,即将一些内容保存下来(所谓的数据持久化),以供后续读取。

常用的方式一般应该是

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("path"));
 AClass a = new AClass();
 String s = "123";
 out.writeObject(a);
 out.writeObject(s);
 out.flush();
 out.close();

  ObjectInputStream ois = new ObjectInputStream(new FileInputStream("path"));
  AClass a2 = (AClass) ois.readObject();
  String s2 = (String) ois.readObject();
 ois.close();

但今天在测试一个代码时,发现在写入一个类A到文件后在读取时,类A中的一个变量(也是类)一直读不到(为null),研究了很久,才发现是在类A中具有方法如下:

 private void writeObject(ObjectOutputStream out) throws IOException
    {
        out.writeInt(size);
        out.writeObject(base);
        out.writeObject(check);
      
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
    {
        size = in.readInt();
        base = (IntArrayList) in.readObject();
        check = (IntArrayList) in.readObject();
 //       charMap = new Utf8CharacterMapping();
    }

可以看到,在readObject里charMap被注释掉了。注意,这里也是一个有点阴差阳错的地方:

  • 假如在write中写入out.writeObject(charMap);然后在read中读 charMap = (Utf8CharacterMapping) in.readObject(); 肯定没有问题。
  • 上面代码这样写,不注释掉read中的最后一句也没有问题
  • 假如按照上面这样写,而read中最后一句变为charMap = (Utf8CharacterMapping) in.readObject();则会直接报错。

问题就在于上面这一段代码,并不会直接报错,而是到用到charMap时才会报出nullException,弄得找bug找了好久。

再回到原来的问题,刚开始时是通过很笨的方法(具体就不说了,反正是各种测试、修改、删减)找到是这段代码的问题,但不是很明白,因为此方法并不是重写方法,而且原始的read/writeObject是ObjectStream的方法,不是很懂这段代码为什么起作用。通过查找相关内容,在这里深入JAVA序列化反序列化找到了答案。

简单来说:

  • 在序列化过程中,如果被序列化的类中定义了writeObject 和 readObject 方法,虚拟机会试图调用对象类里的 writeObject 和 readObject 方法,进行用户自定义的序列化和反序列化。
  • 如果没有这样的方法,则默认调用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。

同时,文章中还介绍了许多其他的相关内容,值得一读,比如使用transient声明不序列化(static也不序列化)

class Employee implements Serializable {
    public String name;
    public String address;
        //该属性为不可序列化的,所以声明为短暂的
    public transient int SSN;
    public Number number;
}

serialVersionUID的定义方式

private static final long serialVersionUID = -6849794470754667710L;

以及一些更深层次的源码级探索。

这篇博客java序列化的内部实现对object的过程做了比较详细的阐述。