序列化和反序列化
组成对象的各个信息可以被保存于数据库或者文件中,对象含其它对象又需要保存被引用对象的各个信息,如此反复,然后再创建出对象,编程复杂。java作为面向对象的编程语言,提供了序列化和反序列化的机制,可以很方便解决这个问题—对象持久化。
1、什么是序列化和反序列化
- 对象序列化:将对象转换为字节序列的过程
- 对象反序列化:将字节序列转换为对象的过程
2、应用场景
将序列化的结果保存在磁盘或者在网络传输
3、序列化的范围
对象序列化能够自动保存对象网的基本类型变量和所有可以序列化的对象,静态变量和关键字transient修饰的成员,不会被序列化
4、序列化和反序列化的API使用
package com.xuliehua; import java.io.Serializable; public class Car implements Serializable{ private String name; private int number; private Person person; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } @Override public String toString() { return "Car{" + "name='" + name + '\'' + ", number=" + number + ", person=" + person + '}'+super.toString(); } }
package com.xuliehua; import java.io.Serializable; public class Person implements Serializable{ private String name; public Person(String name){ this.name=name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
package com.xuliehua; import org.junit.Test; import java.io.*; public class Main { public static void main(String[] args) throws Exception { ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream("test.txt")); Car car=new Car(); car.setName("baoMa"); car.setNumber(666888); car.setPerson(new Person("xiaoMing")); objectOutputStream.writeObject(car); objectOutputStream.writeObject(car); System.out.println(car); ObjectOutputStream objectOutputStream3=new ObjectOutputStream(new FileOutputStream("test2.txt")); objectOutputStream3.writeObject(car); ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream("test.txt")); ObjectInputStream objectInputStream3=new ObjectInputStream(new FileInputStream("test2.txt")); Car car1=(Car)objectInputStream.readObject(); Car car2=(Car)objectInputStream.readObject(); Car car3=(Car)objectInputStream3.readObject(); System.out.println(car1); System.out.println(car2); System.out.println(car3); } }
main函数的执行结果:
序列化的对象:Car{name='baoMa', number=666888, person=com.xuliehua.Person@4079ca2e}com.xuliehua.Car@3ab0f534 Test.txt中反序列化的对象:Car{name='baoMa', number=666888, person=com.xuliehua.Person@699a3d76}com.xuliehua.Car@d394424 Test.txt中反序列化的对象:Car{name='baoMa', number=666888, person=com.xuliehua.Person@699a3d76}com.xuliehua.Car@d394424 Test2.txt中反序列化的对象:Car{name='baoMa', number=666888, person=com.xuliehua.Person@2aa89e44}com.xuliehua.Car@242e983d
注意:
- 对象网中想被序列化的对象必须实现Serializable接口,比如Car或者Person没有实现Serializable,则会抛出异常:java.io.NotSerializableException
- 将一个对象分两次序列化到同一个文件,反序列化两次得到的是同一个对象(地址一致),对象网中的其它对象也是同一个;将一个对象序列化到两个文件,分别从两个文件反序列化得到的是不同的对象(地址不一致),对象网中的其它对象也不是同一个;
5、序列化类中的版本号
在实现Serializable接口的类没有提供版本号serialVersionUID,则java编译器会根据类的细节为编译后得到的字节序列一个版本号。
- 修改了类的细节得到的编译器会产生的不一样的版本号。当已经序列化一个类后,再未反序列化时,修改了类的细节,此时再反序列化,会抛异常:
java.io.InvalidClassException: com.xuliehua.Car;
local class incompatible: stream classdesc serialVersionUID = 2968258898423114756,
local class serialVersionUID = 2942880067466519035
序列化后,修改Person或者Car中的类细节:
package com.xuliehua; import java.io.Serializable; public class Person implements Serializable{ private String name; public int age;//添加一个属性 public Person(String name){ this.name=name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
再反序列化会出现异常:java.io.InvalidClassException
- 避免方案:在实现Serializable接口的类提供版本号serialVersionUID,那么类细节改变了,版本号不会变。在序列化和反序列化之间无论类细节怎样变化,不会有影响。