如果抽象类有一个serialVersionUID

在java中,如果一个类实现Serializable但是是抽象的,它是否应该有一个serialVersionUID long声明,或者只有子类才需要?

在这种情况下,确实意图是所有的子类都处理序列化,因为types的目的是在RMI调用中使用。

提供serialVersionUID是为了确定deseralized对象和类的当前版本之间的兼容性。 因此,在一个类的第一个版本中,或者在这种情况下,在一个抽象的基类中,它并不是真的有必要。 你永远不会有这个抽象类的实例序列化/反序列化,所以它不需要serialVersionUID。

(当然,它会产生一个编译器警告,你想摆脱,对不对?)

原来詹姆斯的评论是正确的。 抽象基类的serialVersionUID传播到子类。 鉴于此,你需要在你的基类中使用serialVersionUID。

要testing的代码:

import java.io.Serializable; public abstract class Base implements Serializable { private int x = 0; private int y = 0; private static final long serialVersionUID = 1L; public String toString() { return "Base X: " + x + ", Base Y: " + y; } } import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class Sub extends Base { private int z = 0; private static final long serialVersionUID = 1000L; public String toString() { return super.toString() + ", Sub Z: " + z; } public static void main(String[] args) { Sub s1 = new Sub(); System.out.println( s1.toString() ); // Serialize the object and save it to a file try { FileOutputStream fout = new FileOutputStream("object.dat"); ObjectOutputStream oos = new ObjectOutputStream(fout); oos.writeObject( s1 ); oos.close(); } catch (Exception e) { e.printStackTrace(); } Sub s2 = null; // Load the file and deserialize the object try { FileInputStream fin = new FileInputStream("object.dat"); ObjectInputStream ois = new ObjectInputStream(fin); s2 = (Sub) ois.readObject(); ois.close(); } catch (Exception e) { e.printStackTrace(); } System.out.println( s2.toString() ); } } 

运行Sub中的main一次,让它创build并保存一个对象。 然后更改Base类中的serialVersionUID,注释掉保存对象的主行(所以不会再保存,只是要加载旧对象),然后再次运行。 这将导致一个例外

 java.io.InvalidClassException: Base; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2 

是的,一般来说,出于同样的原因,任何其他类需要一个序列号 – 避免为其生成一个。 基本上,任何实现serializable的类(不是接口)都应该定义串行版本ID,否则当相同的.class编译不在服务器和客户机JVM中时,您有发生反序列化错误的风险。

如果你想要做某些事情,还有其他的select。 我不确定你的意思是“这是子类的意图…”。 你要写自定义的序列化方法(如writeObject,readObject)? 如果有的话,还有其他的select来处理一个超类。

请参阅: http : //java.sun.com/javase/6/docs/api/java/io/Serializable.html

HTH汤姆

实际上,如果缺lessserialVersionID ,实际上是通过序列化运行时计算出来的,即不在编译期间,从Tom的链接中指出

如果可序列化类没有显式声明serialVersionUID,则序列化运行时将根据类的各个方面计算该类的默认serialVersionUID值…

这使得具有不同JRE版本的事情变得更加复杂。

从概念上讲,序列化数据如下所示:

 subClassData(className + version + fieldNames + fieldValues) parentClassData(className + version + fieldNames + fieldValues) ... (up to the first parent, that implements Serializable) 

因此,当您反序列化时,层次结构中任何类中版本的不匹配都会导致反序列化失败。 没有为接口存储任何内容,所以不需要为它们指定版本。

答:是的,您也需要在基本抽象类中提供serialVersionUID 。 即使它没有字段(即使没有字段,也会存储className + version )。

还要注意以下几点:

  1. 如果类没有一个字段,这是在序列化的数据(一个被删除的字段)中find的,它将被忽略。
  2. 如果类有一个字段,这是不存在序列化的数据(一个新的领域),它被设置为0 /假/空(不是预期的默认值)。
  3. 如果字段更改数据types,则反序列化的值必须可分配给新types。 例如,如果您有一个带有String值的Object字段,将字段types更改为String将会成功,但更改为Integer不会。 但是,将字段从int更改为long将不起作用,即使您可以将int值分配给longvariables。
  4. 如果子类不再扩展在序列化数据中扩展的父类,则它将被忽略(如情况1)。
  5. 如果一个子类现在扩展了一个在序列化数据中没有find的类,父类字段被恢复为0 / false / null值(如情况2)。

简而言之:您可以对字段进行重新sorting,添加和删除它们,甚至可以更改类层次结构。 你不应该重命名字段或类(它不会失败,但值不会被反序列化)。 您不能使用原始types更改字段的types,并且您可以更改引用types字段,前提是可以从所有值中分配新types。

注意:如果基类没有实现Serializable并且只有子类,那么来自基类的字段将performance为transient