Java——transient and 序列化

来源:互联网 发布:淘宝主图视频软件 编辑:程序博客网 时间:2024/05/07 02:17

序列化

序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。
在网络传输过程中,可以是字节或是XML,json等格式。而字节的,XML,json编码格式可以还原完全相等的对象。这个相反的过程又称为反序列化。

transient

java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。

序列化两种方式

1.对象实现了序列化接口Serializable

2.第二种方式为实现接口Externalizable

其实这个接口也是继承了Serializable接口,该接口中有两个方法 void writeExternal(ObjectOutput out)void readExternal(ObjectInput in)
,所以使用这种方式,要实现这两个接口。在 writeExternal() 方法里定义了哪些属性可以序列化,例如 out.writeObject(userName),哪些不可以序列化,对象在经过这里就把规定能被序列化的序列化保存文件,不能序列化的不处理,然后在反序列的时候自动调用 readExternal() 方法,如userName=(String) in.readObject(),根据序列顺序挨个读取进行反序列,并自动封装成对象返回。

可以看出来Externalizable形式的序列化会更灵活一些,可以自己定义哪些字段需要序列化,和transient的功能有些类似。

代码示例

代码中有两种实现方式,第一种的测试代码被注释了。

public class SerializationPractice {    public void serializableTest() {        // Worker worker = new Worker();        Worker1 worker = new Worker1();        worker.setAge(30);        worker.setName("gary");        worker.setHeight(170);        System.out.println(worker);        try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(                new FileOutputStream("/Users/gary/Documents/serializeTest.txt"))) {            objectOutputStream.writeObject(worker);        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }    }    public void deserializeTest() {        try (ObjectInputStream objectInputStream = new ObjectInputStream(                new FileInputStream("/Users/gary/Documents/serializeTest.txt"))) {            // Worker worker = (Worker) objectInputStream.readObject();            Worker1 worker = (Worker1) objectInputStream.readObject();            System.out.println(worker);        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } catch (ClassNotFoundException e) {            e.printStackTrace();        }    }    public static void main(String[] args) {        SerializationPractice practice = new SerializationPractice();        //序列化         practice.serializableTest();        //反序列化        //practice.deserializeTest();    }}class Worker implements Serializable {    private static final long serialVersionUID = 2L;    private String name;    private int age;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    @Override    public String toString() {        return "name=" + name + " ,age=" + age;    }}class Worker1 implements Externalizable {    private static final long serialVersionUID = 1L;    private String name;    private int age;    private int height;    public Worker1() {    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    public int getHeight() {        return height;    }    public void setHeight(int height) {        this.height = height;    }    @Override    public String toString() {        return "name=" + name + ",age=" + age + ",height=" + height;    }    @Override    public void writeExternal(ObjectOutput objectOutput) {        try {            objectOutput.writeObject(name);            objectOutput.writeInt(age);            objectOutput.writeInt(height);        } catch (IOException e) {            e.printStackTrace();        }    }    @Override    public void readExternal(ObjectInput objectInput) {        try {            name = (String) objectInput.readObject();            age = objectInput.readInt();            height = objectInput.readInt();        } catch (IOException e) {            e.printStackTrace();        } catch (ClassNotFoundException e) {            e.printStackTrace();        }    }}

实现接口Externalizable的序列化时要注意一下情况:

  1. writeExternal方法中没序列化的属性,反序列化后得到的是属性类型的默认值,和 transient 作用类似。

  2. 实现接口Externalizable的类要提供无参数的构造函数,否则会报下面的错误

java.io.InvalidClassException: javabase.serialization.Worker1; no valid constructor

在使用Externalizable进行序列化的时候,在读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。所以,实现Externalizable接口的类必须要提供一个public的无参的构造器。

序列化ID

虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID)。

序列化时 serialVersionUID=1L,然后反序列化是修改Worker类的 serialVersionUID=2L,就会报下面的错误。

java.io.InvalidClassException: javabase.serialization.Worker; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2

总结

  1. 如果一个类想被序列化,需要实现Serializable接口或Externalizable接口。否则将抛出NotSerializableException异常,这是因为,在序列化操作过程中会对类型进行检查,要求被序列化的类必须属于Enum、Array和Serializable类型其中的任何一种。
  2. 在变量声明前加上该关键字,可以阻止该变量被序列化到文件中。
  3. 在类中增加writeObject 和 readObject 方法可以实现自定义序列化策略
  4. 序列化并不保存静态变量。

参考资料

Java对象的序列化与反序列化

1 0
原创粉丝点击