一,简介
修饰变量,用于标明该变量不用序列化到字节流中。
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关键字屏蔽变量,然后自己实现的序列化操作。
相关推荐
在进行序列化时,可以使用transient关键字标记不希望被序列化的字段,这样在对象序列化时,被标记为transient的字段将不会被包含在序列化的输出中。例如,如果一个类中有密码这样的敏感信息,并且不希望这些信息在...
然而,需要注意的是,序列化可能会暴露类的私有信息,因此非公开的数据成员和方法应当用`transient`或`volatile`关键字标记,以防止它们被序列化。 接下来,我们讨论`equals`和`hashCode`方法。这两个方法在Java中...
`transient`关键字用于标记不需要序列化的对象属性,通常用于临时状态或敏感信息。 ### 44. try `try`关键字用于尝试执行可能抛出异常的代码块,与`catch`和`finally`关键字配合使用。 ### 45. void `void`...
* transient关键字:在ArrayList中,elementData字段使用transient关键字修饰,以避免序列化时将elementData字段序列化。 * readObject和writeObject方法:在ArrayList中,需要实现readObject和writeObject方法,...
- **用途**:用于标注对象序列化过程中不需要保存的变量。 47. **try** - **用途**:用于尝试执行一段代码,并捕获可能发生的异常。 48. **void** - **用途**:表示空类型,用于声明无返回值的方法。 49. *...
Jackson是Java领域中广泛使用的序列化和反序列化库,由FasterXML团队开发并维护。它能够将Java对象转换为JSON格式的数据,同时也能够将JSON数据解析回对应的Java对象,极大地简化了Java应用程序与JSON数据的交互。...
关于序列化,文档探讨了如何实现Java对象的序列化和反序列化,以及为什么实现了Serializable接口的对象能够被序列化,包括默认序列化机制、SerialVersionUID的作用,以及序列化的影响因素和常见第三方库。...
46. `transient` - 表示字段不会序列化。 47. `try` - 异常处理的一部分,包含可能抛出异常的代码块。 48. `void` - 表示方法不返回任何值。 49. `volatile` - 保证多线程环境中的变量可见性和一致性。 50. `while` ...
- ** transient 关键字**:在序列化过程中,如果希望某些字段不被序列化,可以使用`transient`关键字标记。 3. **Ch03 GUI编程** - **Java AWT(Abstract Window Toolkit)**:是Java早期的图形用户界面库,提供...
transient关键字是用于告诉Java序列化机制,在序列化对象时,该字段不应被序列化。 知识点二:ArrayList的构造函数 文章中提到了ArrayList的三个构造函数: 1. 无参构造函数:创建一个默认初始容量为10的ArrayList...
- 除了直接实现`Serializable`接口,还可以使用`transient`关键字标记不想序列化的成员变量。 14. **在COLLECTION框架中实现比较**: - 要实现Comparable接口,让类的实例能相互比较。 15. **插入法排序**: - ...
30. `transient`: 修饰符,用于声明变量不参与序列化,当对象被序列化时,这些变量的值不会保存。 31. `try`: 定义一个可能抛出异常的代码块,通常与`catch`和`finally`配合使用,用于异常处理。 以上仅是Java...
9. **序列化**:介绍了如何使类支持序列化,理解transient关键字的作用,以及如何实现自己的序列化版本ID。 10. **代码优化**:探讨了性能优化的策略,如避免过度优化,理解对象创建和垃圾收集的过程,以及使用...
46. `transient`:非持久化修饰符,忽略变量在序列化过程中的存储。 47. `try`:异常处理的开始,包含可能会抛出异常的代码。 48. `void`:声明无返回值的方法。 49. `volatile`:确保多线程环境下变量的可见性和...
30. `transient`:用于标记变量,使其在序列化时不保存其值。 31. `try-catch-finally`:用于处理异常,`try`块包含可能抛出异常的代码,`catch`块处理异常,`finally`块确保在任何情况下都会执行。 32. `void`:...
【Java面向对象特征】 ...transient修饰的字段不会参与序列化,即在序列化和反序列化过程中,该字段的值会被忽略。示例代码中的serialVersionUID与序列化版本号有关,用于判断序列化前后类结构是否发生变化。
- **Java 修饰符排除**:例如,通过 `transient` 关键字标记的字段不会被序列化。 - **Gson 的 `@Expose` 注解**:使用该注解标记需要排除的字段。 - **用户自定义排除策略**:允许用户自定义字段排除逻辑,提供更大...