序列化
概述
- Java提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。字节序列化写出到文件之后,相当于文件中持久保存了一个对象的信息。反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。
ObjectOutputStream类
-
java.io.ObjectOutputStream类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。
-
构造方法
-
ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream
-
构造举例,代码如下:
FileOutputStream fileOut = new FileOutputStream("a.txt"); ObjectOutputStream out = new ObjectOutputStream(fileOut);
-
-
序列化操作
-
一个对象要想序列化,必须满足两个条件:
-
该类必须实现java.ioSerializable接口,Serializable是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSeriableException。
-
该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient关键字修饰。
package com.io.objectstream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; /* java.io.ObjectOutputStream extends OutputStream ObjectOutputStream:对象的序列化流 作用:把对象以流的方式写入到文件中保存 构造方法: ObjectOutputStream(OutputStream out)创建一个写入指定的OutputStream的ObjectOutputStream。 参数: OutputStream out :字节输出流。 特有的成员方法: void writeObject(Object obj)将指定的对象写入 ObjectOutputStream。 使用步骤: 1.创建ObjectOutputStream对象,构造方法中传递字节输出流。 2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中 3.释放资源 注意: 序列化和反序列化的时候,会抛出NotSerializableException没有序列化异常 类通过实现java.io.Serializable接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。 Serializable接口也叫标记型接口 要进行序列化和反序列化的类必须实现Serializable接口,就会给类添加一个标记 当我们进行序列化和反序列化的时候,就会检测类上是否有这个标记 有:就可以序列化和反序列化 没有:就会抛出NotSerializableException异常 */ public class ObjectOutputstreamDemo01 { public static void main(String[] args) throws IOException { //1.创建ObjectOutputStream对象,构造方法中传递字节输出流。 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\abc\\person.txt")); //2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中 oos.writeObject(new Person("蛇皮",19));//NotSerializableException //3.释放资源 oos.close(); } }
-
ObjectInputStream类
-
ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。
-
构造方法
- ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjectInputStream。
-
反序列化操作1
-
如果能找到一个对象的class文件,我们可以进行反序列化操作,调用ObjectInputStream读取对象的方法:
public final Object readObject():读取一个对象。
package com.io.objectstream; import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; /* java.io.ObjectInputStream extends InputStream ObjectInputStream:对象的反序列化流 作用:把文件中保存的对象,以流的方式读取出来使用 构造方法: ObjectInputStream(InputStream in)创建从指定的InputStream读取的ObjectInputStream。 参数: InputStream in:字节输入流 特有的成员方法: Object readObject() 从ObjectInputStream读取对象。 使用步骤: 1.创建ObjectInputStream对象,构造方法中传递字节输入流 2.使用ObjectInputStream对象中的方法readObject读取保存对象中的文件 3.释放资源 4.使用读取处理的对象(打印) */ public class ObjectInputStreamDemo01 { public static void main(String[] args) throws IOException, ClassNotFoundException { // 1.创建ObjectInputStream对象,构造方法中传递字节输入流 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\abc\\person.txt")); //2.使用ObjectInputStream对象中的方法readObject读取保存对象中的文件 Object o = ois.readObject(); //3.释放资源 ois.close(); //4.使用读取处理的对象 System.out.println(o); } }
对于JVM可以反序列化对象,它必须是能找到class文件的类。如果找不到该类的class文件,则抛出一个ClassNotFoundException异常。
-
-
反序列化操作2
-
另外,当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常。发生这个异常的原因如下:
- 该类的序列化版本号与流中读取的类描述的版本号不匹配
- 该类包含未知数据类型
- 该类没有可访问的无参数构造方法
-
Serializable接口给需要序列化的类,提供了一个序列化版本号。serialVersionUID该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
private static final long serialVersionUID = 1L; private String name; private int age;
-
练习:序列化集合
-
将存有多个自定义对象的集合序列化操作,保存到list.txt文件中,反序列化list.txt,并遍历集合,打印对象信息。
-
代码实现:
package com.io.objectstream; import java.io.*; import java.util.ArrayList; /* 练习:序列化集合 分析: 1.定义一个存储Person对象的ArrayList集合 2.往ArrayList集合中存储Person对象 3.创建一个序列化流ObjectOutputStream对象 4.使用ObjectOutputStream对象中的方法writeObject,对集合进行序列化 5.创建一个反序列化ObjectInputStream对象 6.使用ObjectInputStream对象中的方法readObject读取文件中保存的集合 7.把Object类型的集合转换为ArrayList类型 8.遍历ArrayList集合 9.释放资源 */ public class TestDemo01 { public static void main(String[] args) throws IOException, ClassNotFoundException { //1.定义一个存储Person对象的ArrayList集合 ArrayList<Person> list = new ArrayList<>(); //2.往ArrayList集合中存储Person对象 list.add(new Person("唐蛇皮",21)); list.add(new Person("菜虚困",20)); list.add(new Person("药羊",22)); //3.创建一个序列化流ObjectOutputStream对象 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\abc\\list.txt")); //4.使用ObjectOutputStream对象中的方法writeObject,对集合进行序列化 oos.writeObject(list); //5.创建一个反序列化ObjectInputStream对象 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\abc\\list.txt")); //6.使用ObjectInputStream对象中的方法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(); } }