Java序列化

Java序列化教程

Java 对象的序列化就是把一个对象变为二进制的数据流的一种方法。它用一个字节序列表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。

反序列化表示将字节序列转换为对应的对象。通过序列化可以将对象大小压缩,减少对象大小,方便对象数据传输或者存储。

如果一个类的对象想被序列化,那么这个类就必须要实现 Serializable 接口。该接口没有什么特殊的方法,它就是一个标识接口,表示一个类具备了被序列化的能力。

如果一个类实现了 Serializable 接口表示该类创建的对象是可以经过二进制数据流进行传输的。如果想让二进制流进行传输时需要借助对象输出流(ObjectOutputStream)和对象输入流(ObjectInputStream)。

15 序列化.jpg

对象的序列化和反序列化存在版本问题,如果序列化的 JDK 版本和反序列化的 JDK 版本不一致就可能造成异常。在进行反序列化的时候,JVM 会把传递过来的字节流中的 serialVersionUID 与本地相应的实体的 serialVersionUID 进行比较。如果相同就认为是一致的,如果不相同就抛出异常。

Java ObjectOutputStream

一个对象如果要进行输出,就必须要使用 ObjectOutputStream 类。常用的方法有:

方法名 描述
public ObjectOutputStream(OutputStream out) throws IOException 构造函数,传入输入流对象
public final void writeObject(Object obj) throws IOException 输出对象

案例

定义一个 Person 类实现 Serializable 接口,使其可以序列化

package com.haicoder.net.stream.object2stream; import java.io.Serializable; public class Person implements Serializable { private String name; private Integer age; public Person() { } public Person(String name, Integer age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }

定义一个序列化类

package com.haicoder.net.stream.object2stream; import java.io.File; import java.io.FileOutputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; public class Object2StreamTest { public static void main(String[] args) throws Exception { System.out.println("嗨客网(www.haicoder.net)"); File file = new File("/Users/haicoder/Documents/code/hai/Object2StreamTest.txt"); //如果文件不存在,会自动创建 OutputStream out = new FileOutputStream(file); ObjectOutputStream objectOutputStream = new ObjectOutputStream(out); objectOutputStream.writeObject(new Person("嗨客网", 100)); objectOutputStream.close(); } }

运行结果如下:

16 序列化写入文件.jpg

序列化的时候,将文本转换了二进制格式,所以,我们在文件中打开的时候是一些我们识别不了的数字。这里面被存储的内容只有属性会被序列化存储。每个对象的属性是各不相同的。

ObjectInputStream

ObjectInputStream 可以将被序列化好的对象反序列化。常用方法有:

方法名 描述
public ObjectInputStream(InputStream in) throws IOException 构造输入函数
public final Object readObject() throws IOException, ClassNotFoundException 从指定的位置读取对象

案例

package com.haicoder.net.stream.object2stream; import java.io.*; public class Object2StreamTest { public static void main(String[] args) throws Exception { System.out.println("嗨客网(www.haicoder.net)"); File file = new File("/Users/haicoder/Documents/code/hai/Object2StreamTest.txt"); //如果文件不存在,会自动创建 InputStream inputStream = new FileInputStream(file); ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); Object object = objectInputStream.readObject(); objectInputStream.close(); System.out.println(object); } }

运行结果如下:

17 反序列化.jpg

我们看到,ObjectInputStream 将二进制编码文件里面的数据转换成了序列化之前的对象,我们重写了 Person 里面的 toString 方法。所以这边打印出 Object 的时候将属性里面对应的数据打印出来了。

transient关键字

Serializable 接口实现类的操作实际上是将一个对象中的全部属性进行类序列化,如果我们不希望一个对象里面的某个属性被序列化,可以使用 transient 关键字进行声明。

案例

定义一个类,然后在属性前面加上 transient 表示不序列化

package com.haicoder.net.stream.object2stream; import java.io.Serializable; public class Person implements Serializable { private transient String name; private Integer age; public Person() { } public Person(String name, Integer age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }

对对象进行序列化存储和读取

package com.haicoder.net.stream.object2stream; import java.io.*; public class Object2StreamTest { public static void main(String[] args) throws Exception { System.out.println("嗨客网(www.haicoder.net)"); File file = new File("/Users/haicoder/Documents/code/hai/Object2StreamTest.txt"); //如果文件不存在,会自动创建 //进行序列化 OutputStream out = new FileOutputStream(file); ObjectOutputStream objectOutputStream = new ObjectOutputStream(out); objectOutputStream.writeObject(new Person("嗨客网", 100)); objectOutputStream.close(); //进行反序列化 InputStream inputStream = new FileInputStream(file); ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); Object object = objectInputStream.readObject(); objectInputStream.close(); System.out.println(object); } }

运行结果如下:

18 属性不序列化.jpg

我们可以看到,对应的 name 虽然在序列化的时候,给名字赋值 嗨客网 ,但是在反序列化的时候,这个值没有了,为默认值 null。

Java序列化总结

序列化可以将一个对象存储结构变小,在数据传输上面会提升性能。ObjectOutputStream 可以将对象进行序列化,ObjectInputStream 将对象反序列化。