- 浏览: 333545 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
zy_mensheng:
请问一下 怎么 js没有解析啊 还是钟表图..
调用BIEE提供的web service -
安铁辉:
师兄你很久没更新博客了
Information Dashboard Design读书笔记 -
mojunbin:
很清晰的文章
秒杀相关知识以及技术 -
yanchangjun8102:
楼主你好,我也最近在研究biee的webservice这块,按 ...
调用BIEE提供的web service -
sacredon:
不错,楼主我是看着你的这篇文章写代码调用的BIEE的Web ...
调用BIEE提供的web service
2009年8月3日 星期一 00时03分
对象序列化的深入探究
关于同学的疑问,我研究了一下jdk的实现,希望对你有所帮助,研究情况如下:
在我本机测试代码,查看序列化的文件guo.txt,在ultraEdit下,
用本地编码看会是一串乱码,但是用十六进制查看,就可以发现规律,文件内容如下:
AC ED 00 05
7A
00 00 02 FD 11 00 0A 0D 00 0A 07.....(后面内容省略)
每次执行,发现前面的AC ED 00 05总会存在。先解释这个吧。
我在此只是想以代码进一步证明:
对于创建一个对象输出流时,查看构造器的代码如下:
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; } }
writeStreamHeader();//这句很重要,即只要你针对文件打开一个对象输出流,它就会向其中写入4个字节的内容。可以查看到写入的内容是:
protected void writeStreamHeader() throws IOException { bout.writeShort(STREAM_MAGIC); bout.writeShort(STREAM_VERSION); }
写入的两个常量在接口ObjectStreamConstants定义如下:
public interface ObjectStreamConstants { /** * Magic number that is written to the stream header. */ final static short STREAM_MAGIC = (short)0xaced; /** * Version number that is written to the stream header. */ final static short STREAM_VERSION = 5;
从注释就可以知道,ACED只是写入序列化文件的一个固定标记,起标识作用。
BlockDataOutputStream类的writeShort方法在内部又使用了Bits类的方法,具体感兴趣的同学可以自己查看源代码。由于写进的是short,在java中short占两个字节,所以文件头就变成了AC ED 00 05。
然后解释7A。其实当你测试比较少的数据时,当写入的数据长度小于255个时,文件的前面部分会有另外一种结果:
AC ED 00 05
77
05 01 10 13 0A 06
bout是OjbectOutStream的私有静态内部类BlockDataOutputStream的实例。
该类持有
/** buffer for writing block data headers */ private final byte[] hbuf = new byte[MAX_HEADER_SIZE];//缓存数据块的头部信息 /** buffer for writing general/block data */ private final byte[] buf = new byte[MAX_BLOCK_SIZE];//该缓冲数组保存要写入的数据,但长度大于MAX_BLOCK_SIZE(1024)时,会刷新缓冲将内容写入文件。
在api中也清楚的注明,摘录入下:
基本数据(不包括 serializable 字段和 externalizable 数据)以块数据记录的形式写入 ObjectOutputStream 中。块数据记录由头部和数据组成。
块数据部分包括标记和跟在部分后面的字节数。连续的基本写入数据被合并在一个块数据记录中。块数据记录的分块因子为 1024 字节。每个块数据记录都将填满 1024 字节,或者在终止块数据模式时被写入。
源码如下:
private void writeBlockHeader(int len) throws IOException { if (len <= 0xFF) { hbuf[0] = TC_BLOCKDATA; hbuf[1] = (byte) len; out.write(hbuf, 0, 2); } else { hbuf[0] = TC_BLOCKDATALONG; Bits.putInt(hbuf, 1, len); out.write(hbuf, 0, 5); } }
以上代码中的TC_BLOCKDATA也定义在接口ObjectStreamConstants中。
/** * Block of optional data. Byte following tag indicates number * of bytes in this block data. */ final static byte TC_BLOCKDATA =(byte)0x77;//跟在77后面的字节记录一个数据块中实际写入的字节数,这里只有5个字节的数据内容,由于只有一个字节记录,所以最多只能有 255个字节的数据部分,数据长度该句代码hbuf[1] = (byte) len;产生。 /** * long Block data. The long following the tag indicates the * number of bytes in this block data. */ final static byte TC_BLOCKDATALONG= (byte)0x7A;//同样的后面跟的四个字节来记录写入的数据长度
每次数据块缓冲区满时(大于1024)就会刷新缓冲区,将块头和数据写入文件保存,所以保存的数据较多时,你会发现在文件的后面部分也会出现多次77 xx或7A xx,那是多次写入引起的
。每次刷新缓冲后,会重置块缓冲buf偏移pos为0,从头写入。数据长度有该句代码产生
Bits.putInt(hbuf, 1, len);
继续查看类Bits的该方法:
static void putInt(byte[] b, int off, int val) { b[off + 3] = (byte) (val >>> 0); b[off + 2] = (byte) (val >>> 8); b[off + 1] = (byte) (val >>> 16); b[off + 0] = (byte) (val >>> 24); }
该方法相信大家应该看得懂,其实就是把长度(int)的每个字节取出放到了hbuf中和7A一起做头部。
还有一点就是,如果你调用输出流的write方法后,却不去关闭输出流,当数据量小于1024个字节时,文件中只会包含序列化的文件头:
AC ED 00 05,而没有真正写入其他数据.数据量大于1024个字节的话,由于超过容量会刷新缓冲区,文件当然就包含数据咯。所以我们在写入较少数据的时候,注意要关闭输出流,这样就可以在关闭时,将缓冲区的数据写入到文件中去。
还有部分非关键代码就不贴出来了,想研究的同学直接查看就是了。以上内容只是本人针对源码结合测试得出的结论,不足之处,还请大家批评指正。
针对对象序列化,会将序列化的类和字段的基本信息保存在序列化文件中,也可以想见反序列化总会依赖一定信息吧,不可能直接针对一个普通文本文件就反序列化,这样就欠考虑了,呵呵。它也会首先根据文件头(即AC ED 00 05)来判定是不是一个序列化文件,如果不是就直接抛出异常。
针对对象序列化的问题,内容补充:
当你想序列化的类实现了Serialiazble接口时,序列化后再次反序列化时,不会调用该类的默认构造器。
而你的类如果实现了Externalizable接口时,在反序列化产生实例时,会调用默认构造器,初始化成员变量。
发表评论
-
【转】hashCode()的作用
2011-05-14 13:41 817http://blog.csdn.net/badboy_blu ... -
AOP
2011-02-24 19:47 1495AOP有三种织入切面的方法:其一是编译期织入,这要求使用特殊的 ... -
code hot swap
2010-08-04 22:54 1274关于code hot swap的说明(即更新部署目录的clas ... -
java基础复习(17)--字符串,集合,I/O
2009-07-30 22:48 1452读取properties 文件时出现乱码 如果 ... -
java基础复习(16)-字符串数组交集,并集和差集
2009-07-29 15:47 6772求两个字符串数组的交集,并集和差集的程序代码(有其他或更好的方 ... -
java基础复习(15)-多态问题强化
2009-07-27 13:34 1132多态题目: 题一: ... -
java基础复习(14)--java并发
2009-07-26 20:05 1780并发: 冒泡排序: SimpleThreadEx ... -
java基础复习(13)--java泛型
2009-07-26 19:53 1493泛型: public static <T> ... -
java基础复习(12)--类型转换,异常概念
2009-07-26 19:43 1527一个对象只能有一种确 ... -
java基础复习(11)--final关键字和抽象类
2009-07-26 19:40 1141final 关键字 final--用于类,方法,变量前 f ... -
java基础复习(10)--链表实现,单例模式
2009-07-26 19:37 1185类的构造器也可以实现 ... -
java基础复习(9)--继承与多态的理解
2009-07-26 19:34 972继承与多态 ... -
java基础复习(8)--数组
2009-07-26 19:12 1489java中有包名的类无法引 ... -
java基础复习(7)--值传递和交换变量值(异或)
2009-07-26 19:10 2372对于java中的方法参数传 ... -
java基础复习(6)
2009-07-26 16:18 996充:java中有包名的类无法引用默认包中的类。但是1.2或1. ... -
java基础复习(5)--考试习题
2009-07-26 16:00 1213今天做练习题,一共有3 ... -
java基础复习(4)
2009-07-26 15:55 968极限编程 测试先行 ... -
java基础复习(3)
2009-07-26 15:51 965int型整数,a>>b,系 ... -
java基础复习(2)
2009-07-26 15:45 1113java中的TRUE和false不可以 ... -
java基础复习(1)
2009-07-26 15:43 1040java虚拟机对java字节码进行优化(针对特定的平台) j ...
相关推荐
内容概要:以上列出的Java面试题涵盖了Java语言的基础知识、面向对象编程、集合、IO流、多线程、反射、类加载器、JVM、序列化、泛型、异常处理、注解等多个方面。 适用人群:以上Java面试题适用于准备Java开发...
序列化和反序列化 继承封装多态的实现原理 集合类 Java集合类总结 Java集合详解:一文读懂ArrayList,Vector与Stack使用方法和实现原理 Java集合详解:Queue和LinkedList Java集合详解:迭代器,快速失败机制与比较器...
JAVA对象的序列化和反序列化 161 为什么需要序列化和反序列化 161 对象的序列化主要有两种用途 161 序列化涉及的类和接口 162 序列化/反序列化的步骤和实例 162 综合的序列化和反序列化练习 163 JAVA.IO包相关流对象...
12.2.8 通过序列化进行深层复制 12.2.9 使克隆具有更大的深度 12.2.10 为什么有这个奇怪的设计 12.3 克隆的控制 12.3.1 副本构建器 12.4 只读类 12.4.1 创建只读类 12.4.2 “一成不变”的弊端 12.4.3 不变字串 ...
12.2.8 通过序列化进行深层复制 12.2.9 使克隆具有更大的深度 12.2.10 为什么有这个奇怪的设计 12.3 克隆的控制 12.3.1 副本构建器 12.4 只读类 12.4.1 创建只读类 12.4.2 “一成不变”的弊端 12.4.3 不变字串 ...
12.2.8 通过序列化进行深层复制 12.2.9 使克隆具有更大的深度 12.2.10 为什么有这个奇怪的设计 12.3 克隆的控制 12.3.1 副本构建器 12.4 只读类 12.4.1 创建只读类 12.4.2 “一成不变”的弊端 12.4.3 不变字串 ...
12.2.8 通过序列化进行深层复制 12.2.9 使克隆具有更大的深度 12.2.10 为什么有这个奇怪的设计 12.3 克隆的控制 12.3.1 副本构建器 12.4 只读类 12.4.1 创建只读类 12.4.2 “一成不变”的弊端 12.4.3 不变字串 ...
12.2.8 通过序列化进行深层复制 12.2.9 使克隆具有更大的深度 12.2.10 为什么有这个奇怪的设计 12.3 克隆的控制 12.3.1 副本构建器 12.4 只读类 12.4.1 创建只读类 12.4.2 “一成不变”的弊端 12.4.3 不变字串 ...
12.2.8 通过序列化进行深层复制 12.2.9 使克隆具有更大的深度 12.2.10 为什么有这个奇怪的设计 12.3 克隆的控制 12.3.1 副本构建器 12.4 只读类 12.4.1 创建只读类 12.4.2 “一成不变”的弊端 12.4.3 不变字串 ...
12.2.8 通过序列化进行深层复制 12.2.9 使克隆具有更大的深度 12.2.10 为什么有这个奇怪的设计 12.3 克隆的控制 12.3.1 副本构建器 12.4 只读类 12.4.1 创建只读类 12.4.2 “一成不变”的弊端 12.4.3 不变字串 ...
12.2.8 通过序列化进行深层复制 12.2.9 使克隆具有更大的深度 12.2.10 为什么有这个奇怪的设计 12.3 克隆的控制 12.3.1 副本构建器 12.4 只读类 12.4.1 创建只读类 12.4.2 “一成不变”的弊端 12.4.3 不变字串 ...
12.2.8 通过序列化进行深层复制 12.2.9 使克隆具有更大的深度 12.2.10 为什么有这个奇怪的设计 12.3 克隆的控制 12.3.1 副本构建器 12.4 只读类 12.4.1 创建只读类 12.4.2 “一成不变”的弊端 12.4.3 不变...
12.2.8 通过序列化进行深层复制 12.2.9 使克隆具有更大的深度 12.2.10 为什么有这个奇怪的设计 12.3 克隆的控制 12.3.1 副本构建器 12.4 只读类 12.4.1 创建只读类 12.4.2 “一成不变”的弊端 12.4.3 不变字串 ...
12.2.8 通过序列化进行深层复制 12.2.9 使克隆具有更大的深度 12.2.10 为什么有这个奇怪的设计 12.3 克隆的控制 12.3.1 副本构建器 12.4 只读类 12.4.1 创建只读类 12.4.2 “一成不变”的弊端 12.4.3 不变字串 ...
引论1.1 本书讨论的内容1.2 数学知识复习1.2.1 指数1.2.2 对数1.2.3 级数1.2.4 模运算1.2.5 证明的方法1.3 递归简论1.4 实现泛型特性构件pre-Java51.4.1 使用Object表示泛型1.4.2 基本类型的包装1.4.3 使用接口...
引论1.1 本书讨论的内容1.2 数学知识复习1.2.1 指数1.2.2 对数1.2.3 级数1.2.4 模运算1.2.5 证明的方法1.3 递归简论1.4 实现泛型特性构件pre-Java51.4.1 使用Object表示泛型1.4.2 基本类型的包装1.4.3 使用接口...
1.5.3 带有限制的通配符 1.5.4 泛型static方法 1.5.5 类型限界 1.5.6 类型擦除 1.5.7 对于泛型的限制 1.6 函数对象 小结 练习 参考文献第2章 算法分析 2.1 数学基础 2.2 模型 2.3 要分析的...
1.5.3 带有限制的通配符 1.5.4 泛型static方法 1.5.5 类型限界 1.5.6 类型擦除 1.5.7 对于泛型的限制 1.6 函数对象 小结 练习 参考文献第2章 算法分析 2.1 数学基础 2.2 模型 2.3 要分析的...