1、概念
(1)序列化
将数据结构或者对象转换成二进制序列的过程
(2)反序列化
将序列化过程所生成的二进制序列转换成数据结构或者对象的过程
(3)持久化
将数据结构或者对象存储起来,如内存、磁盘。
2、序列化的作用
进程之间、客户端和服务器之间数据的传输。因为传输过程只能传输二进制序列。
例如如下图:
![](https://img.haomeiwen.com/i7186219/d5d67046e7c66357.png)
在客户端和服务器之间网络数据传输的时候,我们可以选择Serializable序列化或者广义的json,xml,protbuf ,在安卓的进程之间通信的时候,可以选择Parcelable 序列化。
3、序列化的类型
(1)Serializable
Java独有的序列化
(2) Parcelable
Android独有的序列
(3)广义的序列化
json,xml,protbuf .
4、Serializable序列化方式
对象通过实现Serializable接口或者Externalizable接口,Externalizable接口也是实现了Serializable接口
关于Serializable的几个问题
(1)serialVersionUID
serialVersionUID通常是对象的哈希码,主要用于对象的版本控制唯一标识。
序列化和反序列化的时候类中的serialVersionUID一定要一致。如果序列化时的serialVersionUID和反序列化时的serialVersionUID不一样,将会抛出异常。举个例子
我们定义了Student 类中的serialVersionUID = 1,然后将其序列化到磁盘在反序列化出来的时候,不修改serialVersionUID
package com.it.test.seriv;
import java.io.Serializable;
public class Student implements Serializable {
private static final long serialVersionUID = 1;
private String name;
private String sex;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
public Student(String name, String sex) {
this.name = name;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public static void main(String[] args) {
//序列化
Student student = new Student("小明","男");
System.out.println("写入....");
SerializeableUtils.saveObject(student,"D:\\xuliehuaTest\\");
//反序列化
Student student1 = SerializeableUtils.readObject("D:\\xuliehuaTest\\");
System.out.println("读出....");
System.out.println(student1.toString());
}
}
序列化和反序列化都成功
写入....
读出....
Student{name='小明', sex='男'}
我们将serialVersionUID 改成2,再反序列化出来 试试
private static final long serialVersionUID = 2;
public static void main(String[] args) {
//序列化
// Student student = new Student("小明","男");
// System.out.println("写入....");
// SerializeableUtils.saveObject(student,"D:\\xuliehuaTest\\");
//反序列化
Student student1 = SerializeableUtils.readObject("D:\\xuliehuaTest\\");
System.out.println("读出....");
System.out.println(student1.toString());
}
提示serialVersionUID 不一致的异常。
java.io.InvalidClassException: com.it.test.seriv.Student; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1885)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
at com.it.test.seriv.SerializeableUtils.readObject(SerializeableUtils.java:71)
at com.it.test.seriv.Student.main(Student.java:42)
读出....
Exception in thread "main" java.lang.NullPointerException
at com.it.test.seriv.Student.main(Student.java:44)
因此我们知道serialVersionUID控制了对象的版本,序列化和反序列化的时候serialVersionUID一定要一致。
当然我们也可以不写serialVersionUID,这样JVM会自动给我们添加上serialVersionUID的值。当类没有任何改变的时候,我们直接序列化和反序列化是正常的,但是假如我们在序列化之后,删除或者添加了类的某个成员,JVM就会修改serialVersionUID的值,就会导致和序列化时候的serialVersionUID不一致。这样我们在反序列化的时候就还会报serialVersionUID不一致的错误。因此当我们需要序列化对象的时候,一般手动设置serialVersionUID的值。
(2)希望某个成员不被序列化
使用 transient 关键字修饰成员
private transient String name;
(3)如果类中的一个成员没实现序列化接口,我们去序列化这个类的对象会怎么样?
如果序列化的对象没有包含这个成员,则正常,如果包含,则会报抛异常。
举例
Student中包含2个构造方法,其中User成员没有实现Serializable接口
private String name;
private String sex;
private User user;
public Student(String name, String sex) {
this.name = name;
this.sex = sex;
this.user = user;
}
public Student(String name, String sex, User user) {
this.name = name;
this.sex = sex;
this.user = user;
}
我们先调用第一个构造方法创建对象然后序列化
public static void main(String[] args) {
//序列化
Student student = new Student("小明","男");
System.out.println("写入....");
SerializeableUtils.saveObject(student,"D:\\xuliehuaTest\\");
//反序列化
Student student1 = SerializeableUtils.readObject("D:\\xuliehuaTest\\");
System.out.println("读出....");
System.out.println(student1.toString());
}
没问题
写入....
读出....
Student{name='小明', sex='男'}
调用第二个构造方法
public static void main(String[] args) {
//序列化
Student student = new Student("小明","男",new User());
System.out.println("写入....");
SerializeableUtils.saveObject(student,"D:\\xuliehuaTest\\");
//反序列化
Student student1 = SerializeableUtils.readObject("D:\\xuliehuaTest\\");
System.out.println("读出....");
System.out.println(student1.toString());
}
直接抛出 java.io.NotSerializableException异常,我们只需将User实现Serializable接口即可
写入....
java.io.NotSerializableException: com.it.test.seriv.User
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at com.it.test.seriv.SerializeableUtils.saveObject(SerializeableUtils.java:42)
at com.it.test.seriv.Student.main(Student.java:64)
java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: com.it.test.seriv.User
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1577)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2287)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2211)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2069)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
at com.it.test.seriv.SerializeableUtils.readObject(SerializeableUtils.java:71)
at com.it.test.seriv.Student.main(Student.java:67)
Caused by: java.io.NotSerializableException: com.it.test.seriv.User
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at com.it.test.seriv.SerializeableUtils.saveObject(SerializeableUtils.java:42)
at com.it.test.seriv.Student.main(Student.java:64)
读出....
Exception in thread "main" java.lang.NullPointerException
at com.it.test.seriv.Student.main(Student.java:69)
(4)当一个类实现了Serializable接口,父类没有实现Serializable接口,将子类序列化会怎么样?
- 如果父类没有定义有参的构造方法,子类的构造方法中就不需要调用其构造函数,子类可以正常序列化。只会把子类序列化。
- 如果父类带有有参的构造方法,子类的构造方法就需要调用父类的构造方法,这样相当于序列化子类的时候父类也要序列化,因此父类也要实现Serializable接口。
(5)反序列化成对象之后,会调用构造函数吗?
不会,反序列化生成对象是从二进制直接读出来的,然后再用Object 进行强转,不是原来的那个对象。
(6)序列化和反序列化后的对象关系?
是一个深拷贝,前后对象的引用地址不一样
5、Android中序列化方式parcelable
![](https://img.haomeiwen.com/i7186219/cef93c8b722c53af.png)
网友评论