Java基础 第四节 第十八课
概述
Java 提供了一种对象序列化的机制. 用一个字节序列可以表示一个对象, 该字节序列包含该对象的数据, 对象的类型和对象中存储的属性等信息. 字节序列写出到文件之后, 相当于文件中持久保存了一个对象的信息.
反之, 该字节序列还可以从文件中读取回来. 重构对象, 对它进行反序列化. 对象的数据, 对象的类型和对象中存储的数据信息, 都可以用来在内存中创建对象. 看图理解序列化:
ObjectOutputStream 类
java.io.ObjectOutputStream
类, 将 Java 对象的原始数据类型写出到文件, 实现对象的持久存储.
构造方法
public ObjectOutputStream(OutputStream out)
: 创建一个指定 OutputStream 的 ObjectOutputStream.
构造举例, 代码如下:
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("a.txt");
ObjectOutputStream out = new ObjectOutputStream(fos);
}
- 1
- 2
- 3
- 4
序列化操作
一个想要序列化的对象, 必须满足两个条件:
- 该类必须实现
java.io.Serializable
接口, Serialzable 是一个标记接口. 不实现此接口的类将不会使任何状态序列化或反序列化, 会抛出 NotSerializableException - 该类的所有属性必须是可序列化的. 如果有一个属性不需要可序列化的, 则该属性必须注明是瞬态的, 使用 transient 关键字修饰
import java.io.Serializable;
public class Employee implements Serializable {
public String name;
public String address;
public transient int age; // transient 瞬态修饰成员, 不会被序列化
public void addressCheck() {
System.out.println("Address check: " + name + "--" + address);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
写出对象方法
public final void writeObject (Object obj):
将指定对象写出
- 1
- 2
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Test89 {
public static void main(String[] args) throws IOException {
Employee e = new Employee();
e.name = "我是小白";
e.address = "西伯利亚";
e.age = 20;
try (
FileOutputStream fos = new FileOutputStream("employee.txt");
// 创建序列化流对象
ObjectOutputStream out = new ObjectOutputStream(fos)
) {
// 写出对象
out.writeObject(e);
out.close();
// 姓名, 地址被序列化, 年龄没有被序列化
System.out.println("Serialized data is saved");
} catch (IOException exception) {
exception.printStackTrace();
}
}
}
输出结果:
Serialized data is saved
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
ObjectInputStream 类
ObjectInputSream 反序列化流, 将之前使用 ObjectOutputStream 序列化的原始数据恢复为对象.
构造方法
public ObjectInputStream(InputStream in)```:
创建一个指定 InputStream 的 ObjectInputStream
- 1
- 2
反序列化操作 (第一种)
如果能找到一个对象的 class 文件, 我们可以进行反序列化操作, 调用 ObjectInputStream 读取对象的方法.
public final Object readObject()
: 读取一个对象
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class Test {
public static void main(String[] args) throws IOException {
Employee e = null;
// 创建反序列化流
try (
FileInputStream fis = new FileInputStream("employee.txt");
ObjectInputStream in = new ObjectInputStream(fis)
){
// 读取一个对象
e = (Employee) in.readObject();
// 释放资源
in.close();
} catch (IOException | ClassNotFoundException i){
i.printStackTrace();
}
// 无异常, 直接打印输出
System.out.println("Name: " + e.name);
System.out.println("Address: " + e.address);
System.out.println("age: " + e.age);
}
}
输出结果:
Name: 我是小白
Address: 西伯利亚
age: 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
对于 JVM 可以反序列化对象, 它必须是能够找到 class 文件的类. 如果找不到该类的 class 文件, 则抛出一个 ClassNotFoundException 异常.
反序列化操作 (第二种)
另外, 当 JVM 反序列化对象时, 能找到 class 文件. 但是 class 文件在序列化对象之后发生了修改. 那么反序列化操作也会失败, 抛出一个 IncalidClassException 异常. 发生这个异常的原因如下:
- 该类的序列版本号与从流中读取的类描述符的版本号不匹配
Serializeble 接口给需要序列化的类, 提供了一个序列版本号. serialVersionUID 该版本号的目的在于验证序列化的对象和对应类是否版本匹配.
import java.io.Serializable;
public class Employee implements 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);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
练习: 序列化集合
- 将存有多个自定义对象的集合序列化操作,
案例分析
- 把若干学生对象, 保存到集合中
- 把集合序列化
- 反序列化读取时, 只需要读取一次, 转换为结合类型
- 遍历集合, 可以打印所有的学生信息
案例实现
Student 类
import java.io.Serializable;
public class Student implements Serializable{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
测试类
import java.io.*;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) throws Exception {
// 创建学生对象
Student s1 = new Student("小小白", 16);
Student s2 = new Student("小白", 17);
Student s3 = new Student("大白", 18);
ArrayList<Student> arrayList = new ArrayList<>();
arrayList.add(s1);
arrayList.add(s2);
arrayList.add(s3);
// 序列化操作
serialize(arrayList);
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("list.txt"));
// 读取对象, 强转为ArrayList类型
ArrayList<Student> list = (ArrayList<Student>)ois.readObject();
System.out.println(list.toString());
}
private static void serialize(ArrayList<Student> arrayList) throws Exception {
// 创建序列化流
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("list.txt"));
// 写出对象
out.writeObject(arrayList);
// 释放资源
out.close();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
文章来源: iamarookie.blog.csdn.net,作者:我是小白呀,版权归原作者所有,如需转载,请联系作者。
原文链接:iamarookie.blog.csdn.net/article/details/111659946
- 点赞
- 收藏
- 关注作者
评论(0)