Java序列化和反序列化与transient关键字

当我们需要把Java对象保存在磁盘中或者要在网络中传播,那我们就要用到Java对象的序列化和反序列化。对象序列化把内存中的对象转换成跟平台无关的二进制流,从而可以永久的保存在磁盘中或者在网络中传播。这在现实开发中经常用到,如:web容器把Session保存在磁盘中,减少内存的占用,等需要时,再反序列化。还有分布部署程序时,我们用到的RPC远程访问别人方法返回对象时等等。

实现序列化
我们只要实现Serializable接口就行,当然Java已经帮我们封装好了实现类,所以Serializable接口是一个标记接口,我们不用实现任何方法,它就是用来表明这个类对象是可序列化的。如:

 
  1. public class dog implements Serializable {
  2. private static final long serialVersionUID = 1L;//版本号,反序列化那里会说到
  3. private String name;
  4. private String age;
  5. public dog(String name, String age) {
  6. this.name = name;
  7. this.age = age;
  8. System.out.println("调用了构造方法");
  9. }
  10. public String getName() {
  11. return name;
  12. }
  13. public void setName(String name) {
  14. this.name = name;
  15. }
  16. public String getAge() {
  17. return age;
  18. }
  19. public void setAge(String age) {
  20. this.age = age;
  21. }
  22. }

使用对象流实现序列化

1.创建一个ObjectOutputStream,这个是对象处理流,所以必须建立在其他文件流上(FileOutputStream)

 
  1. ObjectOutputStream outputStream=new ObjectOutputStream(new FileOutputStream("Object.txt"));

2.然后用ObjectOutputStream的writeObject(Object object)方法就可以输出序列化对象。

 
  1. dog d=new dog("小狗","12");
  2. outputStream.writeObject(d);

使用对象流实现反序列化
1.创建一个ObjectInputStream,这个是对象处理流,所以必须建立在其他文件流上(FileInputStream)

 
  1. ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("Object.txt"));

2然后用创建一个ObjectInputStream的raadObject()方法就可以输入序列化对象

 
  1. ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("Object.txt"));
  2. dog dd=(dog)inputStream.readObject();

readObject返回的是Object,我们知道类型可以强制转换。

注意:由于我们读取到的是对象,所以我们必须要有java对象的class文件,而且我们知道dog类只有一个有参构造方法,这个构造方法里有输出语句,但是我们获得对象时,并没有输出,所以我们可以得出反序列化时,我们不用构造方法来初始化对象

对象引用的序列化
当你要序列化的对象有(除了基本类型和String类型外)引用其他对象时,这个对象必须也是要序列化的。

 
  1. public class Cat implements Serializable {
  2. private dog d;
  3. private String name;
  4. public Cat(dog d, String name) {
  5. this.d = d;
  6. this.name = name;
  7. }
  8. public dog getD() {
  9. return d;
  10. }
  11. public void setD(dog d) {
  12. this.d = d;
  13. }
  14. public String getName() {
  15. return name;
  16. }
  17. public void setName(String name) {
  18. this.name = name;
  19. }
  20. }

Cat类有一个dog对象,只有dog对象是可序列化的,Cat类对象才可以序列化,因为当你序列化Cat类对象时,由于dog对象不可序列化,所以在反序列化时为了正常返回Cat对象,要对dog对象进行反序列化,所以dog对象要是可序列化的。当然我们可以用transient关键字进行指定哪些不用序列化,后面会说到。

序列号
当你对同一个对象序列化多次,反序列化回来的多个对象怎么保证是同一个呢,所以就有了序列号。

 
  1. ObjectOutputStream outputStream=new ObjectOutputStream(new FileOutputStream("Object.txt"));
  2. dog d=new dog("小狗","12");
  3. outputStream.writeObject(d);
  4. outputStream.writeObject(d);

我们对d这个对象序列化3次,正常每次序列化都会把d这对象写入到磁盘中,就有了3个d对象在磁盘中,当你反序列化时就有了3个不同的对象,于是Java引入了序列号。

  1. 所有保存到磁盘的对象都有一个序列号。
  2. 当程序序列号一个对象时,先查看这个对象是否序列号,只有没有序列化过的,系统才会把这个对象转换成字节序列化到磁盘中,或则直接输出序列号
    ESO43Q.png
 
  1. ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("Object.txt"));
  2. dog d1=(dog)inputStream.readObject();
  3. dog d2=(dog)inputStream.readObject();
  4. System.out.println(d1==d2);

版本号
我们发现每个序列化的类都有这个 ,如果没有系统每次都会随机生成

 
  1. private static final long serialVersionUID = 1L;

这个我们称为版本号,为了就是当你反序列化时,可以兼容。也就是说,当你的类升级后,只要这个编号不变,序列化机制就把它们当成同一个版本,这样,如果当一个类对象让你序列化后,你修改了这个类,保证改对象可以正常的被反序列化。如果你没有写,改变量由jvm更具类信息就行计算,所以修改后肯定不同,就不能正常的反序列化了。
transient关键字

这个关键字就是让序列化机制不用理会它修饰的变量,这个变量不会被序列化

 
  1. public class dog implements Serializable {
  2. private static final long serialVersionUID = 1L;
  3. private String name;
  4. private transient String age;
  5. public dog(String name, String age) {
  6. this.name = name;
  7. this.age = age;
  8. System.out.println("调用了构造方法");
  9. }
  10. public String getName() {
  11. return name;
  12. }
  13. public void setName(String name) {
  14. this.name = name;
  15. }
  16. public String getAge() {
  17. return age;
  18. }
  19. public void setAge(String age) {
  20. this.age = age;
  21. }
  22. }

测试

 
  1. public class test {
  2. public static void main (String arg[]) throws Exception{
  3. ObjectOutputStream outputStream=new ObjectOutputStream(new FileOutputStream("Object.txt"));
  4. dog d=new dog("小狗","12");
  5. outputStream.writeObject(d);
  6. ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("Object.txt"));
  7. dog d1=(dog)inputStream.readObject();
  8. System.out.println("name:"+d1.getName());
  9. System.out.println("age:"+d1.getAge());
  10. }
  11. }

结果:

 
  1. 调用了构造方法
  2. name:小狗
  3. age:null