寻找序列化对象的serialVersionUID

24

有没有一种方法可以确定序列化的Java对象生成的serialVersionUID

问题在于,我序列化了一个对象,没有明确指定serialVersionUID。现在反序列化过程抱怨类不兼容。然而,我没有以使其不兼容的方式更改类。因此,我认为只需在类中指定与存储在对象数据中相同的serialVersionUID即可解决问题。为了做到这一点,我需要从序列化数据中读取serialVersionUID


当一个方法被添加到类中时会发生什么?使用修改后的序列化类能否读取旧的序列化对象? - sprezzatura
方法并不构成对象的状态,因此在阅读序列化对象时没有问题,除非您不使用任何serialVersionUID。 - Amitesh Rai
9个回答

33

这对我很有效:

long serialVersionUID = ObjectStreamClass.lookup(YourObject.getClass()).getSerialVersionUID();

希望这也能帮到你。


27

有一种简单的方法可以找到类的serialversionUID-

假设您有一个类,您忘记在其中提到serialversionUID-

import java.io.Serializable;

public class TestSerializable implements Serializable {

}

只需这样做 -

serialver -classpath . TestSerializable

这将打印:

static final long serialVersionUID = 5832063776451490808L;

serialver是JDK附带的一个实用工具


3
只有在你拥有旧版本的TestSerializable类的情况下才能这样做。lewap遇到的问题是,他不知道在序列化对象时,使用旧版本的类时serialVersionUID的值是多少。如果他不再拥有旧版本的类,就无法使用serialver来查找。 - Jesper
这就是版本控制的作用。 - james
回答不错,但如果类导入框架类型(如Context),则在Android上无法正常工作。 - FindOut_Quran

13

您可以通过扩展ObjectInputStream来实现此操作:

public class PrintUIDs extends ObjectInputStream {

  public PrintUIDs(InputStream in) throws IOException {
    super(in);
  }

  @Override
  protected ObjectStreamClass readClassDescriptor() throws IOException,
      ClassNotFoundException {
    ObjectStreamClass descriptor = super.readClassDescriptor();
    System.out.println("name=" + descriptor.getName());
    System.out.println("serialVersionUID=" + descriptor.getSerialVersionUID());
    return descriptor;
  }

  public static void main(String[] args) throws IOException,
      ClassNotFoundException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(baos);
    List<Object> list = Arrays.asList((Object) new Date(), UUID.randomUUID());
    oos.writeObject(list);
    oos.close();
    InputStream in = new ByteArrayInputStream(baos.toByteArray());
    ObjectInputStream ois = new PrintUIDs(in);
    ois.readObject();
  }

}

我认为通过替换该方法返回的描述符,可以读取所有序列化数据,但我尚未尝试。


5

序列化的二进制数据中有与之相关的元数据(类似于头文件)。如果你知道它所在的位置,就可以从元数据中读取值(其中包括SerialVersionUID和其他信息,如类名)。

我认为这篇文章可能会对你有帮助:Java序列化算法揭秘

请注意,这些位是“明文”写入的(除非你明确加密了流),因此HEX编辑器可能就足够看到SerialVersionUID了。


1

获取 serialVersionUID 最简单的方法(特别是在难以找到情况下)是为它定义任何数字,并在反序列化失败时监视堆栈跟踪,它会打印出原始值。


1

1

在此输入图片描述 针对Android开发者,

您还可以从“设置”->“编辑器”->“检查”中启用序列化检查->先启用“没有'serialVersionUID'检查的可序列化类”,然后按ALT+ ENTER,工作室将为您创建serialVersionUID。


0

获取序列化对象的serialVersionUID有更简单的方法 - 只需使用apache commons serialization utils将其反序列化为具有不同serial version uid的相同类,并读取异常消息,该消息将更多或更少如下:

org.apache.commons.lang.SerializationException: java.io.InvalidClassException: my.example.SerializableClass; local class incompatible: stream classdesc serialVersionUID = -1, local class serialVersionUID = -2


0

这正是你应该做的 - 指定自己的static final long serialVersionUID

Serializable的文档中有一个关于它的部分。

除非你已经指定了serialVersionUID,否则我认为没有简单的方法可以获取它,除了像@WMR建议的那样解密流。


请注意,它应该被命名为serialVersionUID(而不是serialversionuid - Java区分大小写),并且它应该是private static final long。 - Jesper
我同意。但是有没有一种方法可以从序列化数据中读取生成的UID? - paweloque

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