`

java的序列化机制原理分析2

    博客分类:
  • java
 
阅读更多

 

下面我们来分析下序列化后的字节流内容:

先写一段测试代码:

 

 

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class SeriableTest {

	public static void main(String[] args) throws FileNotFoundException, IOException {
		ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(new File("e:\\log\\aa.txt")));	
		out.writeInt(123);
		out.writeUTF("str12345");
		out.close();
		
//		ObjectInputStream in = new ObjectInputStream(new FileInputStream(new File("e:\\log\\aa.txt")));
//		int a = in.readInt();
//		String test = in.readUTF();
//		System.out.println(a);
//		System.out.println(test);
//		in.close();
	}
	
}

 

 

我们查看文件的内容:


 

其中0xACED是序列化的头信息字段,在new ObjectOutputStream的时候写入:

 

    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); //设置该参数为true,下面会用到
        if (extendedDebugInfo) {
	    debugInfoStack = new DebugTraceInfoStack();
	} else {
	    debugInfoStack = null;
        }   
    }
    protected void writeStreamHeader() throws IOException {
	bout.writeShort(STREAM_MAGIC);//写入一个序列化用到的魔法数
	bout.writeShort(STREAM_VERSION);//写入一个序列化的版本号
    }

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;

 

 

接下来的两个字节0x770E是在ObjectOutputStream close或者flush的时候会调用bout的close方法写入两个信息,

0x77是序列化内容长度小于0xff时设置的,0x0E是内容的长度。

 

 

 public class ObjectOutputStream
    extends OutputStream implements ObjectOutput, ObjectStreamConstants
{  
  public void close() throws IOException {
	flush();
	clear();
	bout.close();
    }

   

 然后bout.close()方法调用:

 

 

private static class BlockDataOutputStream 
	extends OutputStream implements DataOutput{

	public void flush() throws IOException {
	    drain();
	    out.flush();
	}

	public void close() throws IOException {
	    flush();
	    out.close();
	}
   void drain() throws IOException {
	    if (pos == 0) {
		return;
	    }
	    if (blkmode) {//这里为true
		writeBlockHeader(pos);//然后写入关于序列化内容的两个信息
	    }
	    out.write(buf, 0, pos);
	    pos = 0;
	}
	private void writeBlockHeader(int len) throws IOException {
	    if (len <= 0xFF) {
		hbuf[0] = TC_BLOCKDATA; //长度小于0xff时,写入BLOCKDATA的头 == 0x77
		hbuf[1] = (byte) len;//写入内容的长度
		out.write(hbuf, 0, 2);
	    } else {
		hbuf[0] = TC_BLOCKDATALONG;
		Bits.putInt(hbuf, 1, len);
		out.write(hbuf, 0, 5);
	    }
	}

 

然后0x0000007B就是123了,0x0008是后面字符串的长度,接着0x7374723132333435就是字符串str12345了。

 

我们再试试一个另外一个,我们写入一个bean

 

 

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;


public class SeriableTest {

	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
		ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(new File("e:\\log\\aa.txt")));	
		out.writeObject(new Bean());
		out.close();
		
		ObjectInputStream in = new ObjectInputStream(new FileInputStream(new File("e:\\log\\aa.txt")));
		Bean bean = (Bean) in.readObject();
		System.out.println(bean.a + " " + bean.b);
	}
	
}

class Bean implements Serializable{

	private static final long serialVersionUID = -1710202516612576460L;
	int a = 1;
	int b = 2;
}

 

输出为:

 


 

这里 前面的ACED0005跟上面的一样

73表示这个一个TC_OBJECT对象

 

    /**

     * new Object.

     */

    final static byte TC_OBJECT = (byte)0x73;

 

 

 private void writeOrdinaryObject(Object obj, 

				     ObjectStreamClass desc, 
				     boolean unshared) 				     ObjectStreamClass desc, 
				     boolean unshared) 
	throws IOException 
    {
        if (extendedDebugInfo) {
	    debugInfoStack.push(
		(depth == 1 ? "root " : "") + "object (class \"" + 
		obj.getClass().getName() + "\", " + obj.toString() + ")");
        }
        try {
	    desc.checkSerialize();

	    bout.writeByte(TC_OBJECT); //先写入该对象的类型
	    writeClassDesc(desc, false); //写入class对象的描述信息
	    handles.assign(unshared ? null : obj);
	    if (desc.isExternalizable() && !desc.isProxy()) {
		writeExternalData((Externalizable) obj);
	    } else {
		writeSerialData(obj, desc); //写入实例对象的数据
	    }
	} finally {
    	    if (extendedDebugInfo) {
		debugInfoStack.pop();
	    }  
        }
    }

 

我们看下writeClassDesc(desc, false); //写入class对象的描述信息的

 

下面写入的数据位:72 

 

 

    
private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared) 
	throws IOException 
    {
	bout.writeByte(TC_CLASSDESC); //final static byte TC_CLASSDESC = 	(byte)0x72;
	handles.assign(unshared ? null : desc);
	
	if (protocol == PROTOCOL_VERSION_1) {
	    // do not invoke class descriptor write hook with old protocol
	    desc.writeNonProxy(this);
	} else {
	    writeClassDescriptor(desc); //写入类的描述信息
	}
	
	Class cl = desc.forClass();
	bout.setBlockDataMode(true);
	annotateClass(cl); //子类可以重载该方法自己写入class对象
	bout.setBlockDataMode(false);
	bout.writeByte(TC_ENDBLOCKDATA);// final static byte TC_ENDBLOCKDATA =	(byte)0x78;
	
	writeClassDesc(desc.getSuperDesc(), false);//写入父类的信息 没有父类,最后写入0x70
    }

 

 

writeClassDescriptor最后调用的是下面这个方法:

写入的是 00 08 67 72 67 2E 42 65 61 6E  E8 44 23 DF 47 54 0B 34 02  00 02 49 00 01 61 49 00 01 62

 

 

 

 
 void writeNonProxy(ObjectOutputStream out) throws IOException {
	out.writeUTF(name);//写入类的完整名称,我这里是org.Bean  2byte+7byte  
对应16进制为00 08 67 72 67 2E 42 65 61 6E 

out.writeLong(getSerialVersionUID());//写入bean的序列化id 8byte 对应为E8 44 23 DF 47 54 0B 34

	byte flags = 0;
	if (externalizable) {
	    flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
	    int protocol = out.getProtocolVersion();
	    if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {
		flags |= ObjectStreamConstants.SC_BLOCK_DATA;
	    }
	} else if (serializable) {
	    flags |= ObjectStreamConstants.SC_SERIALIZABLE;
	}
	if (hasWriteObjectData) {
	    flags |= ObjectStreamConstants.SC_WRITE_METHOD;
	}
	if (isEnum) {
	    flags |= ObjectStreamConstants.SC_ENUM;
	}
	out.writeByte(flags); //写入类的flag信息,1byte 02
	
	out.writeShort(fields.length);//写入对象的序列化字段个数 这里=2,a,b, 2byte  00 02
	for (int i = 0; i < fields.length; i++) {//遍历flag,写入每个flag的信息   49 00 01 61 49 00 01 62
	    ObjectStreamField f = fields[i];
	    out.writeByte(f.getTypeCode()); //1byte 
	    out.writeUTF(f.getName()); //(2byte + 1byte) + (2byte+1byte)
	    if (!f.isPrimitive()) {//如果不是基本类型,写还需写入基本类型的信息,这里a和b都是基本类型
		out.writeTypeString(f.getTypeString());
	    }
	}
    }

 

 

 

最后就是写入每个字段的值了  ,最后写入00 00 00 01 00 00 00 02  

 

 

    private void defaultWriteFields(Object obj, ObjectStreamClass desc)
	throws IOException
    {
	// REMIND: perform conservative isInstance check here?
	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()); //写入00  00 00 01 00 00 00 02  
	    } finally {
		if (extendedDebugInfo) {
		    debugInfoStack.pop();
		}     
	    }	
	}
    }

 

 

 

  • 大小: 17.3 KB
  • 大小: 17.3 KB
  • 大小: 20.8 KB
分享到:
评论

相关推荐

    Java序列化的机制和原理

    Java序列化API提供一种处理对象序列化的标准机制。在这里你能学到如何序列化一个对象,什么时候需要序列化以及Java序列化的算法,我们用一个实例来示范序列化以后的字节是如何描述一个对象的信息的。……

    Java序列化的机制和原理[归类].pdf

    Java序列化的机制和原理[归类].pdf

    Java序列化机制与原理的深入分析

    有关Java对象的序列化和反序列化也算是Java基础的一部分,下面对Java序列化的机制和原理进行一些介绍

    【Java面试+Java学习指南】 一份涵盖大部分Java程序员所需要掌握的核心知识

    序列化和反序列化 继承、封装、多态的实现原理 容器 Java集合类总结 Java集合详解1:一文读懂ArrayList,Vector与Stack使用方法和实现原理 Java集合详解2:Queue和LinkedList Java集合详解3:Iterator,fail-fast机制...

    Java2实用教程.rar

    10 10序列化与对象克隆 10 11文件锁FileLock 10 12Process类中的流 10 13带进度条的输入流 习题 第11章Java网络的基本知识 11 1使用URL 11 2读取URL中的资源 11 3显示URL资源中的HTML文件 11 4处理超链接 11 5...

    java源码包---java 源码 大量 实例

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    JavaEE技术问题汇总.docx

    如何实现Java序列化与反序列化.序列化和反序列化使用的API 如何实现Java中的一个对象中某一个属性不被序列化,如何实现呢? Java中堆内存和栈内存区别 讲一讲反射,主要是概念,都在哪需要反射机制 JSP中有个...

    JAVA反序列化漏洞知识点整理

     反序列化漏洞的本质是反序列化机制打破了数据和对象的边界,导致攻击者注入的恶意序列化数据在反序列化过程中被还原成对象,控制了对象可能在目标系统上面执行攻击代码,而不可信的输入和未检测反序列化对象的...

    Java思维导图xmind文件+导出图片

    理解通信协议传输过程中的序列化和反序列化机制 基于框架的RPC通信技术 WebService/ApacheCXF RMI/Spring RMI Hession 传统RPC技术在大型分布式架构下面临的问题 分布式架构下的RPC解决方案 Zookeeper ...

    java源码包2

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    Java工程师面试复习指南

    序列化和反序列化 继承封装多态的实现原理 集合类 Java集合类总结 Java集合详解:一文读懂ArrayList,Vector与Stack使用方法和实现原理 Java集合详解:Queue和LinkedList Java集合详解:迭代器,快速失败机制与比较器...

    JAVA上百实例源码以及开源项目

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    JAVA上百实例源码以及开源项目源代码

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    java源码包4

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    【大厂面试题总结】JavaSE面试题总结详细教程

    【大厂面试题总结】JavaSE面试题总结详细教程: 目录: 递归算法之输出某个目录下所有文件和子目录列表 泛型中extends和super的区别 ...java序列化方式 java中实现多态的机制 string常量池和intern韩雅茹

    java源码包3

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    【大厂面试题总结】JavaSE面试题合集及其答案,基本包括javaSE所有知识点和详细解释

    【大厂面试题总结】JavaSE面试题合集及其答案,基本包括javaSE所有知识点和详细解释 。 JavaSE面试题总结详细教程: 目录: 递归算法之输出某个目录下所有...java序列化方式 java中实现多态的机制 string常量池和intern

    精通 Hibernate:Java 对象持久化技术详解(第2版).part2

     B.2 运用反射机制来持久化Java对象 附录C 用XDoclet工具生成映射文件  C.1 创建带有@hibernate标记的Java源文件  C.2 建立项目的目录结构  C.3 运行XDoclet工具 附录D 发布和运行netstore应用  D.1 运行...

    成百上千个Java 源码DEMO 4(1-4是独立压缩包)

    密钥 Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存...

Global site tag (gtag.js) - Google Analytics