对象序列化
对象序列化的目标是将对象保存到磁盘中,或允许在网络中直接传输对象。对象序列化机制允许把内存中的 Java 对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,通过网络将这种二进制流传输到另1个网络节点。其他程序一旦获得了这种二进制流(无论是从磁盘中获取的,还是通过网络获取的) ,都可以将这种二进制流恢复成原来的 Java 对象
如何来使用对象序列化?
前提:
首先,一个类要被序列化,那么它必须实现如下两个接口之一:
- Serializable
- Extemalizable
实现着两个接口的作用,其实就是就是标记一个对象是否可以被序列化。所以比如我们有一个类需要被序列化,我们实现Serializable接口就行,无需实现任何方法。
序列化工具类:ObjectOutputStream
方法:
- writeObject()
反序列化工具类:objectInputStream
方法:
- readObject()
示例:存储Person类对象
import java.io.*;
/**
* @ClassName ObjectOutputStreamWriteObjectExample
* @projectName: object1
* @author: Zhangmingda
* @description: Person 类实现序列化接口
* date: 2021/4/18.
*/
public class ObjectOutputStreamWriteObjectExample {
//实现接口Serializable可以序列化
private static class Person implements Serializable {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//序列化方法
private static void writeObject(Person person,String dstPath) {
try(ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(dstPath))){
objectOutputStream.writeObject(person);
} catch (IOException e) {
e.printStackTrace();
}
}
//反序列化方法
private static<T> void readObject(String srcPath){
try(ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(srcPath))){
T person = (T) objectInputStream.readObject();
System.out.println(person.getClass());
System.out.println(person);
/**
* class ObjectOutputStreamWriteObjectExample$Person
* Person{name='钱老大', age=30}
*/
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Person person = new Person("钱老大",30);
String dstPath = "输入输出文件读写/src/test/output/object-save.txt";
writeObject(person,dstPath);
String srcPath = "输入输出文件读写/src/test/output/object-save.txt";
readObject(srcPath);
}
}
示例:带有引用变量的对象序列化
引用类型也必须是可以序列化的。
import java.io.*;
/**
* @ClassName ObjectOutputStreamWriteObjectExample
* @projectName: object1
* @author: Zhangmingda
* @description: Person 类实现序列化接口
* date: 2021/4/18.
*/
public class ObjectOutputStreamWriteObjectExample {
//实现接口Serializable可以序列化
private static class Person implements Serializable {
String name;
int age;
Job job;
public Person(String name, int age, Job job) {
this.name = name;
this.age = age;
this.job = job;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", job=" + job.getName() +
'}';
}
}
private static class Job implements Serializable {
private String name;
public Job(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
//序列化方法
private static void writeObject(Person person,String dstPath) {
try(ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(dstPath))){
objectOutputStream.writeObject(person);
} catch (IOException e) {
e.printStackTrace();
}
}
//反序列化方法
private static<T> void readObject(String srcPath){
try(ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(srcPath))){
T person = (T) objectInputStream.readObject();
System.out.println(person.getClass());
System.out.println(person);
/**
* class ObjectOutputStreamWriteObjectExample$Person
* Person{name='钱老大', age=30, job=教师}
*/
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Person person = new Person("钱老大",30,new Job("教师"));
String dstPath = "输入输出文件读写/src/test/output/object-save.txt";
writeObject(person,dstPath);
String srcPath = "输入输出文件读写/src/test/output/object-save.txt";
readObject(srcPath);
}
}
指定哪些字段不需要序列化
比如上面的Person对象,可能有的女生认为年龄是一个秘密,不应该被序列化,怎么办?
用transient关键字修饰对应的字段就OK了
示例:private transient
存入:Person{name='钱老大', age=30, job=教师}
读取:Person{name='钱老大', age=0, job=教师} 【不被反序列化的字段置为 对应数据类型默认值】
//序列化写入到文件时transient 未设置,读取时添加了transient,等于对象的类在写入后,读取之前又发生了变化,然后反序列化读取发生
错误类似:local class incompatible: stream classdesc serialVersionUID ......
解决:类在创建时 添加serialVersionUID字段来保证类发生变化后,也能反序列化成功(不被反序列化的字段置为 对应数据类型默认值)
即:类的升级
import java.io.*;
/**
* @ClassName ObjectOutputStreamWriteObjectExample
* @projectName: object1
* @author: Zhangmingda
* @description: Person 类实现序列化接口
* date: 2021/4/18.
*/
public class ObjectOutputStreamWriteObjectExample {
//实现接口Serializable可以序列化
private static class Person implements Serializable {
private static final long serialVersionUID = 769204331117184540L;
private String name;
private transient int age;
private Job job;
public Person(String name, int age, Job job) {
this.name = name;
this.age = age;
this.job = job;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", job=" + job.getName() +
'}';
}
}
private static class Job implements Serializable {
private String name;
public Job(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
//序列化方法
private static void writeObject(Person person,String dstPath) {
try(ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(dstPath))){
objectOutputStream.writeObject(person);
} catch (IOException e) {
e.printStackTrace();
}
}
//反序列化方法
private static<T> void readObject(String srcPath){
try(ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(srcPath))){
T person = (T) objectInputStream.readObject();
System.out.println(person.getClass());
System.out.println(person);
/**
* class ObjectOutputStreamWriteObjectExample$Person
* Person{name='钱老大', age=30, job=教师}
*/
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// Person person = new Person("钱老大",30,new Job("教师"));
// String dstPath = "输入输出文件读写/src/test/output/object-save.txt";
// writeObject(person,dstPath);
String srcPath = "输入输出文件读写/src/test/output/object-save.txt";
readObject(srcPath);
}
}