序列化
序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。
-
也就是说:
把对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为对象的过程称为对象的反序列化。 -
对象的序列化主要有两种用途:
1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
2) 在网络上传送对象的字节序列。
ObjectOutputStream类——实现序列化
构造函数:
- ObjectOutputStream(OutputStream out)
创建写入指定 OutputStream 的 ObjectOutputStream。
常用API
- void writeObject(Object obj)
将指定的对象写入 ObjectOutputStream。 - 其他字节流共有的write方法(此处不与介绍)
ObjectInputStream类——实现反序列化
构造函数
- ObjectInputStream(InputStream in)
创建从指定 InputStream 读取的 ObjectInputStream。
常用API
-
Object readObject()
从 ObjectInputStream 读取对象。 -
其他字节流共有的read方法
序列化与反序列化验证
注意:要实现序列化首先肯定需要写入、读出的类实现Serializable接口(无抽象方法,仅作为标记)
@org.junit.Test
public void test() throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
Student s1 = new Student("male",18,"Lily","08194");
Student s2 = new Student("male",21,"Mike","123132");
oos.writeObject(s1);
oos.writeObject(s2);
Student Lily = (Student) ois.readObject();
Student Mike = (Student) ois.readObject();
/* 检验序列化结果 */
System.out.println(Lily);
System.out.println(Mike);
// Student [gender=male, age=18, Name=Lily, Id=08194]
// Student [gender=male, age=21, Name=Mike, Id=123132]
/* 查看是否为共享空间的同一对象*/
Lily.setAge(20);
System.out.println(Lily);//Student [gender=male, age=20, Name=Lily, Id=08194]
System.out.println(s1);//Student [gender=male, age=18, Name=Lily, Id=08194]
oos.close();
ois.close();
}
//学生实体类
public class Student implements Serializable{
//如不指定serialVersionUID在改动类信息时也会变动,倘若序列化和反序列化过程的UID无法匹配则会出现异常
private static final long serialVersionUID = 1L;
private String gender;
private int age ;
private String Name;
private String Id;
public Student() {
}
@Override
public String toString() {
return "Student [gender=" + gender + ", age=" + age + ", Name=" + Name + ", Id=" + Id + "]";
}
public Student(String gender, int age, String name,String id) {
super();
this.gender = gender;
this.age = age;
Name = name;
Id = id;
}
.......
}
Java的序列化机制
通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。
- 采用
这种方式生成的serialVersionUID是1L,
例如: private static final long serialVersionUID = 1L;
- 采用
这种方式生成的serialVersionUID是根据类名,接口名,方法和属性等来生成的,
例如:
private static final long serialVersionUID = 4603642343377807741L;
总结:
在上面的例子中我们发现:
- Lily.setAge(20);
System.out.println(Lily);//Student [gender=male, age=20, Name=Lily, Id=08194]
System.out.println(s1);//Student [gender=male, age=18, Name=Lily, Id=08194]
改变了反序列化出来的对象,原对象信息不会改变,因此他们不会是同一个对象!
这意味着序列化、反序列化的过程实际上实现了深克隆!
序列化与 Transient 关键字
- Transient 关键字的作用
控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient变量的值被设为初始值,如 int 型的是 0,对象型的是 null。 - 父类非serializable的子类对象
这类子类对象在序列化时不会序列化继承自父类的成员。
@org.junit.Test
public void test() throws Exception {
//Serializable : 可序列化的 将对象转化成字节的,要求对象必须是可序列化的 仅用于标识可序列化的语义
//SerialVersionUID
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
Student s1 = new Student("male",18,"Lily","08194");
Student s2 = new Student("male",21,"Mike","123132");
oos.writeObject(s1);
oos.writeObject(s2);
Student Lily = (Student) ois.readObject();
Student Mike = (Student) ois.readObject();
/* 检验序列化结果 */
System.out.println(Lily);
System.out.println(Mike);
// transient的Id、继承的height\weight都没有被序列化
// Student [gender=male, age=18, Name=Lily, Id=null, height=0, weight=0]
// Student [gender=male, age=21, Name=Mike, Id=null, height=0, weight=0]
oos.close();
ois.close();
}