首页 > 代码库 > java 序列化详解(二)

java 序列化详解(二)

接着前面的文章说,当任何一个类继承Serializable 这个接口时,Eclipse经常会有黄色惊叹号提示。

提示内容如下:

The serializable class Person does not declare a static final serialVersionUID field of type long

点开以后有2个选择 一个是

Adds a default serial version ID to the selected type.

Use this option to add a user-defined ID in combination with
custom serialization code if the type did undergo structural
changes since its first release.

还有一个是

Adds a generated serial version ID to the selected type.

Use this option to add a compiler-generated ID if the type did
not undergo structural changes since its first release.

 

那么这个serialVersionUID  是干嘛的?

简单提一下 ,可以把这个

 

/**
*
*/
private static final long serialVersionUID = 1L;

 

Eclipse帮我们生成的这个语句解释一下。

 

实际上,java序列化呢,主要的应用场景就是 rmi。远程接口调用。

我举个例子,前面我们写的那个例子。把她序列化和反序列化 这个过程分开

放在2个工程里面,(要注意虽然是2个工程但是包要一样),

运行以后程序是正常的,但是你要注意 如果此时你2个工程的person类的

serialVersionUID  这个值如果不一样。你就会发现在反序列化的时候失败了。

只有当这个值相等的时候 序列化和反序列化才会成功。

 

其实这个值主要就是用来强制客户端更新接口用的。(RMI里经常使用)

比如客户端有个类A,服务器端有个类也是A。这个时候2个端用rmi进行通信,

假设服务器端这个类A 修改了某些内容比如增加或删除了一个字段,这个时候你怎么通知

客户端呢,你就把serialVersionUID  这个值修改成和客户端不一样的,这样在序列化或者反序列化的

时候就会出错,如此一来 客户端就知道,噢,序列化出错要更新接口了。

 

此外,前面的那个文章也说明了,序列化的过程默认调用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法

当然我们也可以手动写方法来调用,writeObject 和 readObject 方法可以允许用户控制序列化的过程,手动控制除了能更好的控制序列化所消耗的事件以外,

还有一个优点是可以加密,比如我们要序列化一个 用户的用户名和密码,你默认序列化的话,是有可能被抓取的,但是如果你手动序列化在里面对密码进行

加密,然后在反序列化的时候解密,就非常安全了。

 

再看一段代码。

 

 1 package com.burning.test; 2  3 import java.io.Serializable; 4  5 public class Person implements Serializable { 6  7     public static final int STATIC_VALUE =http://www.mamicode.com/100; 8      9     10     @Override11     public String toString() {12         return "Person [name=" + name + ", age=" + age + "]";13     }14 15     public String getName() {16         return name;17     }18 19     public void setName(String name) {20         this.name = name;21     }22 23     public int getAge() {24         return age;25     }26 27     public void setAge(int age) {28         this.age = age;29     }30 31     private String name;32 33     private int age;34 35 }

然后看看main

 

 1 package com.burning.test; 2  3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileNotFoundException; 6 import java.io.FileOutputStream; 7 import java.io.IOException; 8 import java.io.ObjectInputStream; 9 import java.io.ObjectOutputStream;10 11 public class TestMain {12 13     public static void main(String[] args) {14         // TODO Auto-generated method stub15         File file = new File("person2.out");16         ObjectInputStream oin = null;17         ObjectOutputStream oout = null;18         try {19             oout = new ObjectOutputStream(new FileOutputStream(file));20             Person person = new Person();21             oout.writeObject(person);22             oout.flush();23 24             System.out.println("第1次写完以后" + file.length());25             oout.writeObject(person);26             System.out.println("第2次写完以后" + file.length());27             oout.close();28             oin = new ObjectInputStream(new FileInputStream(file));29             Object obj = oin.readObject();30             Object obj2 = oin.readObject();31             System.out.println(obj == obj2);32 33         } catch (FileNotFoundException e) {34             // TODO Auto-generated catch block35             e.printStackTrace();36         } catch (IOException e) {37             // TODO Auto-generated catch block38             e.printStackTrace();39         } catch (ClassNotFoundException e) {40             // TODO Auto-generated catch block41             e.printStackTrace();42         } finally {43             try {44                 oout.close();45             } catch (IOException e) {46                 // TODO Auto-generated catch block47                 e.printStackTrace();48             }49             try {50                 oin.close();51             } catch (IOException e) {52                 // TODO Auto-generated catch block53                 e.printStackTrace();54             }55 56         }57 58     }59 }

运行一下程序。结果为

 

第1次写完以后83
第2次写完以后88
true。

 

这个地方运行结果一目了然,我们发现,Java 序列化机制为了节省磁盘空间,具有特定的存储规则,当写入文件的为同一对象时,并不会再将对象的内容进行存储,而只是再次存储一份引用,

上面增加的 5 字节的存储空间就是新增引用和一些控制信息的空间。反序列化时,恢复引用关系,使得清单 3 中的 t1 和 t2 指向唯一的对象,二者相等,输出 true。该存储规则极大的节省了存储空间。

 

然后我们再修改一下主类

package com.burning.test;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;public class TestMain {    public static void main(String[] args) {        // TODO Auto-generated method stub        File file = new File("person2.out");        ObjectInputStream oin = null;        ObjectOutputStream oout = null;        try {            oout = new ObjectOutputStream(new FileOutputStream(file));            Person person = new Person();            person.setAge(10);            oout.writeObject(person);            oout.flush();            System.out.println("第1次写完以后" + file.length());            person.setAge(20);            oout.writeObject(person);            System.out.println("第2次写完以后" + file.length());            oout.close();            oin = new ObjectInputStream(new FileInputStream(file));            Object obj = oin.readObject();            Object obj2 = oin.readObject();            System.out.println(obj.toString());            System.out.println(obj2.toString());        } catch (FileNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (ClassNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } finally {            try {                oout.close();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }            try {                oin.close();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }    }}

看一下运行结果

 

第1次写完以后83
第2次写完以后88
Person [name=null, age=10]
Person [name=null, age=10]

 

我们就会发现结果是这样的,因为第一次对象保存完毕以后  你虽然修改了这个对象的值,但是你在第二次序列化对象的时候 因为这2个对象引用相等 所以不会更改值,只会保存一部分引用

所以会得出一个比较奇怪的结果~这个地方要好好理解下

再次修改main 看看运行结果

 

package com.burning.test;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;public class TestMain {    public static void main(String[] args) {        // TODO Auto-generated method stub        File file = new File("person4.out");        ObjectInputStream oin = null;        ObjectOutputStream oout = null;        try {            oout = new ObjectOutputStream(new FileOutputStream(file));            Person person = new Person();            person.setAge(10);            oout.writeObject(person);            oout.flush();            System.out.println("第1次写完以后" + file.length());            Person person2 = new Person();            person2.setAge(20);            oout.writeObject(person2);            System.out.println("第2次写完以后" + file.length());            oout.close();            oin = new ObjectInputStream(new FileInputStream(file));            Object obj = oin.readObject();            Object obj2 = oin.readObject();            System.out.println(obj.toString());            System.out.println(obj2.toString());        } catch (FileNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (ClassNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } finally {            try {                oout.close();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }            try {                oin.close();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }    }}

 

这个结果 就是 

第1次写完以后83
第2次写完以后94
Person [name=null, age=10]
Person [name=null, age=20]

看完这个结果应该比较好理解了。

 

java 序列化详解(二)