我读过几个关于Java如何控制对可序列化对象的访问的问题和讨论。我的问题是关于一个可外部化的对象,以及为什么serialVersionUID应该影响它。我希望答案是“因为可外部化接口扩展了可序列化接口”,因此该字段是必需的。我不可否认的有些烦人的回答是“为什么可外部化扩展了可序列化?”有两个原因。
第一个原因很简单,要实现外部化,需要重写readExternal和writeExternal,以便将数据传输到类实例和从类实例传输数据。
第二种情况有点复杂,但情况如下:
我的新Foo类,当前包含两个字符串字段,但我预计它会随着我的函数的增长而增长,因为用户更改了一些要求。如果我声明Foo是可序列化的,我可以毫无问题地将对象写入文件,直到我需要更新类以添加另一个字段。现在我不能在单个程序中拥有两个版本的Foo对象,因此现有Foo对象的任何文件都是垃圾,如果不写入一个函数,就无法处理读取当前Foo文件并将其写成文本,然后另一个函数读取文本,为新的Foo字段取值并将其写成序列化形式的更新版本。
所以我的想法是,我应该将Foo声明为可外部化,添加一个内部版本字段,该字段是通过writeExternal函数写入的第一个字段,以便readExternal函数首先读取该字段,其余部分可以根据该值进行控制。
将两个版本并排放置进行比较:
enter code
Original version Updated version
1 public class Foo implements Externalizable { public class Foo implements Externalizable {
2
3 static final private long serialVersionUID=????; static final private long serialVersionUID=????;
4 static final private long VER00=0; static final private long VER00=0;
5 ***** static final private long VER01=1;
6
7 private int fooVer=0; private int fooVer=1;
8 public String dsn="", lst=""; ***** public String dsn="", lst="", ext="";
9 public String getDsn() {return dsn;} public String getDsn() {return dsn;}
10 public String getLst() {return lst;} public String getLst() {return lst;}
11 ***** public String getExt() {return ext;}
12 public void putDsn(String s) {dsn=s;} public void putDsn(String s) {dsn=s;}
13 public void putLst(String s) {lst=s;} public void putLst(String s) {lst=s;}
14 ***** public void putExt(String s) {ext=s;}
15
16 public Foo() {super();} public Foo() {super();}
17
18 @Override @Override
19 public void readExternal(ObjectInput in) public void readExternal(ObjectInput in)
20 throws IOException, ClassNotFoundException { throws IOException, ClassNotFoundException {
21 this.fooVer=in.readLong(); this.fooVer=in.readLong();
22 ***** if (this.fooVer==VER01) {
23 ***** this.dsn=(String) in.readObject();
24 ***** this.lst=(String) in.readObject();
25 ***** this.ext=(String) in.readObject();
26 ***** return;
27 ***** }
28 if (this.fooVer==VER00) { if (this.fooVer==VER00) {
29 this.dsn=(String) in.readObject(); this.dsn=(String) in.readObject();
30 this.lst=(String) in.readObject(); this.lst=(String) in.readObject();
31 ***** this.ext="no ext available";
32 return; return;
33 } }
34 throw new ClassNotFoundException throw new ClassNotFoundException
35 ("unsupported fooVer "+this.fooVer); ("unsupported fooVer "+this.fooVer);
36 } }
37
38 @Override @Override
39 public void writeExternal(ObjectOutput out) public void writeExternal(ObjectOutput out)
40 throws IOException { throws IOException {
41 fooVer=VER00; ***** fooVer=VER01;
42 out.writeLong(fooVer); out.writeLong(fooVer);
43 out.writeObject(dsn); out.writeObject(dsn);
44 out.writeObject(lst); out.writeObject(lst);
45 ***** out.writeObject(ext);
46 } }
47 } }
here
由于Foo控制外部化,我应该能够让值不同,比如0和1,并且仍然正确处理记录,但是如果我使用旧值编写并在第一次读取时使用新值读取,我得到一个InvalidClassException,其中消息是:“本地类不兼容:stream classdesc serialVersionUID = 0, 本地类 serialVersionUID = 1”。
因此,据我所知,无论我多么小心,我都必须为serialVersionUID选择一个值,并且永远不要更改它,除非我想丢失所有早期的数据。因此,我基本上做了很多额外的工作来防止序列化使用serialVersionUID来做它想要做的事情。:/
我错过了什么吗?
我必须简单地为 serialVersionUID 选择一个值,并且永远不要更改它,除非我想丢失所有早期的数据
这是正确的,无论您实现 Ssrializable
还是 Externalizable
,它都适用。