Java序列化Serializable解析

来源:互联网 发布:壹卡会可以在淘宝用吗 编辑:程序博客网 时间:2024/05/17 08:29

引入

我们知道在java中一切即对象,那我们如果想存储对象或者传送对象时该怎么办?对象又不是字节或者字符,不能直接用输入输出流来进行读写。这时就用到序列化了。

概念

维基百科上的定义是:

对同步控制而言,表示强制在同一时间内进行单一存取。
在数据储存与传送的部分是指将一个对象存储至一个储存媒介,例如档案或是记亿体缓冲等,或者透过网络传送资料时进行编码的过程,可以是字节或是XML等格式。而字节的或XML编码格式可以还原完全相等的对象。这程序被应用在不同应用程序之间传送对象,以及服务器将对象储存到档案或数据库。相反的过程又称为反序列化。

这篇博客主要讲数据储存与传送的定义。我们知道在java中一切即对象,那我们如果想存储对象或者传送对象时该怎么办?对象又不是字节或者字符,不能直接用输入输出流来进行读写。

那么这时我们对对象的操作就应该把对象转换为字节序列,而后采用输入输出流来进行操作。比如存储到硬盘或者在网络中传输,以达到传递对象的目的。

当你从硬盘中取出或者在网络中接收到字节序列后,再转换为对象,即反序列化

实现

只有实现了Serializable和Externalizable接口的类的对象才能被序列化。

ObjectOutputStream类中的writeObject()方法用来写序列化的对象。ObjectInputStream的readObject()方法读取对象。

import java.io.*;public class Main {    public static void main(String[] args)throws Exception {        Person person = new Person();        person.setName("小明");        person.setAge(19);        person.setSex("男");        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("/Users/lixingyu/object.txt")));        oo.writeObject(person);        oo.close();        System.out.print("write ok !\n");        ObjectInputStream oi = new ObjectInputStream(new FileInputStream(new File("/Users/lixingyu/object.txt")));        Person person1 = (Person) oi.readObject();        System.out.print(person1.getName()+"\n");        System.out.print("read ok!\n");    }    static class Person implements Serializable{        private static final long serialVersionUID = -5809782578272943999L;        public String name;        public int age;        public String sex;        public String getName() {            return name;        }        public void setName(String name) {            this.name = name;        }        public int getAge() {            return age;        }        public void setAge(int age) {            this.age = age;        }        public String getSex() {            return sex;        }        public void setSex(String sex) {            this.sex = sex;        }    }}

s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D

注意到上面的代码中有个s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D,而且只有一个初始化,下面的代码并没有用到该变量,那么它是干什么的呢?
如果我们对上面的内部类做个修改,删除该变量,先将对象存入硬盘,再在Person类中增加一个变量friend,读取会有什么结果呢?(这里要运行两次,一次只存,第二次,增加变量后只读。

import java.io.*;public class Main {    public static void main(String[] args)throws Exception {        Person person = new Person();        person.setName("小明");        person.setAge(19);        person.setSex("男");//        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("/Users/lixingyu/object.txt")));//        oo.writeObject(person);//        oo.close();        System.out.print("write ok !\n");        ObjectInputStream oi = new ObjectInputStream(new FileInputStream(new File("/Users/lixingyu/object.txt")));        Person person1 = (Person) oi.readObject();        System.out.print(person1.getName()+"\n");        System.out.print("read ok!\n");    }    static class Person implements Serializable{//        private static final long serialVersionUID = -5809782578272943999L;        public String name;        public int age;        public String sex;        public String friends;        public String getName() {            return name;        }        public void setName(String name) {            this.name = name;        }        public int getAge() {            return age;        }        public void setAge(int age) {            this.age = age;        }        public String getSex() {            return sex;        }        public void setSex(String sex) {            this.sex = sex;        }    }}

你会看到下面的报错信息:

Exception in thread "main" java.io.InvalidClassException: Main$Person; local class incompatible: stream classdesc serialVersionUID = -3488593190474644338, local class serialVersionUID = 1566115588131499685    at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)    at Main.main(Main.java:15)    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)    at java.lang.reflect.Method.invoke(Method.java:497)    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

事实上,如果你没有为类添加serializableUID,编译器会自动添加生产一个ID,而当我们对实现序列化的接口类的源代码文件,做修改,即使是只添加一个空格,编译器也会生成一个不同的ID,这样当读取之前存放到硬盘上的序列化对象时,编译器会报错,拒绝载入。

所以serializableUID是用来区别序列化对象的,必须要添加,否则很容易出错。

用途

比如上面的从硬盘中存取对象;在android开发中利用Intent在Activity之间传递对象时,有一种方法是,要求这个对象必须是序列化的;还有通过网络传递对象。

4 1
原创粉丝点击