分析轮子(四)- 我也玩一把 Serializable.java
前言:在写 分析轮子(一)-ArrayList.java 的时候曾经下过一个结论 “实现Serializable接口,表示ArrayList是可序列化的”,这个结论是以往学习的经验所得,并且平时在编程的时候也遇到过其他的问题,比如:在写 IDEA使用笔记(八)——自动生成 serialVersionUID 的设置 的时候,其实就遇到了一个对象序列化和反序列化相关的问题,后来解决了,不过没有深入下去和总结一下。编程这件事情,最好实验一把,就算是他人已经研究明白的东西,自己如果不动手试试,可能印象总不是特别的深刻,哪怕随便玩一下,也许都会有完全不同的收获。
注:玩的是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.
*
* <p>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 success
deserialize person success
Person{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://www.oracle.com/technetwork/articles/java/javaserial-1536170.html
https://www.ibm.com/developerworks/cn/java/j-lo-serial/index.html
https://www.cnblogs.com/xdp-gacl/p/3777987.html
http://ju.outofmemory.cn/entry/298220
https://blog.csdn.net/qq_27093465/article/details/78544505
https://blog.csdn.net/jason_279/article/details/52947093
https://blog.csdn.net/u011784767/article/details/78156319?locationNum=2&fps=1
https://www.cnblogs.com/wangg-mail/p/4354709.html
https://blog.csdn.net/leixingbang1989/article/details/50556966
https://www.cnblogs.com/DSNFZ/articles/7618470.html
http://www.cnblogs.com/huhx/p/serializable.html
https://blog.csdn.net/so_geili/article/details/78931742
https://www.oschina.net/question/4873_23270
https://www.cnblogs.com/qq3111901846/p/7894532.html
https://www.cnblogs.com/gtaxmjld/p/4866931.html
https://blog.csdn.net/zhangliao613/article/details/51086562
分析轮子(四)- 我也玩一把 Serializable.java的更多相关文章
- 分析轮子(二)- << ,>>,>> (左移、右移、无符号右移)
前言:写 分析轮子(一)-ArrayList.java 的时候看到源码中有 int newCapacity = oldCapacity + (oldCapacity >> 1); 这样的代 ...
- 分析轮子(十)- HashMap.java 之概念梳理
注:玩的是JDK1.7版本 一:还是原来的风格,先上一下类的继承关系图,这样能够比较清楚的知道此类的相关特性 二:HashMap.java 的代码比较难看,所以,我看了几天,写的话也分开来写,这样能表 ...
- 分析轮子(五)- Vector.java
注:玩的是JDK1.7版本 一: 先上类图,从类图上看和 ArrayList.java 非常相像,可查看 分析轮子(一)-ArrayList.java 二:然后看源码,发现和 ArrayList.ja ...
- 漫谈可视化Prefuse(四)---被玩坏的Prefuse API
这个双12,别人都在抢红包.逛淘宝.上京东,我选择再续我的“漫谈可视化”系列(好了,不装了,其实是郎中羞涩...) 上篇<漫谈可视化Prefuse(三)---Prefuse API数据结构阅读有 ...
- Linux内核分析(四)----进程管理|网络子系统|虚拟文件系统|驱动简介
原文:Linux内核分析(四)----进程管理|网络子系统|虚拟文件系统|驱动简介 Linux内核分析(四) 两天没有更新了,上次博文我们分析了linux的内存管理子系统,本来我不想对接下来的进程管理 ...
- 分析轮子(九)- Cloneable.java
注:玩的是JDK1.7版本 一:Cloneable.java 接口也是标记接口,所以,它没有任何方法和属性,实现此接口表示的意思是:可以调用 Object.java 类的 clone() 方法,进行简 ...
- Linux内核分析第四章 读书笔记
Linux内核分析第四章 读书笔记 第一部分--进程调度 进程调度:操作系统规定下的进程选取模式 面临问题:多任务选择问题 多任务操作系统就是能同时并发地交互执行多个进程的操作系统,在单处理器机器上这 ...
- Flume 1.7 源代码分析(四)从Source写数据到Channel
Flume 1.7 源代码分析(一)源代码编译 Flume 1.7 源代码分析(二)总体架构 Flume 1.7 源代码分析(三)程序入口 Flume 1.7 源代码分析(四)从Source写数据到C ...
- 根文件系统的构建与分析(四)之瑞士军刀busybox生成系统基本命令
根文件系统的构建与分析(四) 转载请注明 http://blog.csdn.net/jianchi88 Author:Lotte 邮箱:baihaowen08@126.com ls /bin, ...
随机推荐
- HDU 3488 Tour (最大权完美匹配)【KM算法】
<题目链接> 题目大意:给出n个点m条单向边边以及经过每条边的费用,让你求出走过一个哈密顿环(除起点外,每个点只能走一次)的最小费用.题目保证至少存在一个环满足条件. 解题分析: 因为要求 ...
- HDU4578 Transformation【线段树】
<题目链接> <转载于 >>> > 题目大意: 有一个序列,有四种操作: 1:区间[l,r]内的数全部加c. 2:区间[l,r]内的数全部乘c. 3:区间[l ...
- OSFPv3的配置
实验目的 1. 掌握 OSPFv3 的配置方法 2. 掌握在帧中继环境下 OSPFv3 的配置方法 3. 掌握 OSPFv3 NSSA 的配置方法 4. 掌握外部路由汇总的配置 5. 掌握区 ...
- 湖南大学第十四届ACM程序设计新生杯(重现赛)
RANK 0 题数 0 期末复习没有参加,补几道喜欢的题. A: AFei Loves Magic 签到 思路 :不需考虑 碰撞 直接计算最终状态即可. #include<bits/stdc ...
- 那些天使用AWS填过的坑和注意事项
一直在找免费的GPU云端,在某乎上看到AWS提供免费的,就上去试了下,结果那个免费一年的只有CPU,并没有GPU,GPU还是需要付费的,相关背景就说这些,下面放几个相关教程,里面会说怎么使用,看了这几 ...
- css的基本定位机制
分为三种:普通流.浮动.绝对定位 普通流:默认,html文档的排列顺序块级从上到下(垂直距离由margin-top.margin-bottom决定):行内元素在一行中从左到右(由margin-left ...
- Java -- 内部类(一)
什么是内部类 将一个类的定义放在另一个类的定义内部,这就是内部类.在Java中内部类主要分为成员内部类.局部内部类.匿名内部类.静态内部类.举个栗子: public class A { public ...
- nodejs内存溢出解决方法
解决方案一:通过 package.json 加大内存,用nodemon启动的 node --v8-options | grep max-ol nodemon启动的文件:/bin/bash -c &q ...
- Set集合架构和常用实现类的源码分析以及实例应用
说明:Set的实现类都是基于Map来实现的(HashSet是通过HashMap实现的,TreeSet是通过TreeMap实现的). (01) Set 是继承于Collection的接口.它是一个不允许 ...
- flask内容之数据库的管理
#! /usr/bin/env python # *-* coding: utf-8 *-* from flask import Flask, flash, redirect from flask i ...