`
jianchen
  • 浏览: 342953 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

java基础复习(18)-对象序列化

阅读更多

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接口时,在反序列化产生实例时,会调用默认构造器,初始化成员变量。

分享到:
评论

相关推荐

    Java基础与实践-源代码.rar

    I/O流是处理数据输入和输出的核心工具,包括文件操作、字符流和字节流、对象序列化和缓冲流的使用。 7. **ch07** - 文件与目录操作 这一章可能包括对文件和目录的创建、读取、删除等操作,以及使用Java的File类和...

    Java基础知识复习资料.rar

    - 还包括缓冲流(BufferedInputStream、BufferedReader)提高性能,对象序列化(ObjectInputStream、ObjectOutputStream)用于持久化对象。 6. **多线程** - 多线程是让程序同时执行多个任务的能力,Java通过...

    Java复习资料

    - 对象序列化与反序列化 8. **第八章:多线程** - 线程的创建方式:继承Thread类与实现Runnable接口 - 线程同步:synchronized关键字,wait(),notify(),notifyAll() - 线程池与ExecutorService 9. **第九章...

    Java期末复习-常用类库

    以上就是Java常用类库的一些核心知识点,涵盖了字符串处理、系统交互、国际化、日期时间、数学计算、大数处理、对象复制、数组操作、比较和正则表达式以及定时任务等多个方面,这些都是Java编程中不可或缺的基础工具...

    Java SE 复习资料

    虽然不常用,但在某些场合(如插件系统、序列化、动态代理)是必不可少的。 9. **泛型**:泛型是Java 5引入的新特性,用于增强类型安全,减少类型转换,并允许编译器进行更严格的检查。理解和运用泛型,可以编写出...

    Java程序设计基础分类复习(答案)

    Java提供了丰富的I/O流类库,支持文件读写、网络通信、对象序列化等。 8. **多线程**: Java内置了对多线程的支持,通过实现Runnable接口或继承Thread类创建线程,以及同步机制(synchronized关键字、wait()、...

    Java编程基础(2011-2012学年第一学期)复习提纲.doc

    ### Java编程基础知识点详解 #### 第一章 Java 语言概述 **1. Java 平台的工作机制** Java 是一种能够跨平台运行的语言。其工作机制基于“编写一次,到处运行”的理念。具体而言,Java 源代码文件(.java 文件)...

    java面试复习资料

    5. **反序列化**:在网络传输过程中,通过反序列化的方式创建对象实例。 #### 六、序列化与反序列化 - **概念**:序列化是将对象的状态信息转换为可以存储或传输的形式的过程;反序列化则是在另一端将获取到的信息...

    JAVA 复习资料

    ### JAVA复习资料知识点详解 #### 一、Java基础概述 在Java的学习过程中,为了更好地理解和掌握这门语言,我们可以通过一些具体的代码实例来深入探讨其核心概念和技术要点。本篇内容将围绕`Person.java`与`Static...

    java考试复习

    - 输入/输出流的概念,如字节流和字符流,以及缓冲流、对象序列化和反序列化。 8. **多线程**: - 创建和管理线程,了解Thread类和Runnable接口。 - 线程同步机制,如synchronized关键字、wait()、notify()和...

    java面试复习资料.pdf

    7. **Java序列化**:序列化是将对象的状态转换为字节流的过程,便于存储和传输。通过实现`Serializable`接口可实现序列化。例如: ```java class MyClass implements Serializable { // class fields } ``` 然后...

    JAVA试题 JAVA复习题 JAVA复习笔记

    - **对象序列化与反序列化**:了解Serializable接口,实现对象的持久化。 - **NIO**:非阻塞I/O模型,适用于高并发场景。 6. **多线程** - **线程的创建与状态**:通过Thread类和Runnable接口创建线程,理解线程...

    java-core-test

    - 数据序列化与反序列化:理解对象序列化的过程,以及Serializable接口的使用。 以上内容涵盖了Java核心知识的主要方面,对于初学者和有经验的开发者都是很好的复习资源。通过深入学习和实践,可以有效提升Java...

    java基础复习笔记(第一阶段)

    6. **输入/输出流**:Java.IO包提供了丰富的类用于处理数据的输入和输出,包括文件操作、网络流、对象序列化等。掌握FileInputStream、FileOutputStream、BufferedReader、PrintWriter等基本类的使用。 7. **泛型**...

    2018年秋季学期-Java应用与开发-复习提纲1

    10. **IO流**:Java的输入输出系统基于流,包括字节流和字符流,以及对象序列化。理解和使用不同类型的流进行文件读写和网络通信是必备技能。 以上只是复习提纲中的一部分内容,全面掌握Java应用与开发还需要深入...

    【良心出品】Java语言程序设计(一)复习资料--Java简答题.doc

    1. **面向对象**:Java是一种完全面向对象的语言,所有的数据都是以对象的形式存在的,这样有助于代码的复用和模块化。 2. **与平台无关性**:Java语言最大的特点之一就是“一次编写,到处运行”(Write Once, Run ...

    java程序语言设计第十版复习题答案

    复习题可能涵盖文件操作、缓冲流、对象序列化、网络流等知识。 7. **多线程**:Java提供内置支持进行并发编程,复习题可能涉及Thread类的使用、同步机制(synchronized关键字、wait()、notify()、notifyAll()方法)...

    Java基础知识点复习资料

    - **对象序列化**:将对象转换为字节流,便于存储或网络传输。 7. **线程** - **并发编程**:Java提供了Thread类和Runnable接口来实现多线程,理解线程的创建、同步和通信非常重要。 - **synchronized关键字**:...

    JAVA课程总复习

    6. **IO流**:讲解输入/输出流的基本概念,包括字节流和字符流,以及缓冲区、对象序列化等相关知识。 7. **多线程**:介绍线程的概念,创建线程的方式(继承Thread类和实现Runnable接口),线程同步和通信机制(如...

    Java基础复习笔记04数据结构-线性表

    ### Java基础复习笔记04数据结构-线性表:深入解析与实现 #### 知识点一:线性表的概念与特性 线性表是数据结构中最基本的一种线性结构,其中的数据元素之间存在一对一的关系,即每个元素都有一个前驱和后继,除了...

Global site tag (gtag.js) - Google Analytics