Java 序列化与反序列化
Java序列化把一个对象
Java Object
变为一个二进制字节序列
byte[]
Java反序列化就是把一个二进制字节序列
byte[]
变为Java对象
Java Object
序列化API
ObjectOutputStream
字节输出流对象,将对象的输出流写到文件中(结合FileOutputStream使用)
实例:
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("1.txt"));
或
FileOutputStream fout = new FileOutputStream("1.txt");
ObjectOutputStream out = new ObjectOutputStream(fout);
此外ObjectOutputStream还提供了
writeObject()
方法来序列化一个对象,并将它发送到输出流。
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("1.txt"));
out.writeObject(new Test("Sentiment",10)); //对Test类中的构造方法进行传参
ObjectInputStream
字节输入流对象,将文件中的二进制字节序列进行反序列化操作(结合FileInputStream)
实例:
ObjectInputStream in = new ObjectInputStream(new FileInputStream("1.txt"));
或
FileInputStream fin = new FileInputStream("1.txt");
ObjectInputStream oin = new ObjectInputStream(fin);
此外ObjectInputStream还提供
readObject()
方法从流中取出下一个对象,并将对象反序列化。它的返回值为Object,因此,需要将它转换成合适的数据类型。
ObjectInputStream In = new ObjectInputStream(new FileInputStream("1.txt"));
Test o = (Test)In.readObject();
System.out.println(o); //读取后输出
实例理解
Test.java(类想要进行序列化操作,必须实现Serializable接口继承)
package Sentiment.unserialize.demo01;
import java.io.Serializable;
public class Test implements Serializable {
private String name;
private int age;
public Test() {
}
public Test(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Test{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
Serializable.java
package Sentiment.unserialize.demo01;
import java.io.*;
public class Serializable {
public static void main(String[] args) throws IOException {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("1.txt"));
out.writeObject(new Test("Sentiment",10));
out.close();
}
}
UnSerializable.java
package Sentiment.unserialize.demo01;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class UnSerializable {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream In = new ObjectInputStream(new FileInputStream("1.txt"));
Test o = (Test)In.readObject();
System.out.println(o);
}
}
运行Serializable.java后便得到二进制字节码文件内容
、
此时在运行UnSerializable.java便得到了二进制字节码反序列化后的内容
serialVersionUID
- 每个可序列化的类在序列化时都会关联一个版本号 , 这个版本号就是 serialVersionUID 属性
- serialVersionUID 属性必须通过 static final long 修饰符来修饰 。
- 如果可序列化的类未声明 serialVersionUID 属性 , 则 Java 序列化时会根据类的各种信息来计算默认的 serialVersionUID 值 . 但是 Oracle 官方文档强烈建议所有可序列化的类都显示声明 serialVersionUID 值 .
实例理解
在Test.java中定义
serialVersionUID
其值为1
package Sentiment.unserialize.demo01;
import java.io.Serializable;
public class Test implements Serializable {
public static final long serialVersionUID = 1L; //定义serialVersionUID
private String name;
private int age;
public Test() {
}
public Test(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Test{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
此时运行Serializable.java进行序列化操作
运行完毕后将
serialVersionUID
的值定义为2—>public static final long serialVersionUID = 2L;,在运行UnSerializable.java会报错
Transient (瞬态变量)
Transient( 瞬态变量 )是一个 Java 关键词 , 它用于标记类的成员变量在持久化到字节流时不要被序列化 ; 在通过网络套接字流传输字节流时 , transient 关键词标记的成员变量不会被序列化 。此为被static修饰的静态变量也不参与序列化操作。
实例理解
将Test.java中的
name
和
age
变量前分别加上
transient
和
static
package Sentiment.unserialize.demo01;
import java.io.Serializable;
public class Test implements Serializable {
public static final long serialVersionUID = 1L; //定义serialVersionUID
private transient String name; //加上transient
private static int age; //加上static
public Test() {
}
public Test(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Test{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
此时在执行Serializable.java和UnSerializable.java,输出结果:Sentiment—>null,10—>0
readObject()方法重写
官方允许用户在被序列化的类中重写 readObject() 方法 , 重写后的方法将负责在反序列化时重构当前类对象 . 用户只需要在重写的 readObject() 方法中实现 defaultReadObject() 方法 , 就可以确保反序列化过程正常执行 .
实例理解
实现 defaultReadObject() 方法调用,并在Test类中重写该方法,在进行序列化和反序列化操作即可执行命令
private void readObject(java.io.ObjectInputStream a) throws IOException, ClassNotFoundException{
a.defaultReadObject();
Runtime.getRuntime().exec("calc.exe");
}
总结
- 类想要进行序列化操作,必须实现Serializable接口继承
- 强烈建议定义serialVersionUID ,序列化、反序列化前后不要修改其值
- 被static、Transient修饰的属性,不会进行序列化操作
- readObject()方法重写时,需要实现defaultReadObject() 方法