java对序列化serialVersionUID的处理分析

继上次分析了java的序列化过程之后,对于serialVersionUID的处理还不是很清晰,今天再看下代码,对serialVersionUID的处理进行了了解,上次的序列化过程分析可以参考另外一篇文章:http://zhwj184.iteye.com/blog/1550699

 

ObjectOutputStream.java调用writeObject的时候会调用到下面的代码:

 

这是调用ObjectStreamClass.java的writeNonProxy方法,写入非代理类的元数据信息

在写入类的元数据的时候会把serialVersionUID写入:


  /**
     * Writes non-proxy class descriptor information to given output stream.
     */
    void writeNonProxy(ObjectOutputStream out) throws IOException {
	out.writeUTF(name);
	out.writeLong(getSerialVersionUID());

	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);
	
	out.writeShort(fields.length);
	for (int i = 0; i < fields.length; i++) {
	    ObjectStreamField f = fields[i];
	    out.writeByte(f.getTypeCode());
	    out.writeUTF(f.getName());
	    if (!f.isPrimitive()) {
		out.writeTypeString(f.getTypeString());
	    }
	}
    }

在读取的时候会做几个部分的校验:

在ObjectInputStream.java的readObject方法时调用ObjectStreamClass.java的readNonProxy方法,

 

这里会读取suid = Long.valueOf(in.readLong());就是读取serialVersionUID,然后先做第一步校验if (isEnum && suid.longValue() != 0L) 如果是枚举类则serialVersionUID为0.


void readNonProxy(ObjectInputStream in) 
	throws IOException, ClassNotFoundException
    {
	name = in.readUTF();
	suid = Long.valueOf(in.readLong());
	isProxy = false;

	byte flags = in.readByte();
	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;
	isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);
	if (isEnum && suid.longValue() != 0L) {
	    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();
	    String fname = in.readUTF();
	    String signature = ((tcode == 'L') || (tcode == '[')) ?
		in.readTypeString() : new String(new char[] { tcode });
	    try {
		fields[i] = new ObjectStreamField(fname, signature, false);
	    } catch (RuntimeException e) {
		throw (IOException) new InvalidClassException(name, 
		    "invalid descriptor for field " + fname).initCause(e);
	    }
	}
	computeFieldOffsets();
    }

然后下面还会对serialVersionUID进行下一步的验证:同样是在ObjectStreamClass.java的initNonProxy方法,suid = Long.valueOf(model.getSerialVersionUID());去除序列化后的model对象的serialVersionUID,if (serializable == localDesc.serializable &&!cl.isArray() &&suid.longValue() != localDesc.getSerialVersionUID()) 然后判断是否跟本地的class对象是否都是继承serializable 接口,并且cl不是数组,且serialVersionUID要跟当前序列化的class对象的serialVersionUID一致。


/**
     * Initializes class descriptor representing a non-proxy class.
     */
    void initNonProxy(ObjectStreamClass model, 
		      Class cl, 
		      ClassNotFoundException resolveEx,
		      ObjectStreamClass superDesc)
	throws InvalidClassException
    {
	this.cl = cl;
	this.resolveEx = resolveEx;
	this.superDesc = superDesc;
	name = model.name;
	suid = Long.valueOf(model.getSerialVersionUID());
	isProxy = false;
	isEnum = model.isEnum;
	serializable = model.serializable;
	externalizable = model.externalizable;
	hasBlockExternalData = model.hasBlockExternalData;
	hasWriteObjectData = model.hasWriteObjectData;
	fields = model.fields;
	primDataSize = model.primDataSize;
	numObjFields = model.numObjFields;
	
	if (cl != null) {
	    localDesc = lookup(cl, true);
	    if (localDesc.isProxy) {
		throw new InvalidClassException(
		    "cannot bind non-proxy descriptor to a proxy class");
	    }
	    if (isEnum != localDesc.isEnum) {
		throw new InvalidClassException(isEnum ?
		    "cannot bind enum descriptor to a non-enum class" :
		    "cannot bind non-enum descriptor to an enum class");
	    }
	    
	    if (serializable == localDesc.serializable &&
		!cl.isArray() &&
		suid.longValue() != localDesc.getSerialVersionUID())
	    {
		throw new InvalidClassException(localDesc.name, 
		    "local class incompatible: " +
		    "stream classdesc serialVersionUID = " + suid +
		    ", local class serialVersionUID = " +
		    localDesc.getSerialVersionUID());
	    }
		
	    if (!classNamesEqual(name, localDesc.name)) {
		throw new InvalidClassException(localDesc.name,
		    "local class name incompatible with stream class " +
		    "name \"" + name + "\"");
	    }
	    
	    if (!isEnum) {
		if ((serializable == localDesc.serializable) &&
		    (externalizable != localDesc.externalizable))
		{
		    throw new InvalidClassException(localDesc.name, 
			"Serializable incompatible with Externalizable");
		}

		if ((serializable != localDesc.serializable) ||
		    (externalizable != localDesc.externalizable) ||
		    !(serializable || externalizable))
		{
		    deserializeEx = new InvalidClassException(localDesc.name,
			"class invalid for deserialization");
		}
	    }
	    
	    cons = localDesc.cons;
	    writeObjectMethod = localDesc.writeObjectMethod;
	    readObjectMethod = localDesc.readObjectMethod;
	    readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
	    writeReplaceMethod = localDesc.writeReplaceMethod;
	    readResolveMethod = localDesc.readResolveMethod;
	    if (deserializeEx == null) {
		deserializeEx = localDesc.deserializeEx;
	    }
	}
	fieldRefl = getReflector(fields, localDesc);
	// reassign to matched fields so as to reflect local unshared settings
	fields = fieldRefl.getFields();
    }
    

所以serialVersionUID在前后都不能做修改,否则会导致序列化失败。

posted @ 2012-08-04 10:04  MXi4oyu  阅读(454)  评论(0编辑  收藏  举报