java 序列化版本号_java序列化的版本管理

本文是针对java序列化的版本管理进行阐述的,请大家先看个例子,

一个用于网络传输的实体类FInterfaceObject,程序中是这样使用的:

public class SeriaTest

{

public static void main(String[] args)

{

String fileUrl = "./example";

FInterfaceObject fIObjOutput =FInterfaceObject.staticFinalFIObj;

write(fileUrl,fIObjOutput);

FInterfaceObject obj =(FInterfaceObject)read(fileUrl);

System.out.println(obj);

}

public static void write(String fileUrl,FInterfaceObjectobj)

{

ObjectOutputStream out = null;

try

{

out = new ObjectOutputStream(newFileOutputStream(fileUrl));

out.writeObject(obj);

}

catch (FileNotFoundException e)

{

e.printStackTrace();

}

catch (IOException e)

{

e.printStackTrace();

}

finally

{

if(out != null)

{

try

{

out.close();

}

catch (IOException e)

{

e.printStackTrace();

}

}

}

}

public static FInterfaceObject read(StringfileUrl)

{

ObjectInputStream in = null;

FInterfaceObject fIObjInput = null;

try

{

in = new ObjectInputStream(newFileInputStream(fileUrl));

fIObjInput = (FInterfaceObject)in.readObject();

}

catch (ClassNotFoundException e)

{

e.printStackTrace();

}

catch (FileNotFoundException e)

{

e.printStackTrace();

}

catch (IOException e)

{

e.printStackTrace();

}

finally

{

if(in != null)

{

try

{

in.close();

}

catch (IOException e)

{

e.printStackTrace();

}

}

}

return fIObjInput;

}

}

类FInterfaceObject的定义如下:

public class FInterfaceObjectimplements Serializable

{

public static finalFInterfaceObjectstaticFinalFIObj = newFInterfaceObject(1);

private int id = 0;

private FInterfaceObject (int value)

{

id = value;

}

public String toString()

{

return"id: "+String.valueOf(id);

}

}

此时,执行程序SeriaTest.java,输出打印信息为:id: 1

之后,由于需求变化,需要给FInterfaceObject新增个域:String name,类定义变为:

public class FInterfaceObject implements Serializable

{

public static final FInterfaceObject staticFinalFIObj = newFInterfaceObject(1);

private int id = 0;

private Stringname = "object1";

private FInterfaceObject(int value)

{

id = value;

}

public String toString()

{

return"id: "+String.valueOf(id) +"name: "+name;

}

}

此时,将程序SeriaTest.java的main()方法改为如下,只写出,且写出到example2中,后面会用到该文件,暂且放下。

public static void main(String[] args)

{

String fileUrl = "./example2";

FInterfaceObject fIObjOutput = FInterfaceObject.staticFinalFIObj;

write(fileUrl,fIObjOutput);

}

现在,再次修改程序SeriaTest.java的main()方法为只读入example文件,去掉写出的两行代码:

public static void main(String[] args)

{

String fileUrl = "./example";

FInterfaceObject obj =(FInterfaceObject)read(fileUrl);

System.out.println(obj);

}

之后执行程序SeriaTest.java,重新读入之前的输出文件example,大家猜猜会打印什么呢?下面是执行结果:

null

java.io.InvalidClassException: practice.FInterfaceObject; local class incompatible:stream classdesc serialVersionUID = 8436122587265072399, local classserialVersionUID = 2044314194551933020

at java.io.ObjectStreamClass.initNonProxy(UnknownSource)

……

为什么读出来的对象是null?这个异常又是什么意思?

原因就在于java的流机制,拒绝读入序列化版本不同的对象,异常信息表明:流读入的对象类序列化版本为8436122587265072399,而本地程序中该类的序列化版本为:2044314194551933020,两者不一致,因此流拒绝读入而抛java.io.InvalidClassException异常。抛异常后,read方法返回null,因此打印出来是null.

那如何解决这个问题呢?

为了向jvm表示,新类兼容之前版本的类,需要将之前类的序列化版本UID写入新类,做为新类的static final 域。即在类FInterfaceObject中,增加下面一行:

private static final longserialVersionUID = 8436122587265072399L;

增加这行后,再执行程序SeriaTest.java,程序输出:

id: 1 name:null.

example.txt文件中的对象是没有域name的,但java流机制可以兼容处理,对流中对象少于本地类中的属性,根据属性类型的不同,取其对应的默认值(如果是对象则是null,数字则是0,如果是boolean则是false)。若流中对象域多于本地类,则忽略这些域。

需求继续变化,又需要给类FInterfaceObject中,增加一个域:int age,类定义变为:

public class FInterfaceObject implements Serializable

{

private static final long serialVersionUID = 8436122587265072399L;

public static final FInterfaceObject staticFinalFIObj = newFInterfaceObject(1);

private int id = 0;

private Stringname = "object1";

private int age = 1;

private FInterfaceObject(int value)

{

id = value;

}

public String toString()

{

return"id:"+String.valueOf(id) +" name: "+name+" age: "+age;

}

}

此时执行程序SeriaTest.java,如下:

public static void main(String[] args)

{

String fileUrl = "./example";

FInterfaceObject obj =(FInterfaceObject)read(fileUrl);

System.out.println(obj);

}

,程序输出:

id: 1 name: null age: 0.

之后,将程序SeriaTest.java修改为读入文件"./example2",程序输出如下:

java.io.InvalidClassException:practice.FInterfaceObject; local class incompatible: stream classdescserialVersionUID = 2044314194551933020, local class serialVersionUID =8436122587265072399

atjava.io.ObjectStreamClass.initNonProxy(Unknown Source)

……

null

异常信息表明,本地类序列化版本与流读入对象的序列化版本不一致,可以通过将serialVersionUID改为2044314194551933020L成功读入文件"./example2",但若再读入"./example",又会因序列化版本不一致而报该异常。

若碰到此种情况,就不能通过仅仅修改代码解决问题了。就需要在业务层面上,规避该问题。

因此,一个用于持久化或者网络传输的类,其序列化版本号最好在一开始就显示的在类中声明,这样,即使后面类发生多次变化,使用的serialVersionUID都相同,就不会出现序列化版本号跟随类变化,导致流拒绝读入不同版本的对象的现象。Eclipse中,对实现了接口Serializable的类,若未显示声明serialVersionUID,会显示编译警告提示。

以下是java序列化的思考点:

1、静态数据域的序列化(基本类型和Object对象)

2、读的旧类序列化后能调用新的方法吗?

3、使用序列化机制clone

4、readResolve()方法

5、Externalizable接口,实现自己的保存恢复对象数据的方法

6、修改默认的序列化方法

7、transient关键字

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值