在java中,如果一个类实现了Serializable但是是抽象的,它是否应该声明一个longVersionUID,或者子类只需要那个?
在这种情况下,所有子类确实打算处理序列化,因为该类型的目的是在RMI调用中使用。
答案 0 :(得分:45)
提供serialVersionUID以确定deseralized对象与类的当前版本之间的兼容性。 因此,在类的第一个版本中,或者在这种情况下,在抽象基类中不是必需的。你永远不会有一个抽象类的实例来序列化/反序列化,因此它不需要serialVersionUID。
(当然,它会生成一个编译器警告,你想摆脱它,对吗?)
事实证明詹姆斯的评论是正确的。抽象基类的serialVersionUID 会传播到子类。鉴于此,您做需要基类中的serialVersionUID。
要测试的代码:
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以使其创建并保存对象。然后更改Base类中的serialVersionUID,注释掉main中保存对象的行(因此它不再保存它,你只想加载旧的),然后再次运行它。这将导致异常
java.io.InvalidClassException: Base; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
答案 1 :(得分:6)
是的,通常,出于同样的原因,任何其他类都需要一个序列ID - 以避免为它生成一个。基本上,任何实现serializable的类(不是接口)都应该定义串行版本ID,否则当同一个.class编译不在服务器和客户端JVM中时,就会出现反序列化错误。
如果你想尝试做些什么,还有其他选择。我不确定你的意思是“这是子类的目的......”。你打算编写自定义序列化方法(例如writeObject,readObject)吗?如果是这样,还有其他选择来处理超类。
请参阅: http://java.sun.com/javase/6/docs/api/java/io/Serializable.html
HTH汤姆
答案 2 :(得分:2)
实际上,如果缺少serialVersionID
,则指出Tom的链接实际上是由序列化运行时计算的,即不是在编译期间
如果可序列化类没有显式声明a serialVersionUID,然后序列化运行时将计算一个 基于各个方面的该类的默认serialVersionUID值 班级......
这使得JRE的版本变得更加复杂。
答案 3 :(得分:0)
从概念上讲,序列化数据如下所示:
subClassData(className + version + fieldNames + fieldValues)
parentClassData(className + version + fieldNames + fieldValues)
... (up to the first parent, that implements Serializable)
因此,当您反序列化时,层次结构中任何类中的版本不匹配都会导致反序列化失败。接口没有存储任何内容,因此无需为它们指定版本。
答案:是的,您还需要在基本抽象类中提供serialVersionUID
。即使它没有字段(即使没有字段也存储className + version
)。
另请注意以下事项:
Object
字段值为String
,则将字段类型更改为String
会成功,但更改为Integer
会赢。但是,即使您可以将int
值分配给long
变量,也不会将字段从int
更改为long
。简单来说:您可以重新排序字段,添加和删除字段,甚至更改类层次结构。你不应该重命名字段或类(它不会失败,但是值不会被反序列化)。您不能更改具有基本类型的字段类型,并且您可以更改引用类型字段,前提是新类型可从所有值分配。
注意:如果基类没有实现Serializable且只有子类实现,那么基类中的字段将表现为transient
。