前言
上一遍关于HashSet的最后部分提到自定义序列化重写序列化的readObect和writeObject方法,个人感觉结束比较仓促。由于序列化在java中有着举重轻重的地位,尤其是在RPC框架中,对象的传输都是通过序列化完成。所以萌生了对java的序列化做一次系统的总结。
初步设想关于java序列化的总结分成三部分:java序列化的内部实现、java反序列化的内部实现、java序列化用法以及理论。
本篇分享为第一部分“java序列化的内部实现”,主要是通过一个序列化实例 剖析java序列化的内部实现过程。
Java对象序列化
java提供了一个“对象序列化框架”,可以将“对象”编码为“字节流”,这个过程称之为“序列化”;反之 也可以从“字节流”编码中重新构建成一个新的对象,这个过程称之为“反序列化”。一旦一个对象被序列化后,就可以通过网络从一台服务器传输到另一台服务器,再进行存储或者反序列化后使用。这个过程在RPC框架中大量使用,因此良好的序列化设计可以减少这个过程的开销,提升服务性能。
Java对象的序列化和反序列化可以通过ObjectOutputStream和ObjectInputStream实现,首先编写一个简单的对象序列化和反序列化实现,这里模拟将一个User对象序列化并存储到D盘的user.txt文件中,代码如下:
package com.sky.serial; import java.io.*; /** * Created by gantianxing on 2017/5/26. */ public class User implements Serializable { //可以用eclipse生成, 也可以随意指定一个非0的值 private static final long serialVersionUID = 1L; private final String name;//姓名 private final int sex;//性别0-女 1-男 private String phoneNum;//手机号 public User(String name,int sex){ this.name = name; this.sex = sex; } public String getName() { return name; } public int getSex() { return sex; } public String getPhoneNum() { return phoneNum; } public void setPhoneNum(String phoneNum) { this.phoneNum = phoneNum; } @Override public String toString(){ return "user info: name="+name+",sex="+sex+",phoneNum="+phoneNum; } public static void main(String[] args) throws Exception{ ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("D://user.txt")); //实例化一个user对象 User user = new User("zhang san",1); user.setPhoneNum("13888888888"); //将对象序列化存储到D:/user.txt 文件中 out.writeObject(user); ObjectInputStream in = new ObjectInputStream(new FileInputStream("D://user.txt")); //反序列化 Object newUser = in.readObject(); System.out.println(newUser); } }
执行上面代码的main方法,控制台打印结果为:
” user info: name=zhang san,sex=1,phoneNum=13888888888”。
打开D:/user.txt 文件进行查看,由于我们是将字节流存储到文件中的,所有直接查看 看到的是乱码,所以需要以二进制的格式查看。java序列化的的过程中都是以16进制写入字节流,采用16进制格式查看对阅读java序列化的源码很方便(用UE和EditPlus都可以,我这里使用的EditPlus)。结果为:
AC ED 00 05 73 72 00 13 63 6F 6D 2E 73 6B 79 2E
73 65 72 69 61 6C 2E 55 73 65 72 00 00 00 00 00
00 00 01 02 00 03 49 00 03 73 65 78 4C 00 04 6E
61 6D 65 74 00 12 4C 6A 61 76 61 2F 6C 61 6E 67
2F 53 74 72 69 6E 67 3B 4C 00 08 70 68 6F 6E 65
4E 75 6D 71 00 7E 00 01 78 70 00 00 00 01 74 00
09 7A 68 61 6E 67 20 73 61 6E 74 00 0B 31 33 38
38 38 38 38 38 38 38 38
思维先转变过来:这里的每个空格隔开的是1个16进制位 表示的是一个字节。直接看这一串16进制可以能有点晕。通过阅读java对象序列化的源码,我把它翻译了一下,每个字节的具体含义如下:
对应上图,我把每一个块儿,再详细描述一次(一共分为28步):
1、AC ED 00 05:在调用ObjectOutputStream(OutputStream out) 构造方法时生成。具体是在writeStreamHeader()方法:
protected void writeStreamHeader() throws IOException { bout.writeShort(STREAM_MAGIC); //魔法数(Magic number 翻译) bout.writeShort(STREAM_VERSION);//版本号 }
STREAM_MAGIC 、STREAM_VERSION 是在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;
AC ED为魔法数。版本号为short类型的5,short是两个字节,用两个字节的16进制表示5 即为:00 05
2、73:TC_OBJECT在ObjectStreamConstants中定义为新对象,表示接下来是一个对象。
/** * new Object. */ final static byte TC_OBJECT = (byte)0x73;
3、72:TC_CLASSDESC表示接下来是类的描述信息,在ObjectStreamConstants中定义为:
/** * new Class Descriptor. */ final static byte TC_CLASSDESC = (byte)0x72;
4、00 13:表示该对象对应类的全路径类名长度(com.sky.serial.User长度为19),这里是16进制表示 0x13,转换为10进制即为 19。
5、63 6F 6D 2E 73 6B 79 2E 73 65 72 69 61 6C 2E 55 73 65 72:就是用16进制表示的字符串com.sky.serial.User
接下来是成员变量的描述信息:
6、00 00 00 00 00 00 00 01:这8个字节表示的是long型的SUID,对应的private static final long serialVersionUID = 1L;
7、02 : SC_SERIALIZABLE表示该类支持序列化,在ObjectStreamConstants中定义为:
/** * Bit mask for ObjectStreamClass flag. Indicates class is Serializable. */ final static byte SC_SERIALIZABLE = 0x02;
8、00 03:表示成员变量个数,user类中的成员变量个数为3(name、sex、phoneNum)。
9、49: 表示第一个成员变量(sex)的类型,16进制的49转换为十进制为73,刚好是大写字母’I’对应的值。’I’ 表示该类型为int。
10、00 03: 表示该变量名的长度,“sex”的长度为3。
11、73 65 78:转换成十进制分别为 115 101 120,对应的字符分布为s e x,即字符串sex。
第一个成员的描述信息结束
12、4C: 表示第二个成员变量(name)的类型,转换为十进制:76,对应为大写字母‘L’,表示是一个类。
13、00 04:表示变量名的长度,“name”的长度为4
6E 61 6D 65:转换为十进制分别为110 97 109 101,对应的字符拼接起来刚好是字符串”name”。
14、74:TC_STRING 表示对象为String类型,在ObjectStreamConstants中定义为:
/**
* new String.
*/
final static byte TC_STRING = (byte)0x74;
15、00 12:表示这个类描述信息的长度("Ljava/lang/String;"),对应的十进制为18。
16、4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B: 表示的即为字符串 "Ljava/lang/String;"
第二个成员变量描述信息结束
17、4C:第三个成员变量(phoneNum),依然是一个对象(还是为String)。
18、00 08:” phoneNum”的长度为8。
19、70 68 6F 6E 65 4E 75 6D:对应的值即为字符串 ” phoneNum”。
20、71: TC_REFERENCE表示是引用,这里” phoneNum”也是String类型,前面已经解析过一次String(第二个成员变量),以后再用直接应用即可。在ObjectStreamConstants中定义为:
/** * Reference to an object already written into the stream. */ final static byte TC_REFERENCE = (byte)0x71;
21、00 7E 00 01:新创建对象从00 7E 00 00开始,01表示引用序号(从00到XX) 这里表示第二个成员变量。在ObjectStreamConstants中定义为:
/** * First wire handle to be assigned. */ final static int baseWireHandle = 0x7e0000;
22、78:TC_ENDBLOCKDATA表是类的描述信息序列化结束,与前面的72相对应,在ObjectStreamConstants中定义为:
/** * End of optional block data blocks for an object. */ final static byte TC_ENDBLOCKDATA = (byte)0x78;
23、70:TC_NULL表示已经被引用,即没有父类,如果该类还是父类,下一步继续解析父类的类描述信息。这里我们的user没有父类,类描述信息序列化结束。在ObjectStreamConstants中定义为:
/** * Null object reference. */ final static byte TC_NULL = (byte)0x70;
接下来开始序列化,成员变量的值信息。
24、00 00 00 01:第一个成员变量sex的值,这里为整型值1,int是4个字节,即表示为:00 00 00 01
25、74 00 09:第二成员变量,74表示接下来的值为一个String对象,00 09 表示值的长度,这里的值为“zhang san” 刚好为9。
26、7A 68 61 6E 67 20 73 61 6E:表示的是第二个成员变量的值 “zhang san”。
27、74 00 0B:第三个成员变量,74同样表示接下来的值为一个String对象,00 0B表示长度是11,这里刚好是“13888888888”的长度
28、31 33 38 38 38 38 38 38 38 38 38:表示的是第三个成员变量的值“13888888888”(逐个字符拼接,比如字符8,对应的int值为56,对应的16进制即为0x38)。
到这里整个序列化流程结束。这只是一个最简单的对象序列化过程,复杂点的比如user类多级继承,成员变量还有自定义对象,自定义对象里又有多级继承等等,序列化会解析类的整个拓扑结构。这里就不再对其他复杂的序列化字节文件进行逐一分析,我们可以在下一节阅读源码中,看到各种情况的序列化过程。
简单总结下:java对象的序列化分成两部分,前一部分是对类以及成员的描述进行序列化(元数据),第二部分是对成员的值进行序列化。
序列化过程源码解析
也许你会问上一节中的字节码是怎么解析出来的,其实一切尽在阅读java序列化框架的源码。Java序列化框架的核心类包括:ObjectOutputStream、ObjectInputStream、ObjectStreamClass、ObjectStreamField、ObjectStreamConstants。
其中序列化和反序列化的主要业务逻辑都在ObjectOutputStream、ObjectInputStream中完成,ObjectStreamClass里主要存放类描述信息,ObjectStreamField存放成员变量描述信息,ObjectStreamConstants存放的主要是一些16进制的常量(文章最后会列举出主要的常量信息)。
先看下上一节中的序列化代码:
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("D://user.txt")); //实例化一个user对象 User user = new User("zhang san",1); user.setPhoneNum("13888888888"); //将对象序列化存储到D:/user.txt 文件中 out.writeObject(user);
第一步创建首一个ObjectOutputStream的实例out,然后调用out的writeObject()方法。我们就以这里为入口,开始阅读java对象序列化的源码。
1、ObjectOutputStream(OutputStream out) 构造方法处理逻辑:
public ObjectOutputStream(OutputStream out) throws IOException { verifySubclass(); //验证有无子类,这里没有 bout = new BlockDataOutputStream(out);//实例化输出流,通过调用bout.writexxx方法向流中写入内容 handles = new HandleTable(10, (float) 3.00); //初始化handles map,已序列化过的对象成员会放入其中 subs = new ReplaceTable(10, (float) 3.00); //初始化替换对象(替换)map enableOverride = false; writeStreamHeader();//写入魔法数、版本 “AC ED 00 05” bout.setBlockDataMode(true); if (extendedDebugInfo) {//开启序列化日志-D sun.io.serialization.extendedDebugInfo=true debugInfoStack = new DebugTraceInfoStack(); } else { debugInfoStack = null; } }
该构造方法,主要初始化工作:a、实例化输出流对象bout,后续调用其writexxx方法向流中写入数据;b、初始化一个内部实现的handles Map,后续用于存放已经处理过的对象成的引用;c、初始化一个内部实现的subs Map,后续用于存放ObjectOutputStream的受信子类的替换对象(用一个对象替换另外一个对象)
2、第二步再开来序列化入口方法writeObject(Object obj)
public final void writeObject(Object obj) throws IOException { if (enableOverride) {//初始化时是false,跳过 writeObjectOverride(obj);//执行子类的writeObjectOverride方法 return; } try { writeObject0(obj, false);//调用该方法向流 写入对象 } catch (IOException ex) { if (depth == 0) { writeFatalException(ex); } throw ex; } }
这个方法先判断bout是否是ObjectOutputStream的子类。这我们直接使用ObjectOutputStream进行实例化,跳过if方法,直接执行writeObject0方法。
3、第三步,调用writeObject0方法:
private void writeObject0(Object obj, boolean unshared) throws IOException { boolean oldMode = bout.setBlockDataMode(false); depth++; //递归深度 try { // handle previously written and non-replaceable objects int h; if ((obj = subs.lookup(obj)) == null) { // subs.lookup(obj) 查找是否有替换对象 writeNull(); //写入空对象 TC 70 return; } else if (!unshared && (h = handles.lookup(obj)) != -1) {// handles.lookup(obj) 查找是否写入,如果已经写入过,直接引用 writeHandle(h);//写入引用TC 71 return; } else if (obj instanceof Class) { writeClass((Class) obj, unshared); //写入引用类TC 76 return; } else if (obj instanceof ObjectStreamClass) { writeClassDesc((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);//根据cl创建ObjectStreamClass,即把待序列化对象 相关信息写入ObjectStreamClass, 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) {//如果已替换,重新执行一次检查,写入TC 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); //写入String } else if (cl.isArray()) { writeArray(obj, desc, unshared); //写入数组 } else if (obj instanceof Enum) { writeEnum((Enum<?>) obj, desc, unshared);//写入枚举 } else if (obj instanceof Serializable) { writeOrdinaryObject(obj, desc, unshared); //写入序列化对象 TC_OBJECT 73 、类描述信息(类名、suid、序列化、成员个数) } else { if (extendedDebugInfo) { throw new NotSerializableException( cl.getName() + "\n" + debugInfoStack.toString()); } else { throw new NotSerializableException(cl.getName()); } } } finally { depth--; bout.setBlockDataMode(oldMode); } }
第一次进入writeObject0 方法前面验证都会跳过,直到调用writeOrdinaryObject方法,开始写入类描述信息
4、第四步,调用writeOrdinaryObject方法 开始写入User类描述信息
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);//写入新类TC 73 writeClassDesc(desc, false);//写入类描述信息、成员变量描述信息 handles.assign(unshared ? null : obj);//处理过的类写入handles,以便下次直接引用使用 if (desc.isExternalizable() && !desc.isProxy()) { writeExternalData((Externalizable) obj);//调用用户重写的writeExternal方法,写入成员变量值 } else { writeSerialData(obj, desc);//否则调用默认方法 写入成员变量值 } } finally { if (extendedDebugInfo) { debugInfoStack.pop(); } } }
这个方法首先调用writeClassDesc()方法向流中写入类描述信息、成员变量描述信息。再调用writeSerialData()方法(在步骤6中讲解),写入成员变量值。先看writeClassDesc()
private void writeClassDesc(ObjectStreamClass desc, boolean unshared) throws IOException { int handle; if (desc == null) { writeNull(); } else if (!unshared && (handle = handles.lookup(desc)) != -1) {//handle中已经存在 该类描述信息 直接引用 writeHandle(handle); } else if (desc.isProxy()) { writeProxyDesc(desc, unshared);//代理 } else { writeNonProxyDesc(desc, unshared);//非代理写入 } }
这里调用 writeNonProxyDesc方法。
private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared) throws IOException { bout.writeByte(TC_CLASSDESC); 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); if (cl != null && isCustomSubclass()) { ReflectUtil.checkPackageAccess(cl); } annotateClass(cl); bout.setBlockDataMode(false); bout.writeByte(TC_ENDBLOCKDATA); //该类描述信息写入完毕 TC_ENDBLOCKDATA 78; writeClassDesc(desc.getSuperDesc(), false); //判断是否还有父类,如果有继续写父类描述信息,否则写入空对象TC_NULL 结束 }
该方法先写入类描述开始TC_CLASSDESC (72)标记;
然后调用writeClassDescriptor(ObjectStreamClass desc)方法à调用ObjectStreamClass类的writeNonProxy方法写入类描述信息,以及成员描述信息;
最后写入类描述完毕TC_ENDBLOCKDATA标记(78), 并判断是否还有父类,如果有,继续递归调用writeClassDesc方法写入父类描述信息,否则 写入空对象标记TC_NULL(70)。
5、最终调用的ObjectStreamClass类的writeNonProxy方法,写入类描述信息:
protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException { desc.writeNonProxy(this); } //ObjectStreamClass类的writeNonProxy方法 void writeNonProxy(ObjectOutputStream out) throws IOException { out.writeUTF(name); out.writeLong(getSerialVersionUID()); //写入suid,这里是1L, 8个字节表示为: 00 00 00 00 00 00 00 01 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;//这里User类实现Serializable,判断逻辑走这里 } if (hasWriteObjectData) { flags |= ObjectStreamConstants.SC_WRITE_METHOD; } if (isEnum) { flags |= ObjectStreamConstants.SC_ENUM; } out.writeByte(flags); //写入 SC_SERIALIZABLE (02) out.writeShort(fields.length); //写入成员变量个数3个 for (int i = 0; i < fields.length; i++) {//遍历成员变量sex、name、phoneNum ObjectStreamField f = fields[i]; out.writeByte(f.getTypeCode()); //写入成员变量类型 out.writeUTF(f.getName()); //写入成员变量名称 if (!f.isPrimitive()) { //如果是对象,写入对象类型,这里name、phoneNum需要写入 out.writeTypeString(f.getTypeString());//写入对象类型 } } } void writeTypeString(String str) throws IOException { int handle; if (str == null) { writeNull(); } else if ((handle = handles.lookup(str)) != -1) { writeHandle(handle); //phoneNum调用这个方法,直接引用name的String描述即可 } else { writeString(str, false); //name字段 调用这个方法 } } private void writeString(String str, boolean unshared) throws IOException { handles.assign(unshared ? null : str); //name 字段的类型String 在这一步写入handles Map,后续如果还有String成员,直接引用即可。 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); } }
以上是类描述和成员变量描述信息的序列化。
6、回到步骤4,类描述和成员变量描述信息的序列化完成后,继续调用writeSerialData方法写入各个成员变量值信息。
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()) { //判断是否重新了writeObject方法 PutFieldImpl oldPut = curPut; curPut = null; SerialCallbackContext oldContext = curContext; if (extendedDebugInfo) { debugInfoStack.push( "custom writeObject data (class \"" + slotDesc.getName() + "\")"); } try { curContext = new SerialCallbackContext(obj, slotDesc); bout.setBlockDataMode(true); slotDesc.invokeWriteObject(obj, this);//调用对象重写的writeObject方法 bout.setBlockDataMode(false); bout.writeByte(TC_ENDBLOCKDATA); } finally { curContext.setUsed(); curContext = oldContext; if (extendedDebugInfo) { debugInfoStack.pop(); } } curPut = oldPut; } else { defaultWriteFields(obj, slotDesc); //User对象的成员值写入,调用这个默认方法 } } }
ObjectStreamClass 类的解析成员拓扑结构方法
private ClassDataSlot[] getClassDataLayout0() throws InvalidClassException { ArrayList<ClassDataSlot> slots = new ArrayList<>(); Class<?> start = cl, end = cl; // locate closest non-serializable superclass while (end != null && Serializable.class.isAssignableFrom(end)) { end = end.getSuperclass(); } HashSet<String> oscNames = new HashSet<>(3); for (ObjectStreamClass d = this; d != null; d = d.superDesc) { if (oscNames.contains(d.name)) { throw new InvalidClassException("Circular reference."); } else { oscNames.add(d.name); } // search up inheritance hierarchy for class with matching name String searchName = (d.cl != null) ? d.cl.getName() : d.name; Class<?> match = null; for (Class<?> c = start; c != end; c = c.getSuperclass()) { if (searchName.equals(c.getName())) { match = c; break; } } // add "no data" slot for each unmatched class below match if (match != null) { for (Class<?> c = start; c != match; c = c.getSuperclass()) { slots.add(new ClassDataSlot( ObjectStreamClass.lookup(c, true), false)); } start = match.getSuperclass(); } // record descriptor/class pairing slots.add(new ClassDataSlot(d.getVariantFor(match), true)); //记录类的继承拓扑关系() } // add "no data" slot for any leftover unmatched classes for (Class<?> c = start; c != end; c = c.getSuperclass()) { slots.add(new ClassDataSlot( ObjectStreamClass.lookup(c, true), false)); //记录类的继承拓扑关系 } // order slots from superclass -> subclass Collections.reverse(slots); return slots.toArray(new ClassDataSlot[slots.size()]); }
7、defaultWriteFields默认的值写入方法
private void defaultWriteFields(Object obj, ObjectStreamClass desc) throws IOException { Class<?> cl = desc.forClass(); if (cl != null && obj != null && !cl.isInstance(obj)) { throw new ClassCastException(); } 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);//写入基础类型,如:int char long等,这里是写入sex字段对应的值 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()); //递归调用writeObject0方法写入对象,这里是写入String值 } finally { if (extendedDebugInfo) { debugInfoStack.pop(); } } } }
到这里整个序列化过程结束。
ObjectStreamClass 描述类初始化过程
在上一节中步骤3中,调用desc = ObjectStreamClass.lookup(cl, true);根据cl创建ObjectStreamClass。简单的说就是把User对象相关信息先写入ObjectStreamClass中,共后续bout写入流使用。
static ObjectStreamClass lookup(Class<?> cl, boolean all) { if (!(all || Serializable.class.isAssignableFrom(cl))) { return null; } processQueue(Caches.localDescsQueue, Caches.localDescs); WeakClassKey key = new WeakClassKey(cl, Caches.localDescsQueue); //创建弱引用的key对象 Reference<?> ref = Caches.localDescs.get(key); Object entry = null; if (ref != null) { entry = ref.get(); } EntryFuture future = null; if (entry == null) { EntryFuture newEntry = new EntryFuture(); Reference<?> newRef = new SoftReference<>(newEntry); do { if (ref != null) { Caches.localDescs.remove(key, ref); //如果缓存中已经存在,先移除 } ref = Caches.localDescs.putIfAbsent(key, newRef);//放入的新类描述到缓存中 if (ref != null) { entry = ref.get(); } } while (ref != null && entry == null); if (entry == null) { future = newEntry; } } if (entry instanceof ObjectStreamClass) { // check common case first return (ObjectStreamClass) entry; } if (entry instanceof EntryFuture) { future = (EntryFuture) entry; if (future.getOwner() == Thread.currentThread()) { /* * Handle nested call situation described by 4803747: waiting * for future value to be set by a lookup() call further up the * stack will result in deadlock, so calculate and set the * future value here instead. */ entry = null; } else { entry = future.get(); } } if (entry == null) { try { entry = new ObjectStreamClass(cl); //调用该构造方法进行初始化 } catch (Throwable th) { entry = th; } if (future.set(entry)) { Caches.localDescs.put(key, new SoftReference<Object>(entry)); //类描述信息放入缓存 } else { // nested lookup call already set future entry = future.get(); } } if (entry instanceof ObjectStreamClass) { return (ObjectStreamClass) entry; } else if (entry instanceof RuntimeException) { throw (RuntimeException) entry; } else if (entry instanceof Error) { throw (Error) entry; } else { throw new InternalError("unexpected entry: " + entry); } }
//类描述初始化 private ObjectStreamClass(final Class<?> cl) { this.cl = cl; name = cl.getName(); //对象类名 isProxy = Proxy.isProxyClass(cl); //是否是代理类 isEnum = Enum.class.isAssignableFrom(cl); //是否是枚举 serializable = Serializable.class.isAssignableFrom(cl); //是否实现了序列化接口 externalizable = Externalizable.class.isAssignableFrom(cl);//是否实现了externalizable接口 Class<?> superCl = cl.getSuperclass(); superDesc = (superCl != null) ? lookup(superCl, false) : null; //直接父类,Object不算 localDesc = this; if (serializable) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { if (isEnum) { suid = Long.valueOf(0); fields = NO_FIELDS; return null; } if (cl.isArray()) { fields = NO_FIELDS; return null; } suid = getDeclaredSUID(cl); //获取对象的suid try { fields = getSerialFields(cl); computeFieldOffsets(); } catch (InvalidClassException e) { serializeEx = deserializeEx = new ExceptionInfo(e.classname, e.getMessage()); fields = NO_FIELDS; } if (externalizable) { //是否实现Externalizable接口 cons = getExternalizableConstructor(cl); } else { cons = getSerializableConstructor(cl); writeObjectMethod = getPrivateMethod(cl, "writeObject", new Class<?>[] { ObjectOutputStream.class }, Void.TYPE); //获取重写的writeObject方法 readObjectMethod = getPrivateMethod(cl, "readObject", new Class<?>[] { ObjectInputStream.class }, Void.TYPE);//获取重写的readObject方法 readObjectNoDataMethod = getPrivateMethod( cl, "readObjectNoData", null, Void.TYPE);//获取重写的readObjectNoData方法 hasWriteObjectData = (writeObjectMethod != null); } writeReplaceMethod = getInheritableMethod( cl, "writeReplace", null, Object.class);//获取重写的writeReplace方法 readResolveMethod = getInheritableMethod( cl, "readResolve", null, Object.class);//获取重写的readResolve方法 return null; } }); } else { suid = Long.valueOf(0); fields = NO_FIELDS; } try { fieldRefl = getReflector(fields, this); //初始化成员变量描述信息 } catch (InvalidClassException ex) { // field mismatches impossible when matching local fields vs. self throw new InternalError(ex); } if (deserializeEx == null) { if (isEnum) { deserializeEx = new ExceptionInfo(name, "enum type"); } else if (cons == null) { deserializeEx = new ExceptionInfo(name, "no valid constructor"); } } for (int i = 0; i < fields.length; i++) { if (fields[i].getField() == null) { defaultSerializeEx = new ExceptionInfo( name, "unmatched serializable field(s) declared"); } } initialized = true; }
//获取对象的成员变量列表 private static ObjectStreamField[] getSerialFields(Class<?> cl) throws InvalidClassException { ObjectStreamField[] fields; if (Serializable.class.isAssignableFrom(cl) && !Externalizable.class.isAssignableFrom(cl) && !Proxy.isProxyClass(cl) && !cl.isInterface()) { if ((fields = getDeclaredSerialFields(cl)) == null) { fields = getDefaultSerialFields(cl); } Arrays.sort(fields); //排序,这就是为什么按照sex、name、phoneNum进行排序 } else { fields = NO_FIELDS; } return fields; }
//初始化成员变量描述信息, private static FieldReflector getReflector(ObjectStreamField[] fields, ObjectStreamClass localDesc) throws InvalidClassException { // class irrelevant if no fields Class<?> cl = (localDesc != null && fields.length > 0) ? localDesc.cl : null; processQueue(Caches.reflectorsQueue, Caches.reflectors); FieldReflectorKey key = new FieldReflectorKey(cl, fields, Caches.reflectorsQueue); Reference<?> ref = Caches.reflectors.get(key); Object entry = null; if (ref != null) { entry = ref.get(); } EntryFuture future = null; if (entry == null) { EntryFuture newEntry = new EntryFuture(); Reference<?> newRef = new SoftReference<>(newEntry); do { if (ref != null) { Caches.reflectors.remove(key, ref); } ref = Caches.reflectors.putIfAbsent(key, newRef);//重新缓存 if (ref != null) { entry = ref.get(); } } while (ref != null && entry == null); if (entry == null) { future = newEntry; } } if (entry instanceof FieldReflector) { // check common case first return (FieldReflector) entry; } else if (entry instanceof EntryFuture) { entry = ((EntryFuture) entry).get(); } else if (entry == null) { try { entry = new FieldReflector(matchFields(fields, localDesc)); //调用FieldReflector构造方法进行初始化 } catch (Throwable th) { entry = th; } future.set(entry); Caches.reflectors.put(key, new SoftReference<Object>(entry));//成员表里描述信息放入缓存 } if (entry instanceof FieldReflector) { return (FieldReflector) entry; } else if (entry instanceof InvalidClassException) { throw (InvalidClassException) entry; } else if (entry instanceof RuntimeException) { throw (RuntimeException) entry; } else if (entry instanceof Error) { throw (Error) entry; } else { throw new InternalError("unexpected entry: " + entry); } }
FieldReflector是ObjectStreamClass的内部静态类,上一步中循环调用matchFields方法对ObjectStreamField初始化:
private static ObjectStreamField[] matchFields(ObjectStreamField[] fields, ObjectStreamClass localDesc) throws InvalidClassException { ObjectStreamField[] localFields = (localDesc != null) ? localDesc.fields : NO_FIELDS; /* * Even if fields == localFields, we cannot simply return localFields * here. In previous implementations of serialization, * ObjectStreamField.getType() returned Object.class if the * ObjectStreamField represented a non-primitive field and belonged to * a non-local class descriptor. To preserve this (questionable) * behavior, the ObjectStreamField instances returned by matchFields * cannot report non-primitive types other than Object.class; hence * localFields cannot be returned directly. */ ObjectStreamField[] matches = new ObjectStreamField[fields.length]; for (int i = 0; i < fields.length; i++) { ObjectStreamField f = fields[i], m = null; for (int j = 0; j < localFields.length; j++) { ObjectStreamField lf = localFields[j]; if (f.getName().equals(lf.getName())) { if ((f.isPrimitive() || lf.isPrimitive()) && f.getTypeCode() != lf.getTypeCode()) { throw new InvalidClassException(localDesc.name, "incompatible types for field " + f.getName()); } if (lf.getField() != null) { m = new ObjectStreamField( lf.getField(), lf.isUnshared(), false); //调用ObjectStreamField的构造方法进行初始化 } else { m = new ObjectStreamField( lf.getName(), lf.getSignature(), lf.isUnshared()); } } } if (m == null) { m = new ObjectStreamField( f.getName(), f.getSignature(), false); } m.setOffset(f.getOffset()); matches[i] = m; } return matches; }
// ObjectStreamField构造方法 ObjectStreamField(Field field, boolean unshared, boolean showType) { this.field = field; this.unshared = unshared; name = field.getName(); Class<?> ftype = field.getType(); type = (showType || ftype.isPrimitive()) ? ftype : Object.class; signature = getClassSignature(ftype).intern(); }
关于常量
需要说下ObjectStreamConstants常量类,里面记录有java序列化的各种标记,多熟悉这里的常量,对阅读源码很有帮助。这里就不再列举,可以自行查阅。
关于成员变量的标记在ObjectStreamField的getClassSignature中定义的:
private static String getClassSignature(Class<?> cl) { StringBuilder sbuf = new StringBuilder(); while (cl.isArray()) { sbuf.append('['); //数组类型 cl = cl.getComponentType(); } if (cl.isPrimitive()) { //基础类型 if (cl == Integer.TYPE) { sbuf.append('I'); } else if (cl == Byte.TYPE) { sbuf.append('B'); } else if (cl == Long.TYPE) { sbuf.append('J'); } else if (cl == Float.TYPE) { sbuf.append('F'); } else if (cl == Double.TYPE) { sbuf.append('D'); } else if (cl == Short.TYPE) { sbuf.append('S'); } else if (cl == Character.TYPE) { sbuf.append('C'); } else if (cl == Boolean.TYPE) { sbuf.append('Z'); } else if (cl == Void.TYPE) { sbuf.append('V'); } else { throw new InternalError(); } } else { sbuf.append('L' + cl.getName().replace('.', '/') + ';'); //对象类型 } return sbuf.toString(); }
保护的构造方法中也有体现:
ObjectStreamField(String name, String signature, boolean unshared) { if (name == null) { throw new NullPointerException(); } this.name = name; this.signature = signature.intern(); this.unshared = unshared; field = null; switch (signature.charAt(0)) { 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; default: throw new IllegalArgumentException("illegal signature"); } }
成员变量的type 和signature互转的逻辑,也在上面两个方法中。
关于ObjectOutputStream中的引用重用
ObjectOutputStream中有两个静态内部类HandleTable、ReplaceTable,是两个简单的map实现。对应的成员变量为:
private final HandleTable handles; private final ReplaceTable subs;
分别用于存放已经序列化过的类描述对象引用、替换类描述对象引用。以便再后续序列化中,遇到相同类型直接引用即可,不再重复解析。
关于序列化对象替换:
新建一个类继承ObjectOutputStream,重写其replaceObject方法。看一个例子就明白了:
package com.sky.serial; import java.io.*; /** * Created by gantianxing on 2017/5/28. */ public class ObjectOutputStreamDemo extends ObjectOutputStream { public ObjectOutputStreamDemo(OutputStream out) throws IOException { super(out); } @Override public Object replaceObject(Object obj) throws IOException { return "replace"; } public static void main(String[] args) { Object s1 = "string1"; Object s2 = "string2"; try { // create a new file with an ObjectOutputStream FileOutputStream out = new FileOutputStream("D://ss.txt"); ObjectOutputStreamDemo oout = new ObjectOutputStreamDemo(out); // 序列化s1 oout.writeObject(s1); // 开启允许替换 oout.enableReplaceObject(true); // 替换 oout.replaceObject(s2); //写入S2,这时S2会被"replace" oout.writeObject(s2); // close the stream oout.close(); // create an ObjectInputStream for the file we created before ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D://ss.txt")); // read and print an int System.out.println("" + (String) ois.readObject()); System.out.println("" + (String) ois.readObject()); } catch (Exception ex) { ex.printStackTrace(); } } }
打印信息为:
string1 replace
说明“string2”在序列化时被“replace”替换。其实上面的源码解析中,也有涉及这部分内容。不再进行详细解析。
关于ObjectStreamClass类的缓存
ObjectStreamClass中一个静态的缓存内部类Caches,主要用途是在序列化的过程中尽量使用,已经存在的类描述对象和成员描述对象,减少内存消耗。
private static class Caches { /** cache mapping local classes -> descriptors */ static final ConcurrentMap<WeakClassKey,Reference<?>> localDescs = new ConcurrentHashMap<>(); /** cache mapping field group/local desc pairs -> field reflectors */ static final ConcurrentMap<FieldReflectorKey,Reference<?>> reflectors = new ConcurrentHashMap<>(); /** queue for WeakReferences to local classes */ private static final ReferenceQueue<Class<?>> localDescsQueue = new ReferenceQueue<>(); /** queue for WeakReferences to field reflectors keys */ private static final ReferenceQueue<Class<?>> reflectorsQueue = new ReferenceQueue<>(); }
localDescs、localDescsQueue对应的是类描述的缓存;
reflectors、reflectorsQueue对应的是成员描述的缓存;
localDescs、reflectors的key是弱引用WeakReference,value是软引用SoftReference
软弱引用都需要配合引用队列使用ReferenceQueue。
关于软、弱引入,引用队列这里就不展开讲啦,后面有时间再单独进行总结。
java基础类型的序列化比较简单,直接调用out.writexxx方法即可(比如out.writeInt(x))。其实java对象的序列化,本质上也是转化为基础类型的序列化,但为了表示对象的内部关系加了很多TC标识而已。
这次就到这里吧:-D
下一篇讲解《java反序列化的内部实现(二)》
相关推荐
Java序列化是Java平台中的一种标准机制,允许对象的状态被保存到磁盘或者在网络中进行传输,以便在后续的时间或地点恢复这些对象。这个过程包括两个主要操作:序列化(将对象转换为字节流)和反序列化(将字节流恢复...
【Protocol Buffer序列化对比Java序列化】 Protocol Buffer(简称PB)是Google开发的一种高效的数据序列化协议,而Java序列化是Java...而Java序列化则在简单的Java项目内部或已有大量Java序列化数据的情况下更为便利。
Java序列化是Java平台中的一种标准机制,允许将对象的状态转换为字节流,以便可以存储这个状态(例如,到磁盘或通过网络传输),之后再恢复为原来的对象。这在许多场景下都非常有用,例如持久化数据、跨网络传输对象...
Java序列化是Java平台内建的一种机制,允许对象的状态被转换为字节流,以便存储、传输或在稍后的时间点恢复。这是Java中的一个重要概念,因为它使得对象能够在不同的网络环境或者持久化存储中传递。Java序列化不仅...
标题中的“jackson库实现定制化的java序列化反序列化操作”指的是利用Jackson库的能力,通过自定义规则来控制对象的序列化和反序列化过程。这通常涉及到创建自定义的`JsonSerializer`和`JsonDeserializer`,或者使用...
Java序列化技术是一种强大的功能,它能够将对象的状态转化为字节流,以便在网络上传输或持久化到文件系统中。这一机制是Java远程方法调用(RMI)、企业JavaBeans(EJB)、Java Native Interface(JNNI)等关键技术的基础。...
Java序列化机制的优点在于它提供了一种标准的方式来处理对象的持久化和在网络间的传输。然而,序列化也存在安全风险,比如序列化可能导致远程代码执行攻击。因此,对于敏感信息或复杂对象结构,应谨慎使用序列化,并...
WebLogic的反序列化漏洞是由于其内部组件在处理反序列化输入时的不安全实现导致的。攻击者可以发送特制的序列化对象到易受攻击的服务端点,如果成功触发漏洞,可能导致远程代码执行(RCE),进而控制系统。 `readMe...
Java序列化是Java平台提供的一种持久化机制,它允许我们将Java对象转换成字节流,以便于存储或者在网络中传输。这一过程被称为序列化,而将字节流还原成原来的对象则称为反序列化。在Java中,实现序列化主要通过实现...
虽然Java标准库的`Tree`类已经实现了序列化,但查看源码可以帮助理解其内部机制。例如,`TreeMap`在反序列化时会重新构建红黑树的平衡,以保持原有的排序特性。 ### 工具支持 一些工具,如Apache Commons Lang的`...
在 Java 中,实现序列化的接口是 Serializable,该接口没有任何方法,但是它规定了一个类必须实现的格式,以便能够被序列化。 在我们的示例中,我们定义了一个 Employee 类,该类实现了 Serializable 接口,并且...
1. **魔数**:标识文件为Java序列化文件,固定为`ACED`。 2. **序列化协议版本号**:目前常用的版本号为`0005`。 3. **对象类型标志**:一个字节用于表示对象类型。例如`0x73`表示普通Java对象。 4. **类或引用标志*...
在Java中,如果一个类实现了Serializable接口,那么该类的对象就可以被序列化。序列化的目的是为了保存对象的状态以便后续使用或在网络上传输。 Hessian,由Caucho Technology开发,是一种二进制的序列化格式。相比...
Java序列化是一个重要的Java特性,它允许我们将Java对象转换为字节流,便于存储或在网络上传输。这一过程称为序列化,反向操作被称为反序列化。Java序列化的主要作用包括: 1. **持久化对象**:通过序列化,我们...
1. **默认序列化**:如果一个类只实现了`Serializable`接口,那么Java会自动处理序列化,将类中所有非`transient`和非`static`的字段转换为字节流。 2. **自定义序列化**:如果类实现了`Serializable`接口,并且...
Java序列化是指将一个Java对象的状态转换为字节流,以便于存储或通过网络进行传输的过程。当接收端接收到这些字节流后,可以将其重新转换为原始的对象状态。这个过程对于分布式系统的数据交换非常重要。 #### 二、...
Java对象的序列化是Java平台提供的一种将对象转化为字节序列的能力,以便于存储或在网络中传输。这个特性使得任何实现了Serializable接口的Java对象都可以被转换成一系列的字节,之后这些字节可以完全恢复,重新生成...
`RedisTemplate`内部涉及到了序列化,包括key和value的序列化。 博客中可能提到了以下两个关键类:`RedisTemplateDelegate`和`RedisStringTemplateDelegate`。这些类可能是用户自定义的扩展,用于定制`...