java序列化的版本管理

来源:互联网 发布:saas java开源架构 编辑:程序博客网 时间:2024/04/30 20:37

本文是针对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
原创粉丝点击