1.关于transient的解释
transient用来表示一个域(对象属性)不是该对象串行化(序列化)的一部分。当一个对象被串行化的时候,transient修饰的变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。
2.实践证明
A.第一步:新建一个其对象可以被序列化的类Book,源码如下:
/** * 该类对象可以被序列化 * @author yuanli * */ public class Book implements Serializable { /** * */ private static final long serialVersionUID = 3173949523199923358L; private int id; private String name; private transient float price; public Book() { super(); } public Book(int id, String name, float price) { super(); this.id = id; this.name = name; this.price = price; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } @Override public String toString() { return "id=" + this.id + ",name=" + this.name + ",price=" + this.price; } }
B.第二步,编写测试类进行测试,代码如下:
public void testTransient() { try { Book book = new Book(1,"aa",12.5f); System.out.println("book序列化之前: " + book.toString()); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); //序列化book对象 oos.writeObject(book); //反序列化book对象 ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())); Book book_serializable = (Book)ois.readObject(); System.out.println("book反序列化之后:" + book_serializable.toString()); } catch (Exception e) { e.printStackTrace(); } }
C.第三步,运行,分析执行结果
book序列化之前: id=1,name=aa,price=12.5
book反序列化之后:id=1,name=aa,price=0.0
从执行结果中可以发现,反序列化之后book对象的price属性变为默认值了,实践证明,被transient关键字修饰的域(属性)确实不会被序列化。
3.困惑之处
通过上面的实践自我感觉已经掌握了transient的用处,然而分析ArrayList源码之后又让我多了几分困惑,部分源码如下:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8683452581122892189L; /** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. */ private transient Object[] elementData; /** * The size of the ArrayList (the number of elements it contains). * * @serial */ private int size; /** * Constructs an empty list with the specified initial capacity. * * @param initialCapacity the initial capacity of the list * @exception IllegalArgumentException if the specified initial capacity * is negative */ public ArrayList(int initialCapacity) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; } /** * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { this(10); } ....... }
研究发现ArrayList的背后实际上是一个动态数组,private transient Object[] elementData;那为什么要修饰为transient呢?这样岂不是不能被序列化了?我带着疑问进行了测试,结果发现能够正常序列化和反序列化,集合中的元素也不会丢失,如果这样,那岂不与上面的实践结论相矛盾?看到下面的解释后,让我豁然开朗。
elementData是一个缓存数组,它通常会预留一些容量,等容量不足时再扩充容量。假如现在实际有了5个元素,而elementData的大小可能是10,那么在序列化时只需要储存5个元素,数组中的最后五个元素是没有实际意义的,不需要储存。所以ArrayList的设计者将elementData设计为transient,然后在writeObject方法中手动将其序列化,并且只序列化了实际存储的那些元素,而不是整个数组
4.再次困惑
LinkedList的部分代码如下:
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable { private transient Entry<E> header = new Entry<E>(null, null, null); private transient int size = 0; /** * Constructs an empty list. */ public LinkedList() { header.next = header.previous = header; } /** * Constructs a list containing the elements of the specified * collection, in the order they are returned by the collection's * iterator. * * @param c the collection whose elements are to be placed into this list * @throws NullPointerException if the specified collection is null */ public LinkedList(Collection<? extends E> c) { this(); addAll(c); } ...... /** * Returns the number of elements in this list. * * @return the number of elements in this list */ public int size() { return size; } ...... }
研究LinkedList源码发现size和head属性被transient修饰,这样的属性应该不会被序列化,可实践证明可以正常序列化,反序列化后调用size()方法返回的结果同序列化前相同。那么这样设计的目的何在?求解中...............
ss