首页 > 代码库 > 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 序列化详解(二)