`

transient关键字作用及集合类序列化的原理

    博客分类:
  • java
阅读更多

一,简介

        修饰变量,用于标明该变量不用序列化到字节流中。

       

         static class User implements Serializable{

            private static final long serialVersionUID = 1L;
            
            private String username = null;
            private transient String password = null; //使用 transient 关键字标明该变量值不会被序列化到字节流中
            public String getUsername()
            {
                return username;
            }
            public void setUsername(String username)
            {
                this.username = username;
            }
            public String getPassword()
            {
                return password;
            }
            public void setPassword(String password)
            {
                this.password = password;
            }
            
         }

 二,特例分析

     了解了transient关键字的作用后,下面来看几个特殊的例子。Map和Set等集合类的序列化实现。JDK源码如下(HashMap为例):

    

/**HashMap序列化实现**/
public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable
{
 
      /**
     * The default initial capacity - MUST be a power of two.
     */
    static final int DEFAULT_INITIAL_CAPACITY = 16;
    
    /**
     * The table, resized as necessary. Length MUST Always be a power of two.
     */
    transient Entry[] table; //存放数据的table

    /**
     * The number of key-value mappings contained in this map.
     */
    transient int size;//数据的个数

}

    通过JDK源码发现,HashMap对于存放数据的变量table和大小size,都使用了transient关键字修饰(即不序列化该变量)。那么HashMap又是如何完成对数据序列化的呢?

   继续阅读JDK源码,发现HashMap自己实现了一套writeObject,和readObject方法。

   

/**
     * Save the state of the <tt>HashMap</tt> instance to a stream (i.e.,
     * serialize it).
     *
     * @serialData The <i>capacity</i> of the HashMap (the length of the
     *		   bucket array) is emitted (int), followed by the
     *		   <i>size</i> (an int, the number of key-value
     *		   mappings), followed by the key (Object) and value (Object)
     *		   for each key-value mapping.  The key-value mappings are
     *		   emitted in no particular order.
     */
    private void writeObject(java.io.ObjectOutputStream s)
        throws IOException
    {
	Iterator<Map.Entry<K,V>> i =
	    (size > 0) ? entrySet0().iterator() : null;

	// Write out the threshold, loadfactor, and any hidden stuff
	s.defaultWriteObject();

	// Write out number of buckets
	s.writeInt(table.length);

	// Write out size (number of Mappings)
	s.writeInt(size);

        // Write out keys and values (alternating)
	if (i != null) {
	    while (i.hasNext()) {
		Map.Entry<K,V> e = i.next();
		s.writeObject(e.getKey());
		s.writeObject(e.getValue());
	    }
        }
    }

    private static final long serialVersionUID = 362498820763181265L;

    /**
     * Reconstitute the <tt>HashMap</tt> instance from a stream (i.e.,
     * deserialize it).
     */
    private void readObject(java.io.ObjectInputStream s)
         throws IOException, ClassNotFoundException
    {
	// Read in the threshold, loadfactor, and any hidden stuff
	s.defaultReadObject();

	// Read in number of buckets and allocate the bucket array;
	int numBuckets = s.readInt();
	table = new Entry[numBuckets];

        init();  // Give subclass a chance to do its thing.

	// Read in size (number of Mappings)
	int size = s.readInt();

	// Read the keys and values, and put the mappings in the HashMap
	for (int i=0; i<size; i++) {
	    K key = (K) s.readObject();
	    V value = (V) s.readObject();
	    putForCreate(key, value);
	}
    }

    当使用ObjectOutputStream writeObject序列化对象时,如果该对象有writeObject方法则调用该对象的writeObject方法(通过反射实现),这样达到序列化重写的目的。JDK源码如下:

 /**
     * Writes instance data for each serializable class of given object, from
     * superclass to subclass.
     * ObjectOutputStream writeObject会调用writeSerialData完成对实现Serializable标志 性接口的序列化
     */
    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;

		if (extendedDebugInfo) {
		    debugInfoStack.push(
			"custom writeObject data (class \"" + 
			slotDesc.getName() + "\")");
		}

                SerialCallbackContext oldContext = curContext;
		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); //待序列化的对象没有writeObject方法,则使用JDK默认的
	    }
	}
    }

 三,总结

    Map,Set,List等都是使用transient关键字屏蔽变量,然后自己实现的序列化操作。

分享到:
评论

相关推荐

    ArrayList的底层原理Java系列2021.pdf

    在进行序列化时,可以使用transient关键字标记不希望被序列化的字段,这样在对象序列化时,被标记为transient的字段将不会被包含在序列化的输出中。例如,如果一个类中有密码这样的敏感信息,并且不希望这些信息在...

    java 序列化和重写 hashCode 的原因

    然而,需要注意的是,序列化可能会暴露类的私有信息,因此非公开的数据成员和方法应当用`transient`或`volatile`关键字标记,以防止它们被序列化。 接下来,我们讨论`equals`和`hashCode`方法。这两个方法在Java中...

    48个关键字

    `transient`关键字用于标记不需要序列化的对象属性,通常用于临时状态或敏感信息。 ### 44. try `try`关键字用于尝试执行可能抛出异常的代码块,与`catch`和`finally`关键字配合使用。 ### 45. void `void`...

    java-集合-知识点汇总

    * transient关键字:在ArrayList中,elementData字段使用transient关键字修饰,以避免序列化时将elementData字段序列化。 * readObject和writeObject方法:在ArrayList中,需要实现readObject和writeObject方法,...

    java关键字、保留字

    - **用途**:用于标注对象序列化过程中不需要保存的变量。 47. **try** - **用途**:用于尝试执行一段代码,并捕获可能发生的异常。 48. **void** - **用途**:表示空类型,用于声明无返回值的方法。 49. *...

    序列化工具jackson

    Jackson是Java领域中广泛使用的序列化和反序列化库,由FasterXML团队开发并维护。它能够将Java对象转换为JSON格式的数据,同时也能够将JSON数据解析回对应的Java对象,极大地简化了Java应用程序与JSON数据的交互。...

    java技术指南

    关于序列化,文档探讨了如何实现Java对象的序列化和反序列化,以及为什么实现了Serializable接口的对象能够被序列化,包括默认序列化机制、SerialVersionUID的作用,以及序列化的影响因素和常见第三方库。...

    java中“53”个关键字(含2个保留字)

    46. `transient` - 表示字段不会序列化。 47. `try` - 异常处理的一部分,包含可能抛出异常的代码块。 48. `void` - 表示方法不返回任何值。 49. `volatile` - 保证多线程环境中的变量可见性和一致性。 50. `while` ...

    java高级编程PPT

    - ** transient 关键字**:在序列化过程中,如果希望某些字段不被序列化,可以使用`transient`关键字标记。 3. **Ch03 GUI编程** - **Java AWT(Abstract Window Toolkit)**:是Java早期的图形用户界面库,提供...

    java提高篇(二一)-----ArrayList.pdf

    transient关键字是用于告诉Java序列化机制,在序列化对象时,该字段不应被序列化。 知识点二:ArrayList的构造函数 文章中提到了ArrayList的三个构造函数: 1. 无参构造函数:创建一个默认初始容量为10的ArrayList...

    java面试题集合

    - 除了直接实现`Serializable`接口,还可以使用`transient`关键字标记不想序列化的成员变量。 14. **在COLLECTION框架中实现比较**: - 要实现Comparable接口,让类的实例能相互比较。 15. **插入法排序**: - ...

    Java的关键字解释.pdf

    30. `transient`: 修饰符,用于声明变量不参与序列化,当对象被序列化时,这些变量的值不会保存。 31. `try`: 定义一个可能抛出异常的代码块,通常与`catch`和`finally`配合使用,用于异常处理。 以上仅是Java...

    effectice java第二版

    9. **序列化**:介绍了如何使类支持序列化,理解transient关键字的作用,以及如何实现自己的序列化版本ID。 10. **代码优化**:探讨了性能优化的策略,如避免过度优化,理解对象创建和垃圾收集的过程,以及使用...

    50个关键字_3个直接量_JAVA的五十个关键词_

    46. `transient`:非持久化修饰符,忽略变量在序列化过程中的存储。 47. `try`:异常处理的开始,包含可能会抛出异常的代码。 48. `void`:声明无返回值的方法。 49. `volatile`:确保多线程环境下变量的可见性和...

    JAVA中的关键字和保留字[归类].pdf

    30. `transient`:用于标记变量,使其在序列化时不保存其值。 31. `try-catch-finally`:用于处理异常,`try`块包含可能抛出异常的代码,`catch`块处理异常,`finally`块确保在任何情况下都会执行。 32. `void`:...

    程序员笔试题目(JAVA).doc

    【Java面向对象特征】 ...transient修饰的字段不会参与序列化,即在序列化和反序列化过程中,该字段的值会被忽略。示例代码中的serialVersionUID与序列化版本号有关,用于判断序列化前后类结构是否发生变化。

    Gson User Guide pdf(英文)

    - **Java 修饰符排除**:例如,通过 `transient` 关键字标记的字段不会被序列化。 - **Gson 的 `@Expose` 注解**:使用该注解标记需要排除的字段。 - **用户自定义排除策略**:允许用户自定义字段排除逻辑,提供更大...

Global site tag (gtag.js) - Google Analytics