在网络通信中,任何数据都是以二进制的形式来传输的,而对象序列化可以把内存中的Java对象转成二进制流,而二进制流可以存放在本地磁盘文件中,通过网络或者程序来获取该二进制流后,都能将该二进制流恢复成Java对象。序列化的这一过程就是将对象状态信息转换为可存储或传输的过程。
对象序列化的作用:
- 对象序列化可以把对象以字节序列的形式永久保存在磁盘中。
- 对象序列化可以用于在网络中传输对象。
- 对象序列化使得对象可以脱离程序的运行而独立存在。
序列化与反序列化:
序列化是将一个Java对象写入IO流中,转换成字节序列,而反序列化是用IO流将字节序列恢复成Java对象。
如果要让对象序列化,则该对象的类必须实现接口 serializable, 该接口没有任何方法,仅作标记,用于代表该类可以实现序列化。
如果序列化的对象类型是别的类,则该类也要实现序列化接口,保证序列化过程中用到的类都要实现序列化,不然会出现异常。
如果要让对象序列化,则该对象的类必须实现接口 serializable, 该接口没有任何方法,仅作标记,用于代表该类可以实现序列化。
如果序列化的对象类型是别的类,则该类也要实现序列化接口,保证序列化过程中用到的类都要实现序列化,不然会出现异常。
对象序列化需要的IO流:
- ObjectOuputStream:该输出流是处理流,需要节点流作为参数。使用该流的writeObject()将一个对象序列化。
- ObjectInputStream:该输入流是处理流,需要一个节点输入流作为参数,使用readObjec()把读取的字节序列(二进制数据)恢复成Java对象。
对象序列化的过程:
- 使用ObjectOuputStream,
- 调用ObjectOuputStream中的writeObject()把对象转换成字节序列
- 代码演示对象序列化:
public class Person implements Serializable{
private String name;
private int age;
private String sex;
public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}/**省略setter 和 getter*/
}
//把对象写入序列化文件
public class Test {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("D:\\Object01.txt");//存放对象字节序列的文件路径
//对象输出流中的节点流是一个缓冲流,该流包装节点流,所以需要flush()
ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(fos));
Person p = new Person("张三", 15, "男");
oos.writeObject(p);
oos.flush();
}
}
对象反序列化的过程:
- 使用ObjectInputStream
- 调用ObjectInputStream的readObject()方法读取流中的对象
- 代码演示反序列化:
FileInputStream fis = new FileInputStream("D:\\Object01.txt");
//创建一个ObjectInputStream输入流,节点流数据源是一个对象序列化文件
ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(fis));
//读取Person对象
Person p = (Person)ois.readObject();
System.out.println(p.getName() + "||" + p.getAge() +"||" + p.getSex());
反序列化简单小结:
- 反序列化读取的数据不是一个Java类,所以使用反序列化恢复Java对象后,需要保证该类存在,不然Java对象是没存在意义的。
- 还有反序列化的对象没有使用构造器来初始化。可以自己往构造器中加入一条提示语句,运行该对象时查看是否运行。
- 如果我们读取的二进制文件中存在多个对象,则必须按照他们序列化的顺序来读取,不然会出错。
- 如果成员变量使用了transient修饰,那么将不会把该成员变量数据序列化。
测试序列化多个对象:
public class Person implements Serializable{
private String name;
private int age;
private transient String sex; //透明化,表示该成员变量不会序列化
public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
/** 省略getter 和 setter */
}
public class Test {
public static void main(String[] args) throws Exception {
//存放对象字节序列的文件路径
FileOutputStream fos = new FileOutputStream("D:\\MoreObject.txt");
//对节点流使用了缓冲流,所以需要flush()
ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(fos));
Person p = new Person("张三", 15, "男");
Person p1 = new Person("李四", 20, "男");
Person p2 = new Person("王五", 25, "男");
//序列化多个对象
oos.writeObject(p);
oos.writeObject(p1);
oos.writeObject(p2);
oos.flush();
//创建一个对象输入流ObjectInputStream
FileInputStream fis = new FileInputStream("D:\\MoreObject.txt");
ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(fis));
//恢复多个对象
Person per1 = (Person) ois.readObject();
Person per2 = (Person) ois.readObject();
Person per3 = (Person) ois.readObject();
// Person per4 = (Person) ois.readObject(); //EOFException
System.out.println(per1.getName() + "||" + per1.getAge() +"||" + per1.getSex());
System.out.println(per2.getName() + "||" + per2.getAge() +"||" + per2.getSex());
System.out.println(per3.getName() + "||" + per3.getAge() +"||" + per3.getSex());
// 输出结果:
// 张三||15||null
// 李四||20||null
// 王五||25||null
}
}
- 实例解析:
- 被transient修饰的成员变量sex,无法对数据序列化,所以输出结果中是null,因为该值是引用类型,如果是值类型就是系统默认的初始值。
- 恢复的对象顺序是和序列化对象一致的,这是因为反序列化文件有多个对象,恢复的顺序也是按照对象序列化时的顺序来恢复的。
- 如果序列化文件中的对象已经读取完了,再次readObject()会引发EOFException异常。