本次讲解中我们在上次的基础上,深入的了解一下序列化的流程以及其中的原理。关于序列化的一些知识与使用,请参见我的另一篇博客:java基础---->Serializable的使用。好了,我们进行以下分析的讲解。

目录导航

  1. Java序列化的原理分析
  2. 自定义Serializable的使用
  3. 友情链接

Java序列化的原理分析

java基础---->Serializable的使用的代码的演示,我们可以知道:

  • 调用writeObject方法序列化一个对象,是将其写入磁盘,以后在程序再次调用readObject时,根据wirteObject方法磁盘的文件重新恢复那个对象
  • Externalizable 接口扩展了Serializable,并增添了两个方法:writeExternal()和readExternal()。在序列化和重新装配的过程中,会自动调用这两个方法

Java的序列化

一、 objectOutputStream.writeObject(user)方法执行的详细如下:

  • ObjectOutputStream的构造函数设置enableOverride = false;
public ObjectOutputStream(OutputStream out) throws IOException {
verifySubclass();
bout = new BlockDataOutputStream(out);
handles = new HandleTable(10, (float) 3.00);
subs = new ReplaceTable(10, (float) 3.00);
enableOverride = false;
writeStreamHeader();
bout.setBlockDataMode(true);
if (extendedDebugInfo) {
debugInfoStack = new DebugTraceInfoStack();
} else {
debugInfoStack = null;
}
}
  • 所以writeObject方法执行的是writeObject0(obj, false);
 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;
}
}

二、 在writeObject0方法中,以下贴出重要的代码

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());
}
}
  • 从上可以看出,如果对象没有实现Serializable接口,在序列化的时候会抛出NotSerializableException异常
  • 上述Person是一个类对象,所以会执行writeOrdinaryObject(obj, desc, unshared)方法;

三、 在writeOrdinaryObject(obj, desc, unshared)方法中,重要代码如下:

if (desc.isExternalizable() && !desc.isProxy()) {
writeExternalData((Externalizable) obj);
} else {
writeSerialData(obj, desc);
}
  • 如果对象实现了Externalizable接口,那么执行writeExternalData((Externalizable) obj)方法
  • 如果对象实现的是Serializable接口,那么执行的是writeSerialData(obj, desc);

四, 我们首先看一下writeSerialData方法,主要执行方法:defaultWriteFields(obj, slotDesc);

private void defaultWriteFields(Object obj, ObjectStreamClass desc) throws IOException {
Class<?> cl = desc.forClass();
if (cl != null && obj != null && !cl.isInstance(obj)) {
throw new ClassCastException();
} desc.checkDefaultSerialize(); int primDataSize = desc.getPrimDataSize();
if (primVals == null || primVals.length < primDataSize) {
primVals = new byte[primDataSize];
}
desc.getPrimFieldValues(obj, primVals);
bout.write(primVals, 0, primDataSize, false); ObjectStreamField[] fields = desc.getFields(false);
Object[] objVals = new Object[desc.getNumObjFields()];
int numPrimFields = fields.length - objVals.length;
desc.getObjFieldValues(obj, objVals);
for (int i = 0; i < objVals.length; i++) {
if (extendedDebugInfo) {
debugInfoStack.push(
"field (class \"" + desc.getName() + "\", name: \"" +
fields[numPrimFields + i].getName() + "\", type: \"" +
fields[numPrimFields + i].getType() + "\")");
}
try {
writeObject0(objVals[i],
fields[numPrimFields + i].isUnshared());
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
}
  • 在此方法中,是系统默认的写入对象的非transient部分。我们在后续的自定义序列化中会做讲解。

五、 我们再来看一下writeExternalData的方法,重要代码如下:

try {
curContext = null;
if (protocol == PROTOCOL_VERSION_1) {
obj.writeExternal(this);
} else {
bout.setBlockDataMode(true);
obj.writeExternal(this);
bout.setBlockDataMode(false);
bout.writeByte(TC_ENDBLOCKDATA);
}
}
  • 在此方法中obj.writeExternal(this)执行了序列化对象的writeExternal方法,在上述例子中也就是User类的writeExternal方法。

Java的反序列化

一、 与上述的write过程类似,objectInputStream.readObject()方法执行了readObject0(false)方法:主要代码:

switch (tc) {
case TC_NULL:
return readNull(); case TC_REFERENCE:
return readHandle(unshared); case TC_CLASS:
return readClass(unshared); case TC_CLASSDESC:
case TC_PROXYCLASSDESC:
return readClassDesc(unshared); case TC_STRING:
case TC_LONGSTRING:
return checkResolve(readString(unshared)); case TC_ARRAY:
return checkResolve(readArray(unshared)); case TC_ENUM:
return checkResolve(readEnum(unshared)); case TC_OBJECT:
return checkResolve(readOrdinaryObject(unshared)); case TC_EXCEPTION:
IOException ex = readFatalException();
throw new WriteAbortedException("writing aborted", ex); case TC_BLOCKDATA:
case TC_BLOCKDATALONG:
if (oldMode) {
bin.setBlockDataMode(true);
bin.peek(); // force header read
throw new OptionalDataException(
bin.currentBlockRemaining());
} else {
throw new StreamCorruptedException(
"unexpected block data");
} case TC_ENDBLOCKDATA:
if (oldMode) {
throw new OptionalDataException(true);
} else {
throw new StreamCorruptedException(
"unexpected end of block data");
} default:
throw new StreamCorruptedException(
String.format("invalid type code: %02X", tc));
}
  • 根据不同的对象类型做相应的处理,这里我们关注的是TC_OBJECT,执行的方法是:checkResolve(readOrdinaryObject(unshared));

七、 跟进去,我们可以看到在readOrdinaryObject(unshared)中,执行了以下代码:

private Object readOrdinaryObject(boolean unshared) throws IOException {
if (bin.readByte() != TC_OBJECT) {
throw new InternalError();
} ObjectStreamClass desc = readClassDesc(false);
desc.checkDeserialize(); Class<?> cl = desc.forClass();
if (cl == String.class || cl == Class.class
|| cl == ObjectStreamClass.class) {
throw new InvalidClassException("invalid class descriptor");
} Object obj;
try {
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {
throw (IOException) new InvalidClassException(
desc.forClass().getName(),
"unable to create instance").initCause(ex);
} passHandle = handles.assign(unshared ? unsharedMarker : obj);
ClassNotFoundException resolveEx = desc.getResolveException();
if (resolveEx != null) {
handles.markException(passHandle, resolveEx);
} if (desc.isExternalizable()) {
readExternalData((Externalizable) obj, desc);
} else {
readSerialData(obj, desc);
} handles.finish(passHandle); if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
{
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
handles.setObject(passHandle, obj = rep);
}
} return obj;
}
  • 加载序列化对象的Class,如果可以实例化,那么创建它的实例:desc.newInstance(),User类的无参构造方法执行

自定义Serializable的使用

一、 我们写了Human类,为了方便Main方法也在这里类里面。

package com.huhx.model;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable; public class Human implements Serializable{ private static final long serialVersionUID = 1L; private String username;
private transient String password; public Human(String username, String password) {
this.username = "not transient " + username;
this.password = "transient " + password;
} @Override
public String toString() {
return username + "\n" + password;
} private void readObject(ObjectInputStream inputStream) throws ClassNotFoundException, IOException {
inputStream.defaultReadObject();
password = (String) inputStream.readObject();
} private void writeObject(ObjectOutputStream outputStream) throws IOException {
outputStream.defaultWriteObject();
outputStream.writeObject(password);
} public static void main(String[] args) throws Exception {
Human human = new Human("huhx", "liuli");
System.out.println("before: \n" + human); ByteArrayOutputStream buff = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(buff);
objectOutputStream.writeObject(human); ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(buff.toByteArray()));
Human human2 = (Human) objectInputStream.readObject();
System.out.println("after: \n" + human2);
}
}
  • 定义了两种方法:readObject和writeObject方法,注意命名是有限制的,这个等下解释。好了,我们先看结果

  aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQYAAABoCAIAAACc3sn1AAAFmUlEQVR4nO3d24GqMBSFYeqiIOqhGpqhGOZBLnuF7Jg4gBH+7+kcxJBRlgHNhmYCYDTf7gBQFyIBiDkSQ9c0TduPxc8fumbVDQd3DrjeNkr0bXkkxr5t+2M7BHyXRmLo2/kT38RjXBc2jQmAHR+Ch8wTtpHjtbDtx6W95SGnfbMRhh9cx0bCJGHs22VHtKPH2Ley18ZGib41eRo6Wf+1h7+WDN1rA6n2iQQuJ5Gwu97Yt92gH+H7U4Z9JOanbYbODDjyn2X9RPvA5dxziWXfHrrE2cL/I5FuH7icHjite6c5cBo6/6uo+IGTXV33+Egkku1z4ITLyZewXbcexcheqgc36zmGc7yjx0LL4vBs3O7m0fbNs4gErsNPdYAgEoAgEoAgEoAgEoAgEoAgEoAgEoAgEoB4SiSS00aO9/oFP/9X99L1cZ5/RSKc4nf0+rUp6v9uAuTB6+MkcyTmT6mui000ipUE7SZ1v3k7/fXd0iIzK2r9gM/tZ9uu8xHXLafmpevKkfZL/97XLt6vz5pXt6PBUjwyJtaf7IsyvyBXjnZPtI0SY9/ad2594RMlQYeNErHSomkYbEWeziqM9NP+e+jCAr1xV0frlS557Zf+veNravEY6efWipka7K0/d27UnuE0EonIW5WsfzgwEpGPvviMWneXSl8aIYyEX7rkt/+PAyfd9YuWm94yPlyh2kjI+bDtRWrX0WfbBbtRwi1dqjESS9gZIi7wLhLJkqDtoaHLeb/c9aMFqFLP9H6UkH4OXdCd/YGT9x1UMhIFf29i198+boJzCfeAajv7YKQ4m5xe62lcY9+5+BHJED/NdUXWd0uLzGbn2qZuSPXTqUMK299fNMQuT70OJX9v3uvZ9v18/Sxv/Tf9wQme8rsEkIlIAIJIAIJIAIJIAIJIAIJIAIJIAIJIAKKWSFxc4jMlJo0X9ueodlAJicSvF7F80P/93KfPHNUOvm6JxPNKfNbnBduN9idR+lPUDuqXN0rcscTH2663PD0pPb8dVC43Evcr8Ylv119OJB7i40j8fImPs113OZF4iCASDyrx8bbrLfdKf0rbQeX0S9jHlPj42/X7Eyv9+aQd1K2W3yWAShAJQBAJQBAJQBAJQBAJQBAJQBAJQBAJQORGIjVlD7iRvEjYGU3Gr5ccAXsmErGSoGk/WeeVjUTJTqz0Z0qUIm0bIWD4PhuJeEnQNJWNEl7pzzQ5pUjbA0QC32cikbhofn4k/NKfafLLJ4BqrJFwS4Lm/+eOEm7pz/IgkUDVzOUInJKg8FEjWrKTukyLGwkOnFCL4I6m80nxWhI0hXU8sbPi3d4cqxZyS5FMQ0QC38dPdYAgEoAgEoAgEoAgEoAgEoAgEoAgEoAgEoB4SiQuvhuQvR/FGevjPP+KRGkJ0a+XHBX1P5w6efT6OMkcidy7B31815/n3eVo7NuuX5+13RwjmPVl/7T9+pN9UeYXhMnE5wqm/UXu7tO3emluKZQ7aJS4412O7CX3g35GbwbgrT93bozNUMYJJBKRt2o3nNv53QdG4n53OUrs+kXLTW8ZH65QbSR+/i5HR0ZiCTtDxAXeRSK4e5DuSaV3/XnUXY4Su370bkbJA6rt7IOR4mxyeq2ncY195+JHJAV3/fHWv+ddjvJez+1uRt76b/qDEzzldwkgE5EABJEABJEABJEABJEABJEABJEABJEARC2RuLjEZ0pMGi/sz1HtoBISiV8vYvmg//u5T585qh18nblyuDMT6K4lPuvzgu1G+5Mo/SlqB/XLGyXuWOLjbddbnp6Unt8OKpcbifuV+MS36y8nEg/xcSR+vsTH2a67nEg8RBCJB5X4eNv1lnulP6XtoHL6JexjSnz87fr9iZX+fNIO6lbL7xJAJYgEIIgEIIgEIIgEIIgEIIgEIIgEIP4AC8V/FpAthR0AAAAASUVORK5CYII=" alt="" />

  • 一个String 保持原始状态,其他设为transient(临时),以便证明非临时字段会被defaultWriteObject()方法自动保存,而transient 字段必须在程序中明确保存和恢复。字段是在构建器内部初始化的,而不是在定义的时候,这证明了它们不会在重新装配的时候被某些自动化机制初始化。若准备通过默认机制写入对象的非transient 部分,那么必须调用defaultWriteObject(),令其作为writeObject()中的第一个操作;并调用defaultReadObject(),令其作为readObject()的第一个操作

二、 Human类中的readObject和writeObject方法的执行,不是我们调用的,而是ObjectInputStream与ObjectOutputStream 对象的readObject,writeObject方法去调用的。重要源代码如下

  • ObjectStreamClass(final Class<?> cl) 构造方法:
if (externalizable) {
cons = getExternalizableConstructor(cl);
} else {
cons = getSerializableConstructor(cl);
writeObjectMethod = getPrivateMethod(cl, "writeObject",
new Class<?>[] { ObjectOutputStream.class },
Void.TYPE);
readObjectMethod = getPrivateMethod(cl, "readObject",
new Class<?>[] { ObjectInputStream.class },
Void.TYPE);
readObjectNoDataMethod = getPrivateMethod(
cl, "readObjectNoData", null, Void.TYPE);
hasWriteObjectData = (writeObjectMethod != null);
}
slotDesc.invokeWriteObject(obj, this);

友情链接

java高级---->Serializable的过程分析的更多相关文章

  1. java基础---->Serializable的使用

    本次讲解中我们建立一个Java的项目去体会一下序列化Serializable的使用,序列化的原理以及序列化的自定义请参见我的另外一篇博客(java高级---->Serializable序列化的源 ...

  2. Elasticsearch Java高级客户端

    1.  概述 Java REST Client 有两种风格: Java Low Level REST Client :用于Elasticsearch的官方低级客户端.它允许通过http与Elastic ...

  3. Java 高级基础——反射

    Java 高级基础--反射 反射的意义:Java 强类型语言,但是我们在运行时有了解.修改信息的需求,包括类信息.成员信息以及数组信息. 基本类型与引用类型 基本类型,(固定的 8 种) 整数:byt ...

  4. 尚硅谷Java高级笔记

    尚硅谷Java高级笔记 idea的使用: 一些小区别: 其他细节参考idea配置pdf 多线程: 基本概念: 多线程的优点: 何时需要多线程: 线程的创建和使用: 创建多线程的第一种方式: /** * ...

  5. 【Java学习系列】第3课--Java 高级教程

    本文地址 可以拜读: 从零开始学 Java 分享提纲: 1. Java数据结构 2. Java 集合框架 3. Java泛型 4. Java序列化 5. Java网络编程 6. Java发送Email ...

  6. [Java面经]干货整理, Java面试题(覆盖Java基础,Java高级,JavaEE,数据库,设计模式等)

    如若转载请注明出处: http://www.cnblogs.com/wang-meng/p/5898837.html   谢谢.上一篇发了一个找工作的面经, 找工作不宜, 希望这一篇的内容能够帮助到大 ...

  7. java.io.Serializable 序列化接口

    什么是序列化.反序列化? Serialization(序列化)是一种将对象以一连串的字节描述的过程: 反序列化deserialization是一种将这些字节重建成一个对象的过程. 序列化通俗一点说就是 ...

  8. Java 序列化Serializable详解

    Java 序列化Serializable详解(附详细例子) Java 序列化Serializable详解(附详细例子) 1.什么是序列化和反序列化Serialization(序列化)是一种将对象以一连 ...

  9. Java里Serializable的那些事

    本文为原创文章,欢迎转载,但请注明出处http://www.cnblogs.com/yexiubiao/p/5014015.html,未在文章页面明显位置给出原文连接的,将保留追究法律责任的权利. 通 ...

随机推荐

  1. BZOJ 1143: [CTSC2008]祭祀river(二分图最大点独立集)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1143 题意: 思路: 二分图最大点独立集,首先用floyd判断一下可达情况. #include< ...

  2. cmake用法及常用命令总结

    CMakeLists.txt 的语法比较简单,由命令.注释和空格组成,其中命令是不区分大小写的.指令是大小写无关的,参数和变量是大小写相关的.但推荐全部使用大写指令.符号 # 后面的内容被认为是注释. ...

  3. _spellmod_leech_spell

    comment  备注 spell 技能ID,玩家释放该技能时附带吸血效果 meetAura  产生吸血效果需要满足的光环ID,比如做一个空的光环,为寒冰箭吸血光环,则有些光环时候,寒冰箭会附带吸血效 ...

  4. 《Linux命令行与shell脚本编程大全》读书笔记

    第一章:初识Linux 1.linux可划分为四个部分:内核.GNU工具.图形化桌面环境.应用程序 2.内核主要负责:系统内存管理.软件程序管理.硬件设备管理.文件系统管理 3.内核的系统内存管理,有 ...

  5. [原]windows sdk版本不对

    系统硬盘换了,重新安装一堆软件,SVN. 之前的SVN地址直接能找到 在编译vs项目的时候出现问题: windows sdk 10.0.14393.0 版本找不到 发现自己按照vs时候更新不了最新sd ...

  6. QT json字符串生成和解析

    1         QT json字符串生成和解析 1.1  QT Json解析流程 (1)  字符串转化为QJsonDocument QJsonParseError json_error; QJso ...

  7. python爬虫学习(三):使用re库爬取"淘宝商品",并把结果写进txt文件

    第二个例子是使用requests库+re库爬取淘宝搜索商品页面的商品信息 (1)分析网页源码 打开淘宝,输入关键字“python”,然后搜索,显示如下搜索结果 从url连接中可以得到搜索商品的关键字是 ...

  8. 连接PL/SQL

    1.登录PL/SQL Developer 这里省略Oracle数据库和PL/SQL Developer的安装步骤,注意在安装PL/SQL Developer软件时,不要安装在Program Files ...

  9. 荧光原位杂交技术 RNA-FiSH (fluorescence in situ hybridization)

    通俗理解:带有荧光标记的DNA探针可以用于检测活体内特定基因的表达情况,活体成像. 荧光原位杂交方法是一种物理图谱绘制方法,使用荧光素标记探针,以检测探针和分裂中期的染色体或分裂间期的染色质的杂交.荧 ...

  10. English trip V1 - B 13. Are you a model? 你是模特吗? Teacher:Patrick Key: 单词回顾、词性后缀

    因为这节课本身内容过于So easy~ Patrick给我补充了很多课外内容 课上内容(Lesson) I doesn't work  2层意思 1) 东西坏了,这个东西不能正常工作了.比如钟不走时间 ...