Java序列化Serializable解析

引入

我们知道在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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值