一,简介
修饰变量,用于标明该变量不用序列化到字节流中。
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关键字(=“不应序列化字段”) 可变参数 字符串生成器 instanceof确切行为(在接口上使用null和用法) 包和导入(静态导入,重要的java.*包) 更多关键词: synchronized 、 transient 、 native 回顾:...
【基础】Java序列化与反序列化 27 为什么需要序列化与反序列化 28 如何实现Java序列化与反序列化 28 【基础】String s = new String("xyz");创建了几个字符串对象 30 【基础】接口是否可继承(extends)接口?抽象类...
集合类(底层实现原理,实现类的优缺点) List ArrayList(, ) LinkedList(, ) Map(,) Hashtable() HashMap(,) HashMap怎么判断两个元素不是同一个(equals 和 hashcode) 多线程操作后果(,) rehash() ConcurrentHashMap...
Java中序列化和反序列化的区别。 什么是SerialVersionUID? 内部类和子类的区别。 JSON 相对于 XML 的优势是什么? 我们可以两次导入相同的包/类吗? JVM 会在运行时加载包两次吗? 静态加载和动态类加载的区别? ...
程序员面试刷题的书哪个好Javonical 作为一名专业的自由程序员,我参加了(并进行了)我应得的 Java 技术测试。 它们通常是申请职位时的第一步,因此您必须确保熟悉预期的答案。...阻止该字段被序列化。
序列化与反序列化 transient 枚举 注解 JDK7新特性 JDK8新特性 JDK9新特性 JDK10新特性 运行时数据区 对象 OOM & SOF 内存模型 垃圾回收算法 垃圾收集器 内存分配与回收策略 类加载 参数优化 进程与线程的区别 进程...
25. 集合类都有哪些?主要方法? 14 26. 简述逻辑操作(&,|,^)与条件操作(&&,||)的区别。 14 27. XML文档定义有几种形式?它们之间有何本质区别?解析XML文档有哪几种方式? 14 28. JSP和Servlet有哪些相同点和不同点...
实例114 transient关键字的应用 143 5.7 接口和内部类 145 实例115 使用sort()方法排序 145 实例116 简化compareTo()方法的重写 146 实例117 策略模式的简单应用 148 实例118 适配器模式的简单应用 149 实例119 普通...
1. 目录 1. 2. 目录 .........................................................................................................................................................1 JVM ........................