序列化/反序列化机制

时间:2011-12-21 07:46:34

标签: java serialization deserialization

说,我有一个类X,它有一个字段值,即

class X implements Serializable {
    private int value;
    // ...
}

此外,此处未显示getter和setter。这个类是序列化的。 在反序列化中,结束同一类具有值字段,访问说明符是公共的。此外,这个类没有getter和setter。所以,我的问题是:

  1. 如果字段的访问说明符发生更改,或者在反序列化结束时类中缺少部分或全部方法,反序列化是否会失败?
  2. 在反序列化期间为字段分配值的机制是什么?

5 个答案:

答案 0 :(得分:5)

一些好的链接The Java serialization algorithm revealed

  

1)如果字段的访问说明符,则反序列化失败   更改或部分或全部方法在类中缺失   反序列化结束?

使用使用反射

进行序列化

Java使用

检测对类的更改

private static final long serialVersionUID

默认值涉及哈希码。序列化根据以下信息创建long类型的单个哈希码:

  • 班级名称和修饰语

  • 该类实现的任何接口的名称

  • 除私有方法和构造函数之外的所有方法和构造函数的描述

  • 除私有,静态和私有瞬态之外的所有字段的描述

序列化机制的默认行为是经典的“更安全而不是抱歉”策略。序列化机制使用suid(默认为极其敏感的索引)来告知类何时发生了更改。如果是这样,序列化机制拒绝使用使用旧类序列化的数据创建新类的实例。

  

2)字段分配其值的机制是什么   在反序列化期间?

答案 1 :(得分:3)

真实细节可以在the Java Object Serialization Specification中阅读。

回答你的问题:

  1. 序列化有一个基本的健全性检查,以查看序列化结束是否使用相同版本的类:serialVersionUID成员必须相等。阅读Stream Unique Identifiers部分以了解有关它的更多信息。基本上,它是一个静态值,您可以通过在类上声明它来管理自己,或让编译器为您生成一个静态值。如果编译器生成它,任何对类的更改都将导致serialVersionUID的更改,因此如果结尾没有完全相同的类,则反序列化会失败。如果你想避免这种情况,请自行声明变量并在对类成员变量的更改确实使类不兼容时手动更新它。

  2. Java虚拟机在这里发挥了很多作用,它可以直接访问所有内部状态而无需getter(标记为transientstatic的字段不是序列化的) 。此外,虽然Serializable接口未指定任何要实现的方法,但您可以声明许多“魔术方法”来影响序列化过程。阅读“The writeObject Method”部分以及更多信息。请注意,您应该谨慎使用这些,因为它们可能会使任何维护开发人员感到困惑!

答案 2 :(得分:2)

我真的不知道你是如何得到这个结果的,但你所说的是序列化的默认行为。所以,我猜你错了。以下是一些示例代码:

public class X implements Serializable
{
     private int value;

     public int getValue() { return value; }
}

这里是序列化/反序列化过程:

X x = new X();
x.setValue(4);

ByteArrayOutputStream buffer = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputSteam(buffer);
oos.writeObject(x);
oos.flush();
oos.close();

ByteArrayInputStream in = new ByteArrayInputStream(buffer.toByteArray());
ObjectInputStream ois = new ObjectInputStream(in);
Object obj = ois.readObject();
if (obj instanceof X)
{
    X readObject = (X) obj;
    System.out.println(readObject.getValue());
}

您可能使用Java Reflection来获得结果。确保您使用getDeclaredFields();getDeclaredMethods();代替方法名称中没有Declared的变体。

答案 3 :(得分:2)

您不需要使用java序列化来使用getter / setter来序列化/反序列化,例如,请检查以下代码:

public class Main {

    public static class Q implements Serializable {
        private int x;
        public Q() {
            x = 10;
        }
        public void printValue() {
            System.out.println(x);
        }
    }

    public static void main(String[] args) throws Exception {
        Q q = new Q();
        FileOutputStream fos = new FileOutputStream("c:\\temp.out");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(q);
        fos.close();

        FileInputStream fis = new FileInputStream("c:\\temp.out");
        ObjectInputStream oin = new ObjectInputStream(fis);
        Q q2 = (Q)oin.readObject();
        fis.close();
        q2.printValue();

    }
}

答案 4 :(得分:0)

  

如果字段的访问说明符发生更改

,则反序列化会失败吗?

没有

  

或者反序列化结束时类中缺少部分或全部方法?

是的,除非接收类的serialVersionUID成员的值等于流中编码的值。

  

在反序列化期间为字段分配值的机制是什么?

太宽泛了,但是:

  • 反思,
  • 名称匹配(而不是按类和流中的位置匹配)。