您的位置:首页 > 编程语言 > Java开发

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

2012-06-04 21:41 615 查看
下面我们来分析下序列化后的字节流内容:

先写一段测试代码:

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
<span style="white-space: normal; background-color: #ffffff;">对应16进制为00 08 67 72 67 2E 42 65 61 6E </span>

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();
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: