Java 对象的序列化就是把一个对象变为二进制的数据流的一种方法。它用一个字节序列表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。
反序列化表示将字节序列转换为对应的对象。通过序列化可以将对象大小压缩,减少对象大小,方便对象数据传输或者存储。
如果一个类的对象想被序列化,那么这个类就必须要实现 Serializable 接口。该接口没有什么特殊的方法,它就是一个标识接口,表示一个类具备了被序列化的能力。
如果一个类实现了 Serializable 接口表示该类创建的对象是可以经过二进制数据流进行传输的。如果想让二进制流进行传输时需要借助对象输出流(ObjectOutputStream)和对象输入流(ObjectInputStream)。
对象的序列化和反序列化存在版本问题,如果序列化的 JDK 版本和反序列化的 JDK 版本不一致就可能造成异常。在进行反序列化的时候,JVM 会把传递过来的字节流中的 serialVersionUID 与本地相应的实体的 serialVersionUID 进行比较。如果相同就认为是一致的,如果不相同就抛出异常。
一个对象如果要进行输出,就必须要使用 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();
}
}
运行结果如下:
序列化的时候,将文本转换了二进制格式,所以,我们在文件中打开的时候是一些我们识别不了的数字。这里面被存储的内容只有属性会被序列化存储。每个对象的属性是各不相同的。
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);
}
}
运行结果如下:
我们看到,ObjectInputStream 将二进制编码文件里面的数据转换成了序列化之前的对象,我们重写了 Person 里面的 toString 方法。所以这边打印出 Object 的时候将属性里面对应的数据打印出来了。
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);
}
}
运行结果如下:
我们可以看到,对应的 name 虽然在序列化的时候,给名字赋值 嗨客网 ,但是在反序列化的时候,这个值没有了,为默认值 null。
序列化可以将一个对象存储结构变小,在数据传输上面会提升性能。ObjectOutputStream 可以将对象进行序列化,ObjectInputStream 将对象反序列化。