概念
把对象以流的方式,写入到文件中保存,这种方式称为对象的序列化。
把文件中保存的对象,以流的方式读取出来,这种方式称为对象的反序列化。
序列化流: ObjectOutputStream
-
构造方法:
– ObjectOutputStream(OutputStream out):参数 OutputStream为字节输出流 -
特有的成员方法:
– void writeObject(Object obj) 将指定的对象写入文件中 -
将员工对象信息通过序列化流写入文件的例子:
// 员工类支持序列化
class Employee implements Serializable{
//职员id
private int id;
//职员姓名
private String name;
//职员性别
private String sex;
//职员年龄
private int age;
//职员职位
private String position;
//入职日期
private Date hireDate;
//当前薪水
private BigDecimal salary;
public Employee(int id, String name, String sex, int age, String position, Date hireDate, BigDecimal salary) {
this.id = id;
this.name = name;
this.sex = sex;
this.age = age;
this.position = position;
this.hireDate = hireDate;
this.salary = salary;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
", position='" + position + '\'' +
", hireDate=" + hireDate +
", salary=" + salary +
'}';
}
}
/**
* 对象的序列化流
*
* @author zhuhuix
* @date 2020-06-26
*/
public class ObjectOutputStreamDemo {
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("c:\\employee.dat");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
Employee employee = new Employee(1, "Will", "男", 24, "组员",
new Date(), BigDecimal.valueOf(20000));
objectOutputStream.writeObject(employee);
objectOutputStream.close();
}
}
反序列化流: ObjectInputStream
-
构造方法:
– ObjectInputStream(InputStream out):参数 InputStream 为字节输入流 -
特有的成员方法:
– Object readObject() 将文件中的对象读取到流中 -
通过反序列化流从文件中读取员工对象信息的例子:
/**
* 对象的序列化流
*
* @author zhuhuix
* @date 2020-06-26
*/
public class ObjectInputStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileInputStream fileInputStream = new FileInputStream("c:\\employee.dat");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
Employee employee = (Employee) objectInputStream.readObject();
System.out.println(employee.toString());
objectInputStream.close();
}
}
transient关键字
被transient的成员变量不能被序列化,即如果用transient声明一个变量,当对象存储时,它的值不需要维持。
- 修改一下员工类,将职员职位、入职日期、薪水用transient关键字修饰
// 员工类支持序列化
class Employee implements Serializable {
//职员id
private int id;
//职员姓名
private String name;
//职员性别
private String sex;
//职员年龄
private int age;
//职员职位
private transient String position;
//入职日期
private transient Date hireDate;
//当前薪水
private transient BigDecimal salary;
public Employee(int id, String name, String sex, int age, String position, Date hireDate, BigDecimal salary) {
this.id = id;
this.name = name;
this.sex = sex;
this.age = age;
this.position = position;
this.hireDate = hireDate;
this.salary = salary;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
", position='" + position + '\'' +
", hireDate=" + hireDate +
", salary=" + salary +
'}';
}
}
- 再次运行序列化保存与反序列化读取的程序,发现用transient关键字修饰的对象属性已不保存数据,读取的信息为空。
InvalidClassException
如果类实现了Serializable接口,java编译器在把java文件编译生成class文件时,就会根据类的定义,在class文件中自动添加一个序列号(serialVersionUID)。
如果后续我们修改了原有类的属性,比如增加了属性及方法等,则在重新编译时,java编译器会在编译完成的class文件中生成一个新的序列号,这会造成新的程序在反序列化旧版本的文件时,抛出InvalidClassException异常。
- 解决办法:在类文件中定义一个不变的序列号,使得新旧版本能够兼容。
// 员工类支持序列化
class Employee implements Serializable {
// 人为指定一个序列化号
private final static long serialVersionUID=1L;
//职员id
private int id;
//职员姓名
private String name;
//职员性别
private String sex;
//职员年龄
private int age;
//职员职位
private transient String position;
//入职日期
private transient Date hireDate;
//当前薪水
private transient BigDecimal salary;
...
}
实际案例
- 定义一个存储Employee对象的集合;
- 在集合中写入多个Employee对象;
- 创建一个序列化流;
- 使用序列化流的写入方法,将集合进行序列化生成对象的文件。
- 创建一个反序化流;
- 通过反序列化流读取文件中保存的集合;
- 遍历打印集合数据。
/**
* 通过序列化流与反序列化流保存与读取员工列表
*
* @author zhuhuix
* @date 2020-06-26
*/
public class EmployeeList {
public static int id = 0;
public static void main(String[] args) throws IOException,ClassNotFoundException {
List<Employee> employeeList = new ArrayList<>();
employeeList.add(new Employee(id++, "Mike", "男", 35, "总经理",
new Date(), BigDecimal.valueOf(100000)));
employeeList.add(new Employee(id++, "Tom", "男", 34, "副总经理",
new Date(), BigDecimal.valueOf(60000)));
employeeList.add(new Employee(id++, "Jack", "男", 30, "研发部主管",
new Date(), BigDecimal.valueOf(40000)));
employeeList.add(new Employee(id++, "Kate", "女", 26, "组员",
new Date(), BigDecimal.valueOf(20000)));
employeeList.add(new Employee(id++, "Will", "男", 24, "组员",
new Date(), BigDecimal.valueOf(20000)));
employeeList.add(new Employee(id++, "Jerry", "男", 28, "产品部主管",
new Date(), BigDecimal.valueOf(40000)));
employeeList.add(new Employee(id++, "Merry", "女", 28, "组员",
new Date(), BigDecimal.valueOf(20000)));
employeeList.add(new Employee(id++, "Leo", "男", 27, "组员",
new Date(), BigDecimal.valueOf(20000)));
employeeList.add(new Employee(id++, "Rose", "女", 29, "市场部主管",
new Date(), BigDecimal.valueOf(40000)));
employeeList.add(new Employee(id++, "Amy", "", 25, "组员",
new Date(), BigDecimal.valueOf(20000)));
employeeList.add(new Employee(id++, "Tony", "男", 23, "组员",
new Date(), BigDecimal.valueOf(20000)));
// 将集合通过序列化流写入到文件中
FileOutputStream fileOutputStream = new FileOutputStream("c:\\employeeList.dat");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(employeeList);
objectOutputStream.close();
// 将文件中集合通过反序列化流读取到集合中
FileInputStream fileInputStream = new FileInputStream("c:\\employeeList.dat");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
ArrayList<Employee> arrayList = (ArrayList<Employee>) objectInputStream.readObject();
for (int i = 0; i < arrayList.size(); i++) {
System.out.println(arrayList.get(i).toString());
}
objectInputStream.close();
}
}
- 读取打印信息