`

java对序列化serialVersionUID的处理分析

    博客分类:
  • java
 
阅读更多

继上次分析了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在前后都不能做修改,否则会导致序列化失败。

0
1
分享到:
评论

相关推荐

    Java序列化_Java序列化结构_

    Java序列化还支持版本控制,即`serialVersionUID`。这个版本号用于验证序列化过程中类的兼容性。如果类在序列化后进行了修改,`serialVersionUID`应该相应更新,否则反序列化可能会失败。如果没有显式定义,Java会...

    java serializable 序列化与反序列化

    **一、Java序列化** 1. **什么是序列化**:序列化是将对象的状态(属性和成员变量)转换为可以存储或传输的数据格式的过程。在Java中,通常是将对象转换为字节数组,以便写入磁盘或通过网络发送。 2. **为什么需要...

    java对象序列化和反序列化

    Java对象序列化与反序列化是Java编程中重要的概念,主要应用于数据持久化、网络传输以及存储等场景。本文将详细解析这两个概念及其在实际应用中的实现方式。 **一、Java对象序列化** 1. **定义**: Java对象序列化...

    Java 文件 序列化 读写

    为了处理这种情况,可以为类定义一个`static final long serialVersionUID`字段,以指定序列化版本。 6. **忽略序列化**: - 如果某个字段不希望参与序列化,可以使用`transient`关键字标记。这样,序列化时会忽略...

    java 对象的序列化与反序列化

    1. **序列化标识符(SerialVersionUID)**:Java允许你为每个可序列化的类定义一个唯一的`serialVersionUID`,默认是由JVM根据类的结构计算出来的。如果类的版本更新导致结构变化,而此值未做更新,反序列化时会抛出`...

    java自动序列化

    Java序列化是将对象转换为字节流的过程,目的是为了保存对象的状态以便稍后恢复或传输到其他地方。通过实现`Serializable`接口,一个Java对象就可以被序列化。这个接口是一个标记接口,没有定义任何方法,仅表示对象...

    Java对象序列化标准最新版

    ### Java对象序列化标准知识点详解 #### 一、系统架构概览 **1.1 概览** Java 对象序列化是一种将Java对象的...以上内容涵盖了Java序列化标准的关键知识点,深入了解这些概念有助于更好地理解和应用Java序列化技术。

    Java序列化

    Java序列化是Java平台中的一种标准机制,允许将对象的状态转换为...通过以上知识点的学习,你可以对Java序列化有深入的理解,无论是理论还是实践,都能为你在开发过程中处理对象持久化、数据传输等问题提供有力支持。

    关于 Java 对象序列化您不知道的 5 件事

    序列化使用的是`ObjectOutputStream`和`ObjectInputStream`,它们继承自`DataOutputStream`和`DataInputStream`,但增加了对Java对象的支持。数据流则主要用于基本类型和字符串的读写。 8. **序列化与克隆** 虽然...

    Java_序列化的高级认识

    序列化ID,即`serialVersionUID`,是Java序列化机制中一个关键的概念。它是一个类的唯一标识符,用于在序列化和反序列化过程中确定类的版本一致性。如果序列化对象和反序列化对象的`serialVersionUID`不匹配,将会抛...

    java 对象序列化

    Java对象序列化是一种将Java对象转换为字节流的过程,以便可以存储在磁盘上、在网络上传输或在任何其他需要持久化数据的场景中使用。这个过程涉及到两个主要概念:序列化(Marshalling)和反序列化(Unmarshalling)...

    Java实现序列化例子

    Java序列化是Java平台提供的一种将对象转换为字节流,以便存储到磁盘、数据库或网络中的机制。它是Java语言内置的一种特性,主要用于持久化数据,也可以在进程间传递对象,或者在网络上传输对象。在Java中,如果一个...

    java序列化实现演示

    在给定的链接"Java序列化机制(2)- serialVersionUID 实验"中,博主通过一个实验详细解释了`serialVersionUID`的作用和重要性。实验可能包括以下步骤: 1. 创建一个实现`Serializable`接口的简单类,并运行序列化...

    java序列化(Serializable)的作用和反序列化.doc

    ### Java序列化(Serializable)的作用与反序列化详解 #### 一、序列化的概念 序列化是指将程序中的对象转换为一系列字节流的过程,主要用于保存对象的状态或在网络之间传输对象。序列化的主要目的是为了能够持久化...

    Java对象序列化的秘密

    2. **serialVersionUID**: 每个可序列化的类都有一个`serialVersionUID`,用于检查序列化版本的一致性。如果类的版本发生变化但此ID未更新,反序列化时可能会抛出`InvalidClassException`。 3. **writeObject()和...

    java程序,序列化和反序列化操作对文件的运用

    总结来说,Java中的序列化和反序列化是处理对象和文件之间转换的关键技术。它们允许我们保存和恢复对象状态,方便数据存储和传输。同时,我们也应该了解如何处理文件操作,以及在面对类结构变动和安全性问题时如何...

    java 序列化代码示例

    Java序列化是Java平台中的一种标准机制,它允许将对象的状态转换为字节流,以便存储、传输或恢复。在Java中,一个类如果要实现序列化,需要实现`Serializable`接口,这是一个标记接口,不包含任何方法。下面我们将...

    java序列化(Serializable)的作用和反序列化

    ### Java序列化(Serializable)的作用与反序列化详解 #### 一、序列化是什么? 序列化是指将程序中的对象转换为字节流的过程,从而方便存储或传输这些对象。通常,序列化用于将对象的状态(即其实例变量的值,而非...

    java对象序列化 传输 保存

    在标签中提到了"源码"和"工具",这可能意味着博客作者还深入分析了Java序列化机制的源代码,并介绍了一些辅助工具,例如`jserialcom`或`serialver`命令,用于查看类的serialVersionUID。 文件列表中的"ser"可能是...

Global site tag (gtag.js) - Google Analytics