前提: 最近在看CopyOnWriteArrayList的源码,发现其内部的数组array是被 transient关键词修饰,觉得奇怪,因为CopyOnWriteArrayList是可以被序列化的,其内部保存的元素也会被序列化,所以引发了对transient的思考:《Transient 关键字修饰变量是否可以被序列化》
Transient关键词
1.transient关键词的第一印象:被其修饰的变量不可被序列化(下面例子验证)
Entity
public class TestStream implements Serializable {
private String id;
private transient String name;
private transient volatile Object[] array;
private transient Integer age;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object[] getArray() {
return array;
}
public void setArray(Object[] array) {
this.array = array;
}
@Override
public String toString() {
return "TestStream{" +
"id='" + id + '\'' +
"age='" + age + '\'' +
", name='" + name + '\'' +
", array=" + Arrays.toString(array) +
'}';
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
复制代码
TestClass
@Test
public void testSeri() throws Exception {
//测试 序列化
TestStream testStream = new TestStream();
testStream.setId("123");
testStream.setName("QBH");
testStream.setArray(new Object[]{1,2,3});
testStream.setAge(18);
System.out.println(testStream);
//将对象读到流里面
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(testStream);
//将对象从流里读出来
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
TestStream object1 = (TestStream)oi.readObject();
System.out.println(object1);
}
复制代码
Result
其中age name array都是被transient修饰的变量,并没有被序列化
TestStream{id='007'age='18', name='juejin', array=[1, 2, 3]}
TestStream{id='007'age='null', name='null', array=null}
复制代码
writeObject和readOject
wirteObject方法
当对象“重写”了这个方法,进行序列化的时候会调用此方法(采用反射进行检查是否对此方法进行重写)。 知道了方法什么时候被调用,那即可知道其用途了:定制序列化步骤
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{}
复制代码
当对象“重写”了这个方法,进行反序列化的时候会调用此方法(采用反射进行检查是否对此方法进行重写) 用途:定制反序列化步骤
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException{}
复制代码
例子
Entity:里面重写了writeObject和readObject方法(CopyOnwriteArrayList也重写了,cpoy过来进行了一些属性的定制序列|反序列化)
package entity;
import java.io.Serializable;
import java.util.Arrays;
import sun.misc.SharedSecrets;
public class TestStream implements Serializable {
private String id;
private transient String name;
private transient volatile Object[] array;//与CopyOnWriteArrayList的数组一致
private transient Integer age;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object[] getArray() {
return array;
}
public void setArray(Object[] array) {
this.array = array;
}
@Override
public String toString() {
return "TestStream{" +
"id='" + id + '\'' +
"age='" + age + '\'' +
", name='" + name + '\'' +
", array=" + Arrays.toString(array) +
'}';
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
s.defaultWriteObject();
Object[] elements = getArray();
// Write out array length
s.writeInt(elements.length);//序列化array的长度
s.writeInt(age);//序列化age属性
// Write out all elements in the proper order.
for (Object element : elements)
s.writeObject(element);
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
int len = s.readInt();//读取数组的长度
setAge(s.readInt());//读取age的值
SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, len);
Object[] elements = new Object[len];
// Read in all elements in the proper order.
for (int i = 0; i < len; i++)
elements[i] = s.readObject();
setArray(elements);
}
}
复制代码
TestClass
@Test
public void testSeri() throws Exception {
//测试 序列化
TestStream testStream = new TestStream();
testStream.setId("123");
testStream.setName("QBH");
testStream.setArray(new Object[]{1,2,3});
testStream.setAge(18);
System.out.println(testStream);
//将对象读到流里面
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(testStream);
//将对象从流里读出来
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
TestStream object1 = (TestStream)oi.readObject();
System.out.println(object1);
}
复制代码
Result
name 和 array 都被反序列化了
TestStream{id='007'age='18', name='juejin', array=[1, 2, 3]}
TestStream{id='007'age='18', name='null', array=[1, 2, 3]}
复制代码
总结
所以被transient修饰的属性并不是不可以被序列化。但是属性write和read顺序要一一对应,不然出现序列化错误
思考
为什么CopyOnWriteArrayList对array进行了transient修饰,还要对其进行重写writeObject和readObject进行序列化??
:因为CopyOnWriteArrayList的array是一个“素组”,数组的容量一般都不等于实际元素的个数,所以避免在反序列化的时候出现数组元素数目不一致的问题(自写一个简单版List进行测试即可),所以进行重写序列化步骤。
此篇文章涉及序列化的知识很浅,只介绍了如何进行定制序列化,如果想要了解序列化的更深的知识,可debug源码进行跟踪