序列化/反序列化机制

5
说,我有一个类 X,它有一个字段值,也就是说,
class X implements Serializable {
    private int value;
    // ...
}

此外,它还有未在此处显示的getter和setter。该类已序列化。 在反序列化时,相同的类具有值字段,并且访问修饰符为public。此外,该类没有getter和setter。因此,我的问题是:

  1. 如果字段的访问修饰符发生更改或在反序列化结束时类中缺少某些或所有方法,是否会导致反序列化失败?
  2. 在反序列化期间,字段如何分配其值的机制是什么?

此链接提供了序列化的工作原理:http://oreilly.com/catalog/javarmi/chapter/ch10.html - Dead Programmer
1
@DeadProgrammer:链接已损坏。 - RenniePet
5个回答

5

一些好的链接 Java序列化算法揭秘

1)如果字段的访问修饰符发生更改或在反序列化端缺少某些或所有方法,那么反序列化是否会失败?

序列化使用反射进行。

Java使用私有静态final serialVersionUID来检测类的更改。

默认情况下涉及哈希码。序列化从以下信息创建单个哈希码(长整型):

  • 类名和修饰符

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

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

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

序列化机制的默认行为是经典的“安全第一”策略。序列化机制使用suid(默认为极其敏感的索引)来判断类是否已更改。如果是,则序列化机制拒绝使用旧类的数据创建新类的实例。

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


所以 serialVersionUID 不关心私有字段,但反序列化过程本身确实关心吗? - Markus Barthlen

3
真正的细节可以在Java对象序列化规范中阅读。
回答你的问题:
1.序列化具有基本的健全性检查,以查看序列化结束时是否使用相同版本的类:serialVersionUID成员必须相等。阅读流唯一标识符部分以了解更多信息。基本上,它是一个静态值,你既可以通过在你的类中声明它来管理它自己,也可以让编译器为你生成一个。如果编译器生成它,任何对类的更改都将导致serialVersionUID的更改,因此如果两端没有完全相同的类,则反序列化将失败。如果你想避免这种情况,请自行声明变量,并在类的成员变量发生更改时手动更新它,以使类不兼容。
2.Java虚拟机在此处执行了大量的操作,它可以直接访问所有内部状态而无需getter(标记为transientstatic的字段不会被序列化)。此外,虽然Serializable接口没有指定要实现的任何方法,但有一些“magic methods”可以声明以影响序列化过程。阅读“writeObject方法”及其后续部分以了解更多信息。需要注意的是,你应该谨慎使用这些方法,因为它们可能会让维护开发人员感到困惑!

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反射来获取结果。请确保使用getDeclaredFields();getDeclaredMethods();而不是方法名称中没有Declared的变量。


你不知道他是如何得到“什么”结果的吗?问题中没有提到任何结果。 - user207421

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();

    }
}

0
反序列化在字段访问修饰符更改的情况下是否失败?
不会。
或者在反序列化端类中缺少某些或所有方法?
是的,除非接收类具有一个serialVersionUID成员,其值等于流中编码的值。
字段在反序列化期间如何分配其值的机制是什么?
太广泛了,但是:
- 反射和 - 名称匹配(而不是按类和流中的位置匹配)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接