博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
分析轮子(四)- 我也玩一把 Serializable.java
阅读量:6690 次
发布时间:2019-06-25

本文共 11906 字,大约阅读时间需要 39 分钟。

前言:在写  的时候曾经下过一个结论 “实现Serializable接口,表示ArrayList是可序列化的”,这个结论是以往学习的经验所得,并且平时在编程的时候也遇到过其他的问题,比如:在写  的时候,其实就遇到了一个对象序列化和反序列化相关的问题,后来解决了,不过没有深入下去和总结一下。编程这件事情,最好实验一把,就算是他人已经研究明白的东西,自己如果不动手试试,可能印象总不是特别的深刻,哪怕随便玩一下,也许都会有完全不同的收获。

 

注:玩的是JDK1.7版 纸上得来终觉浅,绝知此事要躬行。(自勉之。。。

 

一:如果不实现 Serializable.java 接口,我要序列化对象会怎样呢?

1)来个简单的Bean,故意不实现 Serializable.java接口,如下所示

/** * @description:人类 * @author:godtrue * @create:2018-09-09 */public class Person {
//注意这里哈! /** * 身份证号 */ private int id; /** * 姓名 */ private String name; /** * 性别 */ private boolean sex; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isSex() { return sex; } public void setSex(boolean sex) { this.sex = sex; } @Override public String toString() { final StringBuilder sb = new StringBuilder("Person{"); sb.append("id=").append(id); sb.append(", name='").append(name).append('\''); sb.append(", sex=").append(sex); sb.append('}'); return sb.toString(); }}

2)来个序列化和反序列化的测试类,跑一下,看看情况如何?

/** * @description:序列化和反序列化测试类 * @author:godtrue * @create:2018-09-09 */public class SerializeAndDeserialize{    /**    *    *@description: 测试入口,主方法    *@param args    *@return: void    *@author: godtrue    *@createTime: 2018-09-09    *@version: v1.0    */    public static void main(String[] args){        Person person = new Person();        person.setId(1111);        person.setName("双十一");        person.setSex(true);        try {            serializePerson(person);            Person personTemp = deserializePerson();            System.out.println(personTemp);        }catch (Exception e){            e.printStackTrace();        }    }    /**    *    *@description: 序列化人类对象方法    *@param person    *@return: void    *@author: godtrue    *@createTime: 2018-09-09    *@version: v1.0    */    private static void serializePerson(Person person) throws IOException{        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D://PersonInfo.text"));        objectOutputStream.writeObject(person);        System.out.println("serialize person success");        objectOutputStream.close();    }    /**    *    *@description: 反序列化人类方法    *@param    *@return: com.godtrue.Person    *@author: gotrue    *@createTime: 2018-09-09    *@version: v1.0    */    private static Person deserializePerson() throws ClassNotFoundException,IOException{        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D://PersonInfo.text"));        Person person = (Person) objectInputStream.readObject();        System.out.println("deserialize person success");        return person;    }}

3)不实现 Serializable.java 接口,进行对象序列化的后果很严重(序列化不成),并且抛出 java.io.NotSerializableException 异常

Connected to the target VM, address: '127.0.0.1:53429', transport: 'socket'java.io.NotSerializableException: com.godtrue.Person    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1183)    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:347)    at com.godtrue.SerializeAndDeserialize.serializePerson(SerializeAndDeserialize.java:28)    at com.godtrue.SerializeAndDeserialize.main(SerializeAndDeserialize.java:18)Disconnected from the target VM, address: '127.0.0.1:53429', transport: 'socket'Process finished with exit code 0

4)跟一下,看看这个异常时哪里抛出来的

/**     * Write the specified object to the ObjectOutputStream.  The class of the     * object, the signature of the class, and the values of the non-transient     * and non-static fields of the class and all of its supertypes are     * written.  Default serialization for a class can be overridden using the     * writeObject and the readObject methods.  Objects referenced by this     * object are written transitively so that a complete equivalent graph of     * objects can be reconstructed by an ObjectInputStream.     *     * 

Exceptions are thrown for problems with the OutputStream and for * classes that should not be serialized. All exceptions are fatal to the * OutputStream, which is left in an indeterminate state, and it is up to * the caller to ignore or recover the stream state. * * @throws InvalidClassException Something is wrong with a class used by * serialization. * @throws NotSerializableException Some object to be serialized does not * implement the java.io.Serializable interface. * @throws IOException Any exception thrown by the underlying * OutputStream. */ public final void writeObject(Object obj) throws IOException { if (enableOverride) { writeObjectOverride(obj); return; } try { writeObject0(obj, false);//往这里走啦! } catch (IOException ex) { if (depth == 0) { writeFatalException(ex); } throw ex; } }

/**     * Underlying writeObject/writeUnshared implementation.     */    private void writeObject0(Object obj, boolean unshared)        throws IOException    {        boolean oldMode = bout.setBlockDataMode(false);        depth++;        try {            // handle previously written and non-replaceable objects            int h;            if ((obj = subs.lookup(obj)) == null) {                writeNull();                return;            } else if (!unshared && (h = handles.lookup(obj)) != -1) {                writeHandle(h);                return;            } else if (obj instanceof Class) {                writeClass((Class) obj, unshared);                return;            } else if (obj instanceof ObjectStreamClass) {                writeClassDesc((ObjectStreamClass) obj, unshared);                return;            }            // check for replacement object            Object orig = obj;            Class cl = obj.getClass();            ObjectStreamClass desc;            for (;;) {                // REMIND: skip this check for strings/arrays?                Class repCl;                desc = ObjectStreamClass.lookup(cl, true);                if (!desc.hasWriteReplaceMethod() ||                    (obj = desc.invokeWriteReplace(obj)) == null ||                    (repCl = obj.getClass()) == cl)                {                    break;                }                cl = repCl;            }            if (enableReplace) {                Object rep = replaceObject(obj);                if (rep != obj && rep != null) {                    cl = rep.getClass();                    desc = ObjectStreamClass.lookup(cl, true);                }                obj = rep;            }            // if object replaced, run through original checks a second time            if (obj != orig) {                subs.assign(orig, obj);                if (obj == null) {                    writeNull();                    return;                } else if (!unshared && (h = handles.lookup(obj)) != -1) {                    writeHandle(h);                    return;                } else if (obj instanceof Class) {                    writeClass((Class) obj, unshared);                    return;                } else if (obj instanceof ObjectStreamClass) {                    writeClassDesc((ObjectStreamClass) obj, unshared);                    return;                }            }            // remaining cases            if (obj instanceof String) {                writeString((String) obj, unshared);            } else if (cl.isArray()) {                writeArray(obj, desc, unshared);            } else if (obj instanceof Enum) {                writeEnum((Enum) obj, desc, unshared);            } else if (obj instanceof Serializable) {                writeOrdinaryObject(obj, desc, unshared);            } else {                if (extendedDebugInfo) {                    throw new NotSerializableException(                        cl.getName() + "\n" + debugInfoStack.toString());                } else {                    throw new NotSerializableException(cl.getName());//最终在这里抛出了对应的异常                }            }        } finally {            depth--;            bout.setBlockDataMode(oldMode);        }    }

二:嗯,那好吧!现在终于明白如果不实现 Serializable.java 接口,序列化对象的后果了,那就实现一下吧!毕竟JAVA平台都这么规定了,除非换个平台了!

1)调整 Person.java 类,使其实现 Serializable.java 接口,如下所示

/** * @description:人类 * @author:godtrue * @create:2018-09-09 */public class Person implements Serializable{
//注意这里啦! /** * 身份证号 */ private int id; /** * 姓名 */ private String name; /** * 性别 */ private boolean sex; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isSex() { return sex; } public void setSex(boolean sex) { this.sex = sex; } @Override public String toString() { final StringBuilder sb = new StringBuilder("Person{"); sb.append("id=").append(id); sb.append(", name='").append(name).append('\''); sb.append(", sex=").append(sex); sb.append('}'); return sb.toString(); }}

2)序列化和反序列化的测试类 原封未动,这里就不贴出来了

3)Person.java 实现 Serializable.java 后,对象的序列化和反序列化都成功了,如下所示

serialize person successdeserialize person successPerson{id=1111, name='双十一', sex=true}Process finished with exit code 0

4)序列化到文件中的信息,如下所示

 

三:serialVersionUID 的作用

实验步骤:

1)先将 Person.java 类的对象序列化到 D://PersonInfo.text 文件中(在Person.java类中没有显示声明 serialVersionUID)

2)为Person.java 类添加一个年龄属性

/**     * 年龄     */    private int age;

3)反序列化 D://PersonInfo.text 文件中对象信息

4)执行结果如下(抛出了 java.io.InvalidClassException ):

Connected to the target VM, address: '127.0.0.1:51721', transport: 'socket'Disconnected from the target VM, address: '127.0.0.1:51721', transport: 'socket'java.io.InvalidClassException: com.godtrue.Person; local class incompatible: stream classdesc serialVersionUID = 2168487965208983906, local class serialVersionUID = 7553450974679663255    at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:617)    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1622)    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1517)    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771)    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)    at com.godtrue.SerializeAndDeserialize.deserializePerson(SerializeAndDeserialize.java:63)    at com.godtrue.SerializeAndDeserialize.main(SerializeAndDeserialize.java:28)Process finished with exit code 0

5)分析——看抛错的日志信息,引起这个问题的原因是因为,序列化和反序列化信息中的 serialVersionUID 这个属性的值不一致造成的,那问题来了 serialVersionUID 这个属性哪里来的?这个属性的值又是哪里来的?为什么同一个对象的序列化和反序列化信息中的有些信息不一样呢?

5-1)serialVersionUID 这个属性是JDK工具添加上去 5-2)serialVersionUID 这个属性的值,如果没有显式指明,则会由JDK工具自动生成,如果显式声明了,则使用显式声明的 5-3)serialVersionUID 这个属性的默认生成规则,和类中的信息有关,如果类有所改变,则会影响此属性的值的生成 6)继续试验下,5)中的结论,试验步骤如下: 6-1)先将Person.java中的 age 属性去掉,然后显式的声明 private static final long serialVersionUID=1L; 6-2)然后,将 Person.java 类对应的对象信息,序列化到 D://PersonInfo.text 文件中 6-3)然后,将 Person.java 类的 age 属性再添加回去 6-4)然后,将Person.java 类的对象信息反序列化为对象, 6-5)试验ok了,序列化和反序列化都没有问题,只是反序列化后的对象信息中 age 属性的值是默认属性 0
7)继续试验,逆向的验证一下反序列化,试验步骤如下:
7-1)先将Person.java中的 age 属性添加上,然后显式的声明 private static final long serialVersionUID=1L; 7-2)然后,将 Person.java 类对应的对象信息,序列化到 D://PersonInfo.text 文件中 7-3)然后,将 Person.java 类的 age 属性再去掉 7-4)然后,将Person.java 类的对象信息反序列化为对象, 7-5)试验ok了,序列化和反序列化都没有问题,只是,反序列化后的对象信息中没有 age 属性而已,(去掉后,反序列化当然是没有的) 8)从上述的试验中,我们可以发现一些有趣的事情 8-1)当 serialVersionUID 属性的值,不一致时,即使是同一个类,他的序列化和反序列化前后的属性没有增减,也是不能正确反序列化的 8-2)当 serialVersionUID 属性的值,一致时,同一个类,他的序列化和反序列化前后的属性有增减,也能正确反序列化的 参考:

 

转载地址:http://qjkoo.baihongyu.com/

你可能感兴趣的文章
磁带机Media is unrecognized
查看>>
DH密钥交换非对称加密
查看>>
程序员的量化交易之路(19)--Cointrader之Bar实体(7)
查看>>
[Android]getevent,sendevent,input命令的使用
查看>>
开始转移精力,研究BI方向
查看>>
Android配置----adb工具的使用
查看>>
TNS-12502: TNS:listener received no CONNECT_DATA from client
查看>>
【DB2 学习】在复原过程中重定义表空间
查看>>
【mongodb系统学习之八】mongodb shell常用操作
查看>>
教你如何封装异步网络连接NSURLConnection实现带有百分比的下载
查看>>
【RAC】单节点 重启 报ORA-1105 ORA-01606
查看>>
Java IO: 流
查看>>
剑指offer系列之三:在二维数组中查找元素
查看>>
【springmvc+mybatis项目实战】杰信商贸-26.出货表修饰+下载
查看>>
【Android开发】图形图像处理技术-旋转、缩放、倾斜和平移图像
查看>>
简易Java爬虫制作
查看>>
linux中生成考核用的GPT分区结构样例(二)
查看>>
我的友情链接
查看>>
编辑vi 查看网卡命令
查看>>
常见的内存错误及其对策
查看>>