序列化
序列化
3.1 概述
Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据
、对象的类型
和对象中存储的属性
等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据
、对象的类型
和对象中存储的数据
信息,都可以用来在内存中创建对象。看图理解序列化
3.2 ObjectOutputStream类
java.io.ObjectOutputStream
类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。
构造方法
public ObjectOutputStream(OutputStream out)
: 创建一个指定OutputStream的ObjectOutputStream。- 参数OutputStream out:字节输出流
特有的成员方法
void writeObject(Object obj) 将指定的对象写入 ObjectOutputStream
使用步骤
- 创建ObjectOutputStream对象,构造方法中传递字节输出流
- 使用ObjectStream对象中的方法writeObject,把对象中写入到文件中
- 释放资源
序列化操作
- 一个对象要想序列化,必须满足两个条件:
- 该类必须实现
java.io.Serializable
接口,Serializable
是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
。 - 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用
transient
关键字修饰。
public class Person implements Serializable {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + 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;
}
}
测试类
public static void main(String[] args) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:\\Test\\01.txt"));
oos.writeObject(new Person("雷神",33));
oos.close();
}
3.3 ObjectInputStream类
ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。
java.io.ObjectInputStream extends InputStream
ObjectInputStream:对象的反序列化流
作用:把文件中保存的对象,以流的方式读取出来
构造方法
public ObjectInputStream(InputStream in)
: 创建一个指定InputStream的ObjectInputStream。- 参数
- InputStream in:字节输入流
特有的成员方法
Object readObject() 从 ObjectInputStream 读取对象
使用步骤
- 创建ObjectInputStream对象,构造方法中传递字节输入流
- 使用ObjectInputStream对象中的方法readObject读取保存对象中的文件
- 释放资源
- 使用读取出来的对象(打印)
注意
readObject方法声明抛出了ClassNotFoundExeption(class文件找不到异常)
当不存在对象的class文件时抛出此异常
反序列化的前提:
- 类必须实现Serializable
- 必须存在类对应的class文件
示例代码
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:\\Test\\01.txt"));
Object o = ois.readObject();
ois.close();
System.out.println(o);
Person p = (Person)o;
System.out.println(p.getName()+p.getAge());
}
瞬态关键字
transient关键字:瞬态关键字
被transient修饰的成员变量,不能被序列化
如 private transient int age;
static关键字:静态关键字
静态优先于非静态加载到内存中(静态优先于对象进入到内存中)
被static修饰的成员变量同样不能被序列化,序列化的都是对象
反序列化操作2
序列号冲突的异常
另外,当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException
异常。发生这个异常的原因如下:
- 该类的序列版本号与从流中读取的类描述符的版本号不匹配
- 该类包含未知数据类型
- 该类没有可访问的无参数构造方法
Serializable
接口给需要序列化的类,提供了一个序列版本号。serialVersionUID
该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
public class Employee implements java.io.Serializable {
// 加入序列版本号
private static final long serialVersionUID = 1L;
public String name;
public String address;
// 添加新的属性 ,重新编译, 可以反序列化,该属性赋为默认值.
public int eid;
public void addressCheck() {
System.out.println("Address check : " + name + " -- " + address);
}
}
图解
3.4 练习:序列化集合
- 将存有多个自定义对象的集合序列化操作,保存到
list.txt
文件中。 - 反序列化
list.txt
,并遍历集合,打印对象信息。
案例分析
- 定义一个存储Person对象的ArrayList集合
- 往ArrayList集合中存储Person对象
- 创建一个序列化流ObjectOutputStream对象
- 使用ObjectOutputStream对象中的方法WriteObject,对集合进行序列化
- 创建一个反序列化ObjectInputStream对象
- 使用ObjectInputSteam对象中的方法readObject读取文件中保存的集合
- 把Object类型的接合转换为ArrayList类型
- 遍历ArrayList集合
- 释放资源
示例代码
public static void main(String[] args) throws IOException, ClassNotFoundException {
ArrayList<Person> list = new ArrayList<>();
list.add(new Person("张三",18));
list.add(new Person("李四",19));
list.add(new Person("王一",28));
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:\\Test\\list.txt"));
// 4. 使用ObjectOutputStream对象中的方法WriteObject,对集合进行序列化
oos.writeObject(list);
// * 5. 创建一个反序列化ObjectInputStream对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:\\Test\\list.txt"));
// * 6. 使用ObjectInputSteam对象中的方法readObject读取文件中保存的集合
Object o = ois.readObject();
// * 7. 把Object类型的接合转换为ArrayList类型
ArrayList<Person> list2 = (ArrayList<Person>)o;
// * 8. 遍历ArrayList集合
for (Person p : list2) {
System.out.println(p);
}
// * 9. 释放资源
ois.close();
oos.close();
}