一、概述
很多时候我们需要将对象的信息以文件的形式存储到硬盘上以便以后可以恢复使用。我们可以按照一定的格式将变量的值依次写到特定格式的文件中。但有时候我们希望只有我们自己可以读懂它并且修改它,这就是序列化的作用。
概念:将对象转化为字节序列的过程称为对象的序列化。而把字节序列恢复为对象的过程称为对象的反序列化。
序列化的两种主要用途:
(1):将对象的字节序列以文件的形式存储到硬盘上。
(2):在网络上传输对象的字节序列。
二、实现序列化
只需要让类实现Serializable这个接口即可。此接口没有任何方法,是一个标识性的接口用来标识此类可以被序列化。
三、具体例子
Pond.java
1 package bean; 2 import java.io.Serializable; 3 4 public class Pond implements Serializable 5 { 6 private int length; 7 private int width; 8 private int height; 9 10 public Pond(int length, int width, int height) 11 { 12 this.length = length; 13 this.width = width; 14 this.height = height; 15 } 16 17 @Override 18 public String toString() 19 { 20 return length + ", " + width + ", " + height; 21 } 22 }
Test.java
1 package test; 2 import java.io.FileOutputStream; 3 import java.io.ObjectOutputStream; 4 import bean.Pond; 5 6 public class Test 7 { 8 @org.junit.Test 9 public void test() throws Exception 10 { 11 Pond small = new Pond(1, 2, 3); 12 Pond middle = new Pond(4, 5, 6); 13 Pond large = new Pond(7, 8, 9); 14 System.out.println("Before"); 15 System.out.println(small); 16 System.out.println(middle); 17 System.out.println(large); 18 FileOutputStream fos = new FileOutputStream("pond.hxy"); 19 ObjectOutputStream oos = new ObjectOutputStream(fos); 20 oos.writeObject(small); 21 oos.writeObject(middle); 22 oos.writeObject(large); 23 oos.close(); 24 } 25 }
pond.hxy中内容如下:
反序列化:
1 package test; 2 import java.io.FileInputStream; 3 import java.io.FileOutputStream; 4 import java.io.IOException; 5 import java.io.ObjectInputStream; 6 import java.io.ObjectOutputStream; 7 8 import bean.Pond; 9 10 public class Test 11 { 12 @org.junit.Test 13 public void test() throws IOException, ClassNotFoundException 14 { 15 Pond small = new Pond(1, 2, 3); 16 Pond middle = new Pond(4, 5, 6); 17 Pond large = new Pond(7, 8, 9); 18 FileOutputStream fos = new FileOutputStream("pond.hxy"); 19 ObjectOutputStream oos = new ObjectOutputStream(fos); 20 oos.writeObject(small); 21 oos.writeObject(middle); 22 oos.writeObject(large); 23 oos.close(); 24 small = null; 25 middle = null; 26 large = null; 27 FileInputStream fis = new FileInputStream("pond.hxy"); 28 ObjectInputStream ois = new ObjectInputStream(fis); 29 small = (Pond)ois.readObject(); 30 middle = (Pond)ois.readObject(); 31 large = (Pond)ois.readObject(); 32 System.out.println(small); 33 System.out.println(middle); 34 System.out.println(large); 35 System.out.println("运行结束"); 36 } 37 }
运行结果:
四、序列化ID
private static final long serialVersionUID = 1L; 其实就是这个东西。它的作用可以理解成是用于唯一标识一个类的long型变量。
反序列化的时候,JVM会把字节流中的序列化ID和本地相应类的ID作比较,如果不一致则会抛出异常。
如果没有显示定义序列化ID,JAVA序列化机制会自动生成一个序列化ID,这种情况下,只有当时编译生成的类具有正确的序列化ID。
所以总的来说,显式地定义序列化ID有两种用途:
1、 在某些场合,希望类的不同版本对序列化兼容,需要确保类的不同版本具有相同的serialVersionUID。
2、 在某些场合,不希望类的不同版本对序列化兼容,需要确保类的不同版本具有不同的serialVersionUID。
五、其他说明
每次调用readObject()都会从stream中读出下一个对象,读取顺序与写入顺序是相同的,且次数超过会抛出异常。
当对象被序列化的时候,被该对象引用的实例变量也会被序列化。且所有被引用的对象也会被序列化。这些操作都是自动进行的。
如果其中有不能序列化的对象(没有实现序列化接口),执行期间会抛出异常。
反序列化后的对象与原对象内容相同,但是地址不同。所以序列化可以用来做深拷贝。