Transient 关键字修饰变量是否可以被序列化

前提: 最近在看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源码进行跟踪

猜你喜欢

转载自juejin.im/post/5d60b95e6fb9a06ae57cfa75