Java 反序列化
Java的反序列化,是序列化的逆向过程。基础类型的序列化比较简单,这里主要讲解对象的反序列化,从文件或网络中把字节序列读取出来,并解析字节序列中的对象类描述信息、以及对象成员的值信息,并生成一个新对象的过程。
先看下对象反序列化的参数:字节序列(字节数组)
这里的字节序列一般是通过上一篇《java序列化的内部实现(一)》中序列化过程产生的;
当然如果有必要的话也可以手工拼装(这种场景比较少);
更多是对序列化参数的字节序列进行篡改,操控反序列端的对象生成(如:被攻击者攻击)。
一个简单的例子
Java的反序列化时通过实例化ObjectInputStream,并调用其readObject方法实现的。为了方便理解,这里的字节序列采用字节数组,方便后续手动篡改。
package com.sky.serial; import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.InputStream; import java.io.ObjectInputStream; /** * Created by gantianxing on 2017/5/29. */ public class UserDemo { private static final byte[] serialByteArray = new byte[]{ (byte)0xAC,(byte)0xED,0x00,0x05,0x73,0x72,0x00,0x13,0x63,0x6F,0x6D,0x2E,0x73,0x6B,0x79,0x2E,0x73,0x65,0x72,0x69,0x61,0x6C,0x2E,0x55,0x73,0x65,0x72,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x00,0x03,0x49,0x00,0x03,0x73,0x65,0x78,0x4C,0x00,0x04,0x6E,0x61,0x6D,0x65,0x74,0x00,0x12,0x4C,0x6A,0x61,0x76,0x61,0x2F,0x6C,0x61,0x6E,0x67,0x2F,0x53,0x74,0x72,0x69,0x6E,0x67,0x3B,0x4C,0x00,0x08,0x70,0x68,0x6F,0x6E,0x65,0x4E,0x75,0x6D,0x71,0x00,0x7E,0x00,0x01,0x78,0x70,0x00,0x00,0x00,0x01,0x74,0x00,0x09,0x7A,0x68,0x61,0x6E,0x67,0x20,0x73,0x61,0x6E,0x74,0x00,0x0B,0x31,0x33,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38 }; public static void main(String[] args) throws Exception{ //拼装字节序列 InputStream is = new ByteArrayInputStream(serialByteArray); ObjectInputStream in = new ObjectInputStream(is); //反序列化 Object newObj = in.readObject(); System.out.println(newObj.getClass()); //判断是否为User类型 if(newObj instanceof User){ User newUser = (User)newObj; System.out.println("user name:" + newUser.getName()); System.out.println(newUser); } } }
执行main方法,执行结果为:
class com.sky.serial.User user name:zhang san user info: name=zhang san,sex=1,phoneNum=13888888888
第一行打印信息 可以看到本次反序列化生成的对象,其实就是上一篇分享示例中的User类。
第二 三行打印信息,跟上一篇分享示例中打印内容相同,细心的朋友其实已经发现这里的字节数组,就是上一篇分享示例中序列化到文件user.txt中的内容:
篡改字节序列
这里我们模拟把“张三”的性别sex该为女性0,即在字节数组中把0x00,0x,00,0x00,0x01改为0x00,0x,00,0x00,0x00,代码如下:
package com.sky.serial; import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.InputStream; import java.io.ObjectInputStream; /** * Created by gantianxing on 2017/5/29. */ public class UserDemo { private static final byte[] serialByteArray = new byte[]{ (byte)0xAC,(byte)0xED,0x00,0x05,0x73,0x72,0x00,0x13,0x63,0x6F,0x6D,0x2E,0x73,0x6B,0x79,0x2E ,0x73,0x65,0x72,0x69,0x61,0x6C,0x2E,0x55,0x73,0x65,0x72,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x01,0x02,0x00,0x03,0x49,0x00,0x03,0x73,0x65,0x78,0x4C,0x00,0x04,0x6E ,0x61,0x6D,0x65,0x74,0x00,0x12,0x4C,0x6A,0x61,0x76,0x61,0x2F,0x6C,0x61,0x6E,0x67 ,0x2F,0x53,0x74,0x72,0x69,0x6E,0x67,0x3B,0x4C,0x00,0x08,0x70,0x68,0x6F,0x6E,0x65 ,0x4E,0x75,0x6D,0x71,0x00,0x7E,0x00,0x01,0x78,0x70,0x00,0x00,0x00,0x00,0x74,0x00 ,0x09,0x7A,0x68,0x61,0x6E,0x67,0x20,0x73,0x61,0x6E,0x74,0x00,0x0B,0x31,0x33,0x38 ,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38 }; public static void main(String[] args) throws Exception{ //拼装字节序列 InputStream is = new ByteArrayInputStream(serialByteArray); ObjectInputStream in = new ObjectInputStream(is); //反序列化 Object newObj = in.readObject(); System.out.println(newObj.getClass()); //判断是否为User类型 if(newObj instanceof User){ User newUser = (User)newObj; System.out.println("user name:" + newUser.getName()); System.out.println(newUser); } } }
执行main方法,打印信息如下:
class com.sky.serial.User user name:zhang san user info: name=zhang san,sex=0,phoneNum=13888888888
从第三行打印信息看出,张三的性别已经变为“女性”了。
我们从User类的定义中看到 sex是final的(private final int sex;),即在第一次被构造方法初始化后,就不允许被改变。但反序列化会打破这个逻辑,如何防范这种篡改呢,我将在下一遍《java序列化用法以及理论》中进行讲解。本篇重点关注,java反序列化的内容实现。
Java反序列化内部实现
1、Java的反序列化时通过实例化ObjectInputStream,并调用其readObject方法实现的。我们以ObjectInputStream的构造方法为起点进行源码解析:
public ObjectInputStream(InputStream in) throws IOException { verifySubclass(); //检查是否是ObjectInputStream的子类,这里不是 bin = new BlockDataInputStream(in);//初始化输入流对象bin,后续通过bin.readxxx()方法读取字节信息 handles = new HandleTable(10);//已读取的 vlist = new ValidationList(); enableOverride = false; readStreamHeader();//读取魔法数+版本号(AC ED),并进行检查 bin.setBlockDataMode(true); }
//检查 魔法数+版本号方法 protected void readStreamHeader() throws IOException, StreamCorruptedException { short s0 = bin.readShort(); //读取STREAM_MAGIC short s1 = bin.readShort();//读取 STREAM_VERSION if (s0 != STREAM_MAGIC || s1 != STREAM_VERSION) { //如果不匹配AC ED,直接抛出异常,反序列化失败 throw new StreamCorruptedException( String.format("invalid stream header: %04X%04X", s0, s1)); } }
构造方法,主要初始化工作包括:初始化输入流对象bin,后续通过bin.readxxx()方法读取字节信息;初始化handles、vlist;读取魔法数+版本号(AC ED),并进行检查,如果检查失败直接抛出异常,反序列化失败。
2、调用readObjet方法(Object newObj = in.readObject();)开始反序列化:
public final Object readObject() throws IOException, ClassNotFoundException { if (enableOverride) { //判断是否开启替换,这里没有 return readObjectOverride(); } // if nested read, passHandle contains handle of enclosing object int outerHandle = passHandle; try { Object obj = readObject0(false); //读取逻辑 handles.markDependency(outerHandle, passHandle); ClassNotFoundException ex = handles.lookupException(passHandle); if (ex != null) { throw ex; } if (depth == 0) { vlist.doCallbacks(); } return obj; } finally { passHandle = outerHandle; if (closed && depth == 0) { clear(); } } }
readObject0方法的主要工作流程:读取TC标记,判断TC标记类型,根据不同的类型,进行不同逻辑处理。
private Object readObject0(boolean unshared) throws IOException { boolean oldMode = bin.getBlockDataMode(); if (oldMode) { int remain = bin.currentBlockRemaining(); if (remain > 0) { throw new OptionalDataException(remain); } else if (defaultDataEnd) { /* * Fix for 4360508: stream is currently at the end of a field * value block written via default serialization; since there * is no terminating TC_ENDBLOCKDATA tag, simulate * end-of-custom-data behavior explicitly. */ throw new OptionalDataException(true); } bin.setBlockDataMode(false); } byte tc; while ((tc = bin.peekByte()) == TC_RESET) { //peek读取下一个字节,这个字节是个TC标记,并判断是不是TC_RESET bin.readByte(); handleReset(); } depth++; //该方法会被递归调用,记录入栈次数 try { switch (tc) { //判断TC标记的类型 case TC_NULL:空对象标记 return readNull();//读取空对象 case TC_REFERENCE://引用已存在的TC标记 return readHandle(unshared); //读取引用对象 case TC_CLASS: //class类型TC标记 return readClass(unshared); case TC_CLASSDESC: //普通类描述TC标记 case TC_PROXYCLASSDESC: //代理类描述TC标记 return readClassDesc(unshared); // case TC_STRING: //string TC标记 case TC_LONGSTRING: //长string TC标记 return checkResolve(readString(unshared)); 本例中第二次进入readObject0方法,为对象类型成员变量name字段赋值。 case TC_ARRAY: //数组TC标记 return checkResolve(readArray(unshared)); case TC_ENUM://枚举TC标记 return checkResolve(readEnum(unshared)); case TC_OBJECT://新对象TC标记73,本实例第一次进入readObject0方法,会匹配到该处 return checkResolve(readOrdinaryObject(unshared)); case TC_EXCEPTION://异常TC标记 IOException ex = readFatalException(); throw new WriteAbortedException("writing aborted", ex); case TC_BLOCKDATA: case TC_BLOCKDATALONG: if (oldMode) { bin.setBlockDataMode(true); bin.peek(); // force header read throw new OptionalDataException( bin.currentBlockRemaining()); } else { throw new StreamCorruptedException( "unexpected block data"); } case TC_ENDBLOCKDATA://类描述结束TC标记 if (oldMode) { throw new OptionalDataException(true); } else { throw new StreamCorruptedException( "unexpected end of block data"); } default://没有找到匹配的 TC类型 throw new StreamCorruptedException( String.format("invalid type code: %02X", tc)); } } finally { depth--; bin.setBlockDataMode(oldMode); } }
阅读这个方法的关键是搞清楚,bin.peekByte()和bin.readByte()区别,前者字节数组的index指针不会往后移,下次读取还是从原来的位置开始;后者数组的index会往后移一个字节,下次读取会从这个新位置开始。
本实例中第一次进入readObject0方法会匹配到 case TC_OBJECT: (新对象标记73),第二次进入readObject0方法会匹配到case TC_STRING:为name字段赋值,第三进入是为phoneNum字段赋值。
3、先看下第一次进入readObject0时,会先调用readOrdinaryObject方法,然后调用checkResolve方法(checkResolve方法放到最后解释):
private Object readOrdinaryObject(boolean unshared) throws IOException { if (bin.readByte() != TC_OBJECT) {//读取出TC标记,并验证是否是新对象标记 throw new InternalError(); } ObjectStreamClass desc = readClassDesc(false);//根据对象的格式,下一步是读取类描述信息 desc.checkDeserialize(); Class<?> cl = desc.forClass(); //读取Class:com.sky.serial.User if (cl == String.class || cl == Class.class || cl == ObjectStreamClass.class) { throw new InvalidClassException("invalid class descriptor"); } Object obj; try { obj = desc.isInstantiable() ? desc.newInstance() : null; //通过类描述信息,初始化对象obj,注意不是通过调用构造方法初始化 } catch (Exception ex) { throw (IOException) new InvalidClassException( desc.forClass().getName(), "unable to create instance").initCause(ex); } passHandle = handles.assign(unshared ? unsharedMarker : obj);//把对象obj放入handles map中,以便后续直接引用。 ClassNotFoundException resolveEx = desc.getResolveException(); if (resolveEx != null) { handles.markException(passHandle, resolveEx); } if (desc.isExternalizable()) {//判断是否实现Externalizable接口 readExternalData((Externalizable) obj, desc); //最终调用对象的实现的readExternal方法,反序列化,为obj成员变量赋值 } else { readSerialData(obj, desc);//调用默认方法为obj成员变量赋值 } handles.finish(passHandle); if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) //判断对象是否实现readResolve方法 { Object rep = desc.invokeReadResolve(obj);//反射调用对象的readResolve方法 if (unshared && rep.getClass().isArray()) { rep = cloneArray(rep); } if (rep != obj) {//如果对象readResolve返回的对象与 默认序列化对象不等,返回readResolve方法返回的对象 handles.setObject(passHandle, obj = rep); } } return obj; }
该方法会先调用readClassDesc获取对象的类描述信息,然后调用newInstance方法实例化对象obj,注意此处会生成一个空Obj(User类型)对象(成员变量值为空或者默认初始值),不是通过调用构造方法,而是JVM底层实现。然后调用readSerialData方法为空对象的成员变量赋值。
newInstance方法
public T newInstance(Object ... initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class<?> caller = Reflection.getCallerClass(); checkAccess(caller, clazz, null, modifiers); } } if ((clazz.getModifiers() & Modifier.ENUM) != 0) throw new IllegalArgumentException("Cannot reflectively create enum objects"); ConstructorAccessor ca = constructorAccessor; // read volatile if (ca == null) { ca = acquireConstructorAccessor(); } @SuppressWarnings("unchecked") T inst = (T) ca.newInstance(initargs);//此处会生成一个User空对象,不是通过调用构造方法,而是JVM底层实现。 return inst; }
4、调用readClassDesc方法,根据本例中的TC信息会调用readNonProxyDesc方法获取对象的类描述信息:
private ObjectStreamClass readClassDesc(boolean unshared) throws IOException { byte tc = bin.peekByte(); //peek读取TC标记 switch (tc) { case TC_NULL: return (ObjectStreamClass) readNull(); case TC_REFERENCE: return (ObjectStreamClass) readHandle(unshared); case TC_PROXYCLASSDESC: return readProxyDesc(unshared); case TC_CLASSDESC: return readNonProxyDesc(unshared); //本实例中会首先匹配到这里,开始读取类描述信息 default: throw new StreamCorruptedException( String.format("invalid type code: %02X", tc)); } } private ObjectStreamClass readNonProxyDesc(boolean unshared) throws IOException { if (bin.readByte() != TC_CLASSDESC) {//读取TC标记,并验证是否是类描述TC标记 throw new InternalError(); } ObjectStreamClass desc = new ObjectStreamClass(); int descHandle = handles.assign(unshared ? unsharedMarker : desc);//放入Handles map中,以便,后续遇到相同类型直接引用。 passHandle = NULL_HANDLE; ObjectStreamClass readDesc = null; try { readDesc = readClassDescriptor(); //开始读取类描述 } catch (ClassNotFoundException ex) { throw (IOException) new InvalidClassException( "failed to read class descriptor").initCause(ex); } Class<?> cl = null; ClassNotFoundException resolveEx = null; bin.setBlockDataMode(true); final boolean checksRequired = isCustomSubclass();//检查是否是ObjectInputStream的自定义子类,通过classLoader判断,自定义的classLoader为 App try { if ((cl = resolveClass(readDesc)) == null) {//通过反射,反射出对象类型 resolveEx = new ClassNotFoundException("null class"); } else if (checksRequired) { ReflectUtil.checkPackageAccess(cl); } } catch (ClassNotFoundException ex) { resolveEx = ex; } skipCustomData(); //跳过类描述结束符 desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false)); handles.finish(descHandle); passHandle = descHandle; return desc;//返回类描述信息 } private void skipCustomData() throws IOException { int oldHandle = passHandle; for (;;) { if (bin.getBlockDataMode()) { bin.skipBlockData(); bin.setBlockDataMode(false); } switch (bin.peekByte()) { case TC_BLOCKDATA: case TC_BLOCKDATALONG: bin.setBlockDataMode(true); break; case TC_ENDBLOCKDATA: //本实例会匹配到这里:78, 类描述读取结束 bin.readByte(); passHandle = oldHandle; return; default: readObject0(false); break; } } }
5、真正读取类描述信息的方法是readClassDescriptor,所有的类信息、成员描述信息都会在本方法中完成,最终返回一个类描述信息对象ObjectStreamClass :
protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException { ObjectStreamClass desc = new ObjectStreamClass(); desc.readNonProxy(this); return desc; } 调用ObjectStreamClass类的readNonProxy方法进行类描述信息读取: void readNonProxy(ObjectInputStream in) throws IOException, ClassNotFoundException { name = in.readUTF(); //读取类名,这里为“com.sky.serial.User” suid = Long.valueOf(in.readLong());//读取SUID,这里为1L isProxy = false; byte flags = in.readByte();//读取下一个字节,这里为02,表示该类实现了serializable接口 hasWriteObjectData = ((flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0); hasBlockExternalData = ((flags & ObjectStreamConstants.SC_BLOCK_DATA) != 0); externalizable = ((flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0); boolean sflag = ((flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0); if (externalizable && sflag) { throw new InvalidClassException( name, "serializable and externalizable flags conflict"); } serializable = externalizable || sflag; // 这里serializable赋值为true isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);// 这里isEnum 本次赋值为false,User类不是范型 if (isEnum && suid.longValue() != 0L) {//说明范型不能有非0的suid throw new InvalidClassException(name, "enum descriptor has non-zero serialVersionUID: " + suid); } int numFields = in.readShort(); //读取成员变量个数 if (isEnum && numFields != 0) { throw new InvalidClassException(name, "enum descriptor has non-zero field count: " + numFields); } fields = (numFields > 0) ? new ObjectStreamField[numFields] : NO_FIELDS; //实例化成员变量数组 for (int i = 0; i < numFields; i++) {//解析成员变量描述信息并放入成员变量数组 char tcode = (char) in.readByte(); //读取成员变量type,这里的三个成员类型分别为:I(int),L(String),L(String) String fname = in.readUTF();//读取成员变量名称这里分别为:sex、name、phoneNum String signature = ((tcode == 'L') || (tcode == '[')) ? in.readTypeString() : new String(new char[] { tcode });//如果成员变量是对象(L)或者数组([), 还需通过in.readTypeString()获取真实类型 try { fields[i] = new ObjectStreamField(fname, signature, false);//调用ObjectStreamField构造方法生成成员变量描述对象 } catch (RuntimeException e) { throw (IOException) new InvalidClassException(name, "invalid descriptor for field " + fname).initCause(e); } } computeFieldOffsets();//对primDataSize、numObjFields赋值 } ObjectStreamField构造方法: 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"); } }
该类的所有成员描述信息解析结束。回到readNonProxyDesc方法,继续判断是否有父类,如果有继续解析父类描述信息,否则开始解析成员变量的值信息,为每个成员赋值。
6、在readOrdinaryObject方法中根据类描述信息初始化对象obj成功后,开始为成员变量赋值,这里调用的readSerialData方法:
private void readSerialData(Object obj, ObjectStreamClass desc) throws IOException { ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();//这步获取对象父子关系拓扑结构,这里User没有直接父类(Object除外),只需要对User对象进行赋值。 for (int i = 0; i < slots.length; i++) {//遍历拓扑结构,这里slots只有一个User对象。 ObjectStreamClass slotDesc = slots[i].desc;//User对象的类描述信息 if (slots[i].hasData) { if (obj == null || handles.lookupException(passHandle) != null) { defaultReadFields(null, slotDesc); // skip field values } else if (slotDesc.hasReadObjectMethod()) {//判断是否有readObject方法,如果有通过反射调用对象的readObject方法为成员变量赋值 SerialCallbackContext oldContext = curContext; if (oldContext != null) oldContext.check(); try { curContext = new SerialCallbackContext(obj, slotDesc); bin.setBlockDataMode(true); slotDesc.invokeReadObject(obj, this); } catch (ClassNotFoundException ex) { /* * In most cases, the handle table has already * propagated a CNFException to passHandle at this * point; this mark call is included to address cases * where the custom readObject method has cons'ed and * thrown a new CNFException of its own. */ handles.markException(passHandle, ex); } finally { curContext.setUsed(); if (oldContext!= null) oldContext.check(); curContext = oldContext; } /* * defaultDataEnd may have been set indirectly by custom * readObject() method when calling defaultReadObject() or * readFields(); clear it to restore normal read behavior. */ defaultDataEnd = false; } else {//使用默认方法对成员变量赋值 defaultReadFields(obj, slotDesc); } if (slotDesc.hasWriteObjectData()) { skipCustomData(); } else { bin.setBlockDataMode(false); } } else { if (obj != null && slotDesc.hasReadObjectNoDataMethod() && handles.lookupException(passHandle) == null) { slotDesc.invokeReadObjectNoData(obj);//调用对象的readObjectNoData方法进行赋值 } } } }
为对象成员变量赋值的默认方法defaultReadFields:
private void defaultReadFields(Object obj, ObjectStreamClass desc) throws IOException { Class<?> cl = desc.forClass(); if (cl != null && obj != null && !cl.isInstance(obj)) { throw new ClassCastException(); } int primDataSize = desc.getPrimDataSize(); if (primVals == null || primVals.length < primDataSize) { primVals = new byte[primDataSize]; } bin.readFully(primVals, 0, primDataSize, false); if (obj != null) { desc.setPrimFieldValues(obj, primVals);//为prim类型赋值(如,int char等基础类型) } int objHandle = passHandle; ObjectStreamField[] fields = desc.getFields(false); Object[] objVals = new Object[desc.getNumObjFields()]; int numPrimFields = fields.length - objVals.length; for (int i = 0; i < objVals.length; i++) {//获取对象类型成员变量值数组(如,String、数组、自定义对象等) ObjectStreamField f = fields[numPrimFields + i]; objVals[i] = readObject0(f.isUnshared()); //递归调用readObject0为对象赋值,这里为String,见readObject0方法注释 if (f.getField() != null) { handles.markDependency(objHandle, passHandle); } } if (obj != null) { desc.setObjFieldValues(obj, objVals); //为对象成员变量赋值 } passHandle = objHandle; }
7、为基础类型赋值方法setPrimFieldValues,这里只有一个基础类型int型的sex成员变量:
void setPrimFieldValues(Object obj, byte[] buf) { if (obj == null) { throw new NullPointerException(); } for (int i = 0; i < numPrimFields; i++) { long key = writeKeys[i]; if (key == Unsafe.INVALID_FIELD_OFFSET) { continue; // discard value } int off = offsets[i]; switch (typeCodes[i]) { case 'Z': unsafe.putBoolean(obj, key, Bits.getBoolean(buf, off)); break; case 'B': unsafe.putByte(obj, key, buf[off]); break; case 'C': unsafe.putChar(obj, key, Bits.getChar(buf, off)); break; case 'S': unsafe.putShort(obj, key, Bits.getShort(buf, off)); break; case 'I': unsafe.putInt(obj, key, Bits.getInt(buf, off));//从字节数组buf中读取数据,为对象的sex成员变量赋值,并移动字节数组下标,类似in.read()方法。注意不是调用setxxx方法赋值,是有jvm底层实现,看不到源码。 break; case 'F': unsafe.putFloat(obj, key, Bits.getFloat(buf, off)); break; case 'J': unsafe.putLong(obj, key, Bits.getLong(buf, off)); break; case 'D': unsafe.putDouble(obj, key, Bits.getDouble(buf, off)); break; default: throw new InternalError(); } } }
8、为对象类型成员变量赋值:递归调用readObject0,匹配到TC_STRING, 调用checkResolve(readString(unshared));为name、phoneNum字段赋值。
private String readString(boolean unshared) throws IOException { String str; byte tc = bin.readByte();//读取TC标记 switch (tc) { case TC_STRING: str = bin.readUTF(); //匹配到这里,读取String内容,返回 break; case TC_LONGSTRING: str = bin.readLongUTF(); break; default: throw new StreamCorruptedException( String.format("invalid type code: %02X", tc)); } passHandle = handles.assign(unshared ? unsharedMarker : str); //放入handles map, handles.finish(passHandle); return str; } 为对象类型的成员变量赋值,注意不是调用的setxx方法赋值,而是jvm底层实现赋值。 void setObjFieldValues(Object obj, Object[] vals) { if (obj == null) { throw new NullPointerException(); } for (int i = numPrimFields; i < fields.length; i++) { long key = writeKeys[i]; if (key == Unsafe.INVALID_FIELD_OFFSET) { continue; // discard value } switch (typeCodes[i]) { case 'L': case '[': Object val = vals[offsets[i]]; if (val != null && !types[i - numPrimFields].isInstance(val)) { Field f = fields[i].getField(); throw new ClassCastException( "cannot assign instance of " + val.getClass().getName() + " to field " + f.getDeclaringClass().getName() + "." + f.getName() + " of type " + f.getType().getName() + " in instance of " + obj.getClass().getName()); } unsafe.putObject(obj, key, val);//调用jvm底层实现为对象类型成员变量赋值 break; default: throw new InternalError(); } } }
9、checkResolve方法:最后看下checkResolve方法,在使用自定义并继承ObjectInputStream类的对象进行反序列化时,可以开启enableResolve=true,并重写resolveObject方法,并用resolveObject方法返回的对象替换默认序列化对象返回:
private Object checkResolve(Object obj) throws IOException { if (!enableResolve || handles.lookupException(passHandle) != null) { return obj;//本示例中 到这步返回 } Object rep = resolveObject(obj);//调用子类实现的resolveObject方法 if (rep != obj) {//如果与序列化的对象不相等,则进行替换 handles.setObject(passHandle, rep); } return rep; }
到这里整个反序列化流程结束。
简单总结下,对象的反序列化过程跟序列化过程类似,分两步完成:
第一步读取类描述信息,并根据这些信息通过jvm底层实例化一个指定类型的空对象;
第二步读取成员变量值信息对这个空对象的成员变量进行赋值,成员变量分为基础类型、对象类型,基础类型直接赋值,对象类型继续递归调用readObject0方法进行赋值,这里的赋值并不是调用的setter方法,而是调用jvm底层实现的方法进行赋值。
可见对象的反序列化过程,可以不调用对象的构造方法,以及setter方法就可以完成新对象的创建,并对成员变量进行赋值。
其他序列化和反序列化方式
本篇对象反序列化和前一篇对象序列化的示例中,都是采用默认实现Serializable接口的方式,调用默认的序列化和反序列化方法进行分析。源码中我们可以看到还有很多其他方法不在实例中体现,比如:自定义序列化方法readObject和writeObject;实现Externalizable序列化方式的readExternal和writeExternal;readResolve替换方法;readObjectNoDataMethod方法;子类扩展的替换方法resolveObject和replaceObject方法;以及Enum枚举类型序列化。这些方法源码处理逻辑在以上注释中都有体现,下一篇主要讲解这些方法的区别和适用场景。
对象的序列化和发序列化讲解完毕,基础类型的序列化和反序列化比较简单,直接调用out.writexxx完成序列化,调用in.readxxx完成反序列化(比如out.writeInt()、in.readInt())。其实对象的序列化最终也是转化为对基础类型的序列化,但是为了标记他们在对象里的关系,添加了很多TC标记而已。
相关推荐
Java反序列化是一种将已序列化的对象状态转换回对象的过程,通常用于持久化数据或在网络间传输对象。在Java中,这一过程涉及到`java.io.ObjectInputStream`类,它能够读取由`java.io.ObjectOutputStream`写入的字节...
标题中的“jackson库实现定制化的java序列化反序列化操作”指的是利用Jackson库的能力,通过自定义规则来控制对象的序列化和反序列化过程。这通常涉及到创建自定义的`JsonSerializer`和`JsonDeserializer`,或者使用...
这个过程包括两个主要操作:序列化(将对象转换为字节流)和反序列化(将字节流恢复为对象)。在Java中,如果一个类需要支持序列化,它应该实现`java.io.Serializable`接口。 在Java序列化过程中,`...
Hessian反序列化过程接收二进制流,并根据其定义解析成Java对象。这个过程对于跨语言通信特别有用,因为它能将一个语言的对象转换为另一种语言可以理解的格式。 在实际应用中,Hessian常用于构建轻量级的Web服务,...
虽然实现`Serializable`接口能够方便地进行序列化和反序列化,但需要注意的是,序列化会暴露对象的内部状态,可能引发安全问题。此外,如果一个类包含不希望被序列化的字段,可以通过添加`transient`关键字来标记...
Java中的序列化与反序列化是Java编程语言中用于处理对象状态持久化和网络传输的重要技术。序列化是指将一个Java对象转换为字节流的过程,这样就可以将对象的状态保存到磁盘上或者通过网络传输。反序列化则是将字节流...
- Protocol Buffer:PB在序列化和反序列化时通常比Java序列化更快,因为它的数据格式更加紧凑,且解析算法优化。 - Java序列化:Java序列化虽然方便,但生成的数据量较大,且序列化和反序列化速度相对较慢。 2. ...
在 Java 中,实现反序列化的接口是 Serializable,该接口没有任何方法,但是它规定了一个类必须实现的格式,以便能够被反序列化。 在我们的示例中,我们使用了 ByteArrayInputStream 将字节流读取出来,并将其恢复...
【技术分享】二进制角度构造Java反序列化Payload的主题主要涉及Java安全领域的漏洞分析与利用,特别是针对Java反序列化漏洞的构造方法。反序列化漏洞常常出现在企业级应用中,因为Java对象序列化机制在传递和存储...
本文将深入探讨Java中的对象序列化与反序列化的概念、原理、实现方法以及相关的注意事项。 **一、对象序列化的概念和作用** 对象序列化是将一个Java对象转换成字节流的过程,这个字节流可以存储在磁盘上,也可以在...
2. **版本控制**:使用`serialVersionUID`字段进行版本控制,以防在不同版本的类之间进行序列化和反序列化时出现问题。 3. **非序列化成员**:有时我们不希望某些成员被序列化,可以使用`transient`关键字标记这些...
Java序列化不仅适用于Java对象,还可以通过反序列化过程将这些字节流重新构建回原始对象。 Java中实现序列化的接口是`java.io.Serializable`,任何类如果实现了这个接口,那么它的实例就可以被序列化。这个接口没有...
反序列化时,Java会尝试恢复对象的状态,包括它的字段值。 然而,需要注意的是,序列化可能会带来安全风险,因为恶意用户可能利用序列化的漏洞进行攻击。因此,除非必要,应避免序列化敏感数据。同时,如果你的类...
总结来说,Java的序列化和反序列化是通过`Serializable`接口和相关机制实现的,它对于数据持久化、网络通信和跨进程通信至关重要。通过自定义序列化和反序列化方法,我们可以更好地控制对象的存储和恢复过程,确保...
- **序列化工具类**:Java提供了`java.io.ObjectOutputStream`和`java.io.ObjectInputStream`两个类来进行对象的序列化和反序列化操作。 ##### ObjectOutputStream - **构造函数**:需要传入一个`OutputStream`...
【JAVA反序列化原理】 Java反序列化是将已序列化的字节流恢复为原始Java对象的过程。这一过程涉及到`java.io.ObjectInputStream`类的`readObject()`方法,该方法能够从输入流中读取字节序列并将其转化为对象。为了...
- **实现 `writeObject()` 和 `readObject()` 方法**:可以通过实现这两个方法来自定义序列化和反序列化的逻辑。 #### 3.2 类加载机制 序列化过程中,涉及到的对象可能需要通过类加载器动态加载。Java的类加载机制...
5. **安全**:序列化漏洞可能导致安全问题,例如反序列化攻击。 因此,选择合适的序列化工具至关重要。对于Java对象,推荐使用高效的JSON序列化库,如Jackson或Gson,它们支持配置以优化性能和数据大小。同时,确保...
总结来说,Java的序列化和反序列化机制是实现对象在网络间传输的重要工具,而通过Socket进行对象传输时需要确保对象是可序列化的,并处理好版本控制和安全性问题。在实际开发中,我们需要根据具体需求选择合适的序列...