Android序列化-Serializable接口

来源:互联网 发布:一键加群好友软件 编辑:程序博客网 时间:2024/05/16 23:02

场景,有一件礼物Gift(对象)我要把它送给远在国外的女票,怎么办?只能把这件礼物放在包裹里邮寄,女票收到之后在打开包裹得到这个礼物。那么我的理解Serializeable就是干这件事的。

Serializable是Java里面所提供的一个序列化接口,它只是一个空接口,内部并没有任何方法,是一个典型的标识接口(注1)。为我们提供了标准的序列化与反序列化操作。Serializable的使用非常简单,只要把想实现序列化的对象Gift 实现该接口,并添加下面一行代码就可以private static final long serialVersionUID=8712312312312321312L。其实serialVersoinUID也不是必须的,如果没有手动添加,那么也会自动生成一个。不过会对反序列化产生影响。下面我们看一个Gift实例:

public  class Gift implements Serializable {    public static final long serialVersionUID = 8098080809810941L;    public String name;    public int size;    public float price;    public Gift(String name, int size, float price) {        this.name = name;        this.size = size;        this.price = price;    }    @Override    public String toString() {        return "Gift{" +                "name='" + name + '\'' +                ", size=" + size +                ", price=" + price +                '}';    }}


通过Serializable实现序列化非常简单,实现完Serializable接口的Gift就是一个可以进行序列化与反序列化的对象。那么如何进行序列化和反序列化呢?也非常简单:

  try {            //序列化过程            Gift giftSrc = new Gift("lv", 20, 10000f);            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(Environment.getExternalStorageDirectory().toString() + "/gift.txt"));            out.writeObject(giftSrc);            out.close();            //反序列化过程            ObjectInputStream in = new ObjectInputStream(new FileInputStream(Environment.getExternalStorageDirectory().toString() + "/gift.txt"));            Gift giftSerial = (Gift) in.readObject();            in.close();            Log.e(TAG, "反序列化:" + giftSerial.toString());        } catch (IOException e) {            e.printStackTrace();        } catch (ClassNotFoundException e) {            e.printStackTrace();        }

 运行结果:

可以看到,和我们之前序列化的Gift对象内容一致。

上述序列化的过程:把实现了Serializable接口的Gift通过流写入到本地文件。

反序列的过程:通过流把本地文件中的内容读取出来,构建一个Gift对象,不过这时候的Gift对象与原来的Gift虽然内容一致,但是其本质并不是同一个对象。


serialVersionUID的作用:

serialVersionUID被称为序列化版本号,鉴别反序列时生成的类对象是不是之前序列化的类对象。

我们之前说过不手动生成serialVersonUID也是可以实现序列化的,只是对反序列化有些影响。那么我们就来详细的说一下手动和自动两种情况。

1.隐式:不手动生成,系统会根据类的结构自动生成其Hash值。而我们在序列化的时候会把生成serialVersionUID也一起写进本地文件。反序列化时系统会把之前写进去的serialVersionUID和之前写进去的做对比,如果一致,那么可以反序列化,如果不一致,那么反序列化失败Cash! 所以系统自动生成这种方式对对象的一致性要求很高,不能随意增删改。例如删除一个成员变量,那么系统会自动计算当前类的hash值并把它赋值给serialVersionUID,那么当前类的serialVersionUID就和反序列话中的不一致,造成反序列化失败。

2.显式:比如1L或者根据类名,接口名,成员方法以及属性等生成一个64为的hash并赋值给serialVersionUID,其余的参数我们可以进行修改,这种方式可以很大程度上避免反序列化的失败。

代码示例:在上面的基础上我们使用自动生成,序列化之后我们更改一个成员变量。

    public static class Gift implements Serializable {        //        public static final long serialVersionUID = 8098080809810941L;        public String name;        public int size;        public float price;        public String place;        public Gift(String name, int size, float price, String place) {            this.name = name;            this.size = size;            this.price = price;            this.place = place;        }        @Override        public String toString() {            return "Gift{" +                    "name='" + name + '\'' +                    ", size=" + size +                    ", price=" + price +                    ", place='" + place + '\'' +                    '}';        }    }
反序列化运行结果:

很明显由于我们修改了Gift的成员变量,导致系统自动重新计算生成了一个新的serialVersionUID,其与反序列化中我们之前写进去的不同,所以不认为是同一个类,反序列化失败。

Tips:静态成员不属于类所以不参与序列化,transient关键字同样。

注1:标识接口是没有任何方法和属性的接口。标识接口不对实现它的类有任何语义上的要求,它仅仅表明实现它的类属于一个特定的类型。
标接口在Java语言中有一些很著名的应用,比如java.io.Serializable和java.rmi.Remote等接口便是标识接口。标识接口,当一个类实现了一个标识接口之后就像是给自己打了个标签。
打个比喻,不是很恰当。就像是一个人穿了件名牌衣服(实现了标识接口),别人一看他穿的衣服(标识接口)就知道他的品味、身份(特性)。

                                             
0 0
原创粉丝点击