链接: https://www.cnblogs.com/zhwj184/archive/2012/06/02/3027487.html
https://www.cnblogs.com/zhwj184/archive/2012/06/04/3119633.html
我们查看下ObjectOutputStream的writeObject()方法
//final方法,不允许子类覆盖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;}}
如果要自定义这个序列化过程,则可以写一个子类,集成ObjectOutputStream,然后覆盖其两个方法
protected ObjectOutputStream() throws IOException, SecurityException {SecurityManager sm = System.getSecurityManager();if (sm != null) {sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);}bout = null;handles = null;subs = null;enableOverride = true;debugInfoStack = null;}protected void writeObjectOverride(Object obj) throws IOException {}
我们再看下具体的writeObject0()方法:
private void writeObject0(Object obj, boolean unshared) throws IOException{boolean oldMode = bout.setBlockDataMode(false);depth++;try {// 先对obj实例的类信息进行序列化,int h;if ((obj = subs.lookup(obj)) == null) {writeNull();return;} else if (!unshared && (h = handles.lookup(obj)) != -1) {//可以自定义class类信息的序列化handlerwriteHandle(h);return;} else if (obj instanceof Class) { //类信息序列化writeClass((Class) obj, unshared);return;} else if (obj instanceof ObjectStreamClass) { //类信息序列化,此时还包括serialVersionUIDwriteClassDesc((ObjectStreamClass) obj, unshared);return;}// check for replacement object//这里还可以对序列化的类进行替换序列化Object orig = obj;Class cl = obj.getClass();ObjectStreamClass desc;for (;;) {// REMIND: skip this check for strings/arrays?Class repCl;desc = ObjectStreamClass.lookup(cl, true);if (!desc.hasWriteReplaceMethod() ||(obj = desc.invokeWriteReplace(obj)) == null ||(repCl = obj.getClass()) == cl){break;}cl = repCl;}if (enableReplace) {Object rep = replaceObject(obj);if (rep != obj && rep != null) {cl = rep.getClass();desc = ObjectStreamClass.lookup(cl, true);}obj = rep;}// if object replaced, run through original checks a second time//如果类信息被替换过,则需要进行第二次处理if (obj != orig) {subs.assign(orig, obj);if (obj == null) {writeNull();return;} else if (!unshared && (h = handles.lookup(obj)) != -1) {writeHandle(h);return;} else if (obj instanceof Class) {writeClass((Class) obj, unshared);return;} else if (obj instanceof ObjectStreamClass) {writeClassDesc((ObjectStreamClass) obj, unshared);return;}}// remaining cases//写入类实例对象的数据,第一次总是在此执行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) { //我们的bean需要实现Serializable接口,才能进行序列化writeOrdinaryObject(obj, desc, unshared);} else {if (extendedDebugInfo) {throw new NotSerializableException(cl.getName() + "\n" + debugInfoStack.toString());} else {throw new NotSerializableException(cl.getName());}}} finally {depth--;bout.setBlockDataMode(oldMode);}}
我们先简单的看下如果是一个String,如何做这个序列化的过程:
private void writeString(String str, boolean unshared) throws IOException {handles.assign(unshared ? null : str);long utflen = bout.getUTFLength(str);if (utflen <= 0xFFFF) {bout.writeByte(TC_STRING);bout.writeUTF(str, utflen);} else {bout.writeByte(TC_LONGSTRING);bout.writeLongUTF(str, utflen);}}
bout的writeUTF()方法:
void writeUTF(String s, long utflen) throws IOException {if (utflen > 0xFFFFL) {throw new UTFDataFormatException();}writeShort((int) utflen); //先写入长度,if (utflen == (long) s.length()) {writeBytes(s); //然后写入字节流} else {writeUTFBody(s);}}
很简单,就是写入一个字符串的一个字节的标示符,然后写入字符串的字节流。
那么再看看writeOrdinaryObject(obj, desc, unshared)如何对一个bean进行序列化
private void writeOrdinaryObject(Object obj,ObjectStreamClass desc,boolean unshared)throws IOException{if (extendedDebugInfo) {debugInfoStack.push((depth == 1 ? "root " : "") + "object (class \"" +obj.getClass().getName() + "\", " + obj.toString() + ")");}try {desc.checkSerialize(); //检查下是否可以进行序列化,比如socket对象之类的,如果对象无法进行序列化,则抛出异常。bout.writeByte(TC_OBJECT); //先写入一个字节的类对象的标示符writeClassDesc(desc, false); //序列化对象的class类信息handles.assign(unshared ? null : obj); //保存类的seariableID跟对象的映射关系if (desc.isExternalizable() && !desc.isProxy()) { //如果我们自定义了对象的序列化过程,则调用对象的writeExternalData方法。如果实现Externalizable// /** true if represented class implements Externalizable */// private boolean externalizable;writeExternalData((Externalizable) obj);} else {writeSerialData(obj, desc); //否则调用默认的序列化方法}} finally {if (extendedDebugInfo) {debugInfoStack.pop();}}}
然后我们先看看writeClassDesc(desc, false)的实现:
private void writeClassDesc(ObjectStreamClass desc, boolean unshared)throws IOException{int handle;if (desc == null) {writeNull();} else if (!unshared && (handle = handles.lookup(desc)) != -1) {writeHandle(handle);} else if (desc.isProxy()) { //如果是proxy对象,则调用该序列化机制 ,isProxy的判断//isProxy = Proxy.isProxyClass(cl); Returns true if and only if the specified class was dynamically generated to be a proxy class using the getProxyClass method or the newProxyInstance method.writeProxyDesc(desc, unshared);} else {writeNonProxyDesc(desc, unshared);}}/*** Writes class descriptor representing a dynamic proxy class to stream.*/private void writeProxyDesc(ObjectStreamClass desc, boolean unshared)throws IOException{bout.writeByte(TC_PROXYCLASSDESC); //写入代理对象的标示符handles.assign(unshared ? null : desc);Class cl = desc.forClass();Class[] ifaces = cl.getInterfaces(); //如果是proxy对象,则写入对象的interfaces的名称bout.writeInt(ifaces.length); //先写入interface个数for (int i = 0; i < ifaces.length; i++) {bout.writeUTF(ifaces[i].getName()); //再写入每个interface的名称}bout.setBlockDataMode(true);annotateProxyClass(cl);bout.setBlockDataMode(false);bout.writeByte(TC_ENDBLOCKDATA); //结束标签writeClassDesc(desc.getSuperDesc(), false);//递归写入父类的序列化信息,因为java是单继承,}/*** Writes class descriptor representing a standard (i.e., not a dynamic* proxy) class to stream.*/private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared)throws IOException{bout.writeByte(TC_CLASSDESC);//写入class对象的标示符handles.assign(unshared ? null : desc);if (protocol == PROTOCOL_VERSION_1) { //如果非代理类对象的具体class信息,查看下面的方法// do not invoke class descriptor write hook with old protocoldesc.writeNonProxy(this);} else {writeClassDescriptor(desc);}Class cl = desc.forClass();bout.setBlockDataMode(true);annotateClass(cl);bout.setBlockDataMode(false);bout.writeByte(TC_ENDBLOCKDATA);writeClassDesc(desc.getSuperDesc(), false);//递归写入父类的序列化信息,因为java是单继承,}
writeClassDescriptor(desc)方法:
throws IOException{desc.writeNonProxy(this);}void writeNonProxy(ObjectOutputStream out) throws IOException {out.writeUTF(name);//写入类的名称out.writeLong(getSerialVersionUID());//写入类的SerialVersionUIDbyte flags = 0;if (externalizable) {//是否实现externalizable接口flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;int protocol = out.getProtocolVersion();if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {flags |= ObjectStreamConstants.SC_BLOCK_DATA;}} else if (serializable) {//是否实现serializable接口flags |= ObjectStreamConstants.SC_SERIALIZABLE;}if (hasWriteObjectData) {//是否有自定义的重写序列化方法flags |= ObjectStreamConstants.SC_WRITE_METHOD;}if (isEnum) {flags |= ObjectStreamConstants.SC_ENUM;//是否是枚举}out.writeByte(flags);out.writeShort(fields.length); //遍历写入各个类的各个field字段类型名称等信息for (int i = 0; i < fields.length; i++) {ObjectStreamField f = fields[i];out.writeByte(f.getTypeCode()); //typecode参考ObjectStreamField.java类out.writeUTF(f.getName());if (!f.isPrimitive()) {out.writeTypeString(f.getTypeString());}}}
typecode:
case 'Z': type = Boolean.TYPE; break;case 'B': type = Byte.TYPE; break;case 'C': type = Character.TYPE; break;case 'S': type = Short.TYPE; break;case 'I': type = Integer.TYPE; break;case 'J': type = Long.TYPE; break;case 'F': type = Float.TYPE; break;case 'D': type = Double.TYPE; break;case 'L':case '[': type = Object.class; break;
至此,对象obj的class相关信息已经全部写入。
然后我们再查看具体写入obj数据的过程
private void writeSerialData(Object obj, ObjectStreamClass desc)throws IOException{ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();for (int i = 0; i < slots.length; i++) {ObjectStreamClass slotDesc = slots[i].desc;if (slotDesc.hasWriteObjectMethod()) { //Returns true if represented class is serializable (but not externalizable) and defines a conformant writeObject method. Otherwise, returns false.Object oldObj = curObj;ObjectStreamClass oldDesc = curDesc;PutFieldImpl oldPut = curPut;curObj = obj;curDesc = slotDesc;curPut = null;if (extendedDebugInfo) {debugInfoStack.push("custom writeObject data (class \"" +slotDesc.getName() + "\")");}try {bout.setBlockDataMode(true);slotDesc.invokeWriteObject(obj, this);bout.setBlockDataMode(false);bout.writeByte(TC_ENDBLOCKDATA);} finally {if (extendedDebugInfo) {debugInfoStack.pop();}}curObj = oldObj;curDesc = oldDesc;curPut = oldPut;} else {defaultWriteFields(obj, slotDesc); //写入对象的字段数据}}}private void defaultWriteFields(Object obj, ObjectStreamClass desc)throws IOException{// REMIND: perform conservative isInstance check here?desc.checkDefaultSerialize();int primDataSize = desc.getPrimDataSize(); //先写入private field的数据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++) { //写入非private的数据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()); //递归调用writeObject0写入每个field的数据} finally {if (extendedDebugInfo) {debugInfoStack.pop();}}}}
下面我们来分析下序列化后的字节流内容:
先写一段测试代码:
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 ObjectOutputStreamextends OutputStream implements ObjectOutput, ObjectStreamConstants{public void close() throws IOException {flush();clear();bout.close();}
然后bout.close()方法调用:
private static class BlockDataOutputStreamextends 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) {//这里为truewriteBlockHeader(pos);//然后写入关于序列化内容的两个信息}out.write(buf, 0, pos);pos = 0;}private void writeBlockHeader(int len) throws IOException {if (len <= 0xFF) {hbuf[0] = TC_BLOCKDATA; //长度小于0xff时,写入BLOCKDATA的头 == 0x77hbuf[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)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 protocoldesc.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 34byte 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 02out.writeShort(fields.length);//写入对象的序列化字段个数 这里=2,a,b, 2byte 00 02for (int i = 0; i < fields.length; i++) {//遍历flag,写入每个flag的信息 49 00 01 61 49 00 01 62ObjectStreamField f = fields[i];out.writeByte(f.getTypeCode()); //1byteout.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();}}}}
