transient:not lasting long; staying somewhere only a short time.
作用
- 当对象被序列化时(写入字节序列到目标文件)时,transient阻止实例中那些用此关键字声明的变量持久化;当对象被反序列化时(从源文件读取字节序列进行重构),这样的实例变量值不会被持久化和恢复
- 类变量(用static修饰的)即使用
transient
修饰,也不会被序列化。
在jdk中的应用实例
ArrayList:
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
按道理,被transient
修饰的实例遍历,是不会序列化的呢。然而
public static void main(String[] args) {
ArrayList<Integer> arrayList=new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
arrayList.add(4);
/*序列话对象arrayList到a.txt文件中*/
try {
OutputStream outputStream=new FileOutputStream("/home/fengli/Desktop/a.txt");
outputStream=new ObjectOutputStream(outputStream);
((ObjectOutputStream) outputStream).writeObject(arrayList);
} catch (IOException e) {
e.printStackTrace();
}
//把arrayList置为null,便于反序列话
arrayList=null;
/*反序列话*/
try {
InputStream inputStream=new FileInputStream("/home/fengli/Desktop/a.txt");
inputStream=new ObjectInputStream(inputStream);
arrayList= (ArrayList<Integer>) ((ObjectInputStream) inputStream).readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
/*现在测试arrayList中是否有数据*/
for(int i=0;i<arrayList.size();++i){
System.out.println("arrayList.get(i) = " + arrayList.get(i));
}
}
运行结果:
arrayList.get(i) = 1
arrayList.get(i) = 2
arrayList.get(i) = 3
arrayList.get(i) = 4
Q:为哈子安?
因为ArrayList重写了writeObject()
readObject()
两个方法(mb,我idea的structure没选择show non-public,导致始终没找到这两个方法。疑惑了半天!)
源码:
/**
* Save the state of the <tt>ArrayList</tt> instance to a stream (that
* is, serialize it).
*
* @serialData The length of the array backing the <tt>ArrayList</tt>
* instance is emitted (int), followed by all of its elements
* (each an <tt>Object</tt>) in the proper order.
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);/这里
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
/**
* Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
* deserialize it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
s.readInt(); // ignored
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
int capacity = calculateCapacity(elementData, size);
SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
ensureCapacityInternal(size);
Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}
至于jdk为什么要这样操作,请看 ArrayList中elementData为什么被transient修饰?
原因在于elementData是一个缓存数组,它通常会预留一些容量,等容量不足时再扩充容量,那么有些空间可能就没有实际存储元素,采用上诉的方式来实现序列化时,就可以保证只序列化实际存储的那些元素,而不是整个数组,从而节省空间和时间。
结论
- transient使用来禁止序列化实例对象的。