1、定义:
把对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为对象的过程称为对象的反序列化。
2、用途:
(1)需要把一些对象持久保存起来,通过序列化将内存种的这些对象转换为一系列的字节,就是变成文件。
(2)需要在网络上传送字节序列。
1、先看一下接口Serializable的源代码。
package java.io;
// 一大堆注释
public interface Serializable {
}
空的?一个空的方法怎么实现对象的序列化和反序列化的?其实注释里已经提到了,它只是一个标志而已,告诉JVM实现这个接口的类的对象可被序列化。
2、上代码
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class Student implements Serializable{
private static final long serialVersionUID = 1l;
public static String school = "Java School";
public int id;
public String name;
transient String address;
public Student(int id, String name, String address) {
this.id = id;
this.name = name;
this.address = address;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Student{id = "+id+", name = "+name+", address = "+address+"}";
}
}
public class MySeriaalizable{
public static void main(String[] args) {
File file = new File("D:/test.txt");
Student s1 = new Student(1001, "Student1", "幸福路1号");
// 序列化
try {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
out.writeObject(s1);
out.close();
}catch (Exception e) {
e.printStackTrace();
}
// 反序列化
try {
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
Student s2 = (Student) in.readObject();
System.out.println(s2.toString());
System.out.println(s1.equals(s2));
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 代码输出
Student{id = 1001, name = Student1, address = null}
false
使用起来非常方便,只需要ObjectOutputStream的 writeObject(Object object) 方法实现序列化,ObjectInputStream的readObject() 方法实现反序列化。
代码输出address=null?看一下序列化的结果是什么?
可以看到test.txt文件中还有一些乱码,这不重要。重要的是可以很清楚的看到里面有id, name, 但是没有school, address, 这是因为被transient修饰的字段不会被序列化;另外static修饰的属性属于类,不仅仅属于某个对象,序列化操作的是对象,所以静态变量也不会被序列化。
serialVersionUID非必需但很有必要,如果自己不声明,JVM会动态的生成。原则上,序列化后的数据中的serialVersionUID与当前类当中的serialVersionUID一致时,对象才能够反序列化成功。在序列化过程种,系统将serialVersionUID写入到序列化的文件中,反序列化时,首先检测文件中的serialVersionUID和当前类的serialVersionUID是否一致,如果一致则反序列化成功,否则报错:java.io.InvalidClassException。
靠JVM动态声明serialVersionUID,类发生改变serialVersionUID会发生变化,且不同的编译器生成的serialVersionUID也会不同。看一下示例,在部分2中代码基础上,去掉serialVersionUID进行序列化后,对Student类增加一个属性(修改或者删除也一样),然后对序列化结果直接进行反序列化。
class Student implements Serializable{
//private static final long serialVersionUID = 1l;
public static String school = "Java School";
public int id;
public String name;
transient String address;
public String newStr;
...... // 和部分2代码相同
}
public class MySeriaalizable{
public static void main(String[] args) {
File file = new File("D:/test.txt"); // 读取动态生成serialVersionUID序列化的结果
Student s1 = new Student(1001, "Student1", "幸福路1号");
// 反序列化
try {
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
Student s2 = (Student) in.readObject();
System.out.println(s2.toString());
System.out.println(s1.equals(s2));
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
java.io.InvalidClassException: mytest.Student; local class incompatible: stream classdesc serialVersionUID = -5603698407479578026, local class serialVersionUID = 970959418018661672
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:621)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at mytest.MySeriaalizable.main(MySeriaalizable.java:61)
错误提示:stream classdesc serialVersionUID=-5603698407479578026, local class serialVersionUID=970959418018661672,两次动态生成的serialVersionUID不同。
如果自己声明serialVersionUID,则不会出现这个错误,在反序列化生成的对象只有未发生改变的属性的赋值,比如将“name”改名为“name1”后的反序列化结果为:
Student{id = 1001, name = null, address = null}
false