1.什么是序列化和反序列化?
序列化:把对象转为字节序列的过程称为对象的序列化;
反序列化:把字节序列恢复为对象的过程称为对象的反序列化。
有时候我们想把一些信息持久化保存起来,那么序列化的意思就是把内存里面的这些对象给变成一连串的字节描述的过程。
2.什么时候需要序列化?
把内存中的对象状态保存到一个文件或数据库中的时候;
用套接字在网络上传送对象的时候。
3.实现序列化的方式
序列化本身和语言是无关的。
下面是一些常见的实现序列化的方式:
- Java对象序列化;
- JSON序列化;
- XML;
- Protostuff;
- Hessian(它基于HTTP协议传输,使用Hessian二进制序列化,对于数据包比较大的情况比较友好。);
- Dubbo Serialization(阿里dubbo序列化);
- FST(高性能、序列化速度大概是JDK的4~10倍,大小是JDK大小的1/3左右);
- 自定义协议进行序列化。
4.如何实现序列化?
如下代码,我们自己实现一个Person类,在类当中定义成员变量name,age,sex,stuId,count,如果要将Person进行序列化只需要实现Serializable接口即可。
import java.io.Serializable;
class Person implements Serializable {
private String name;
private int age;
private String sex;
transient private int stuId;
private static int count = 100;
public Person(String name, int age, String sex, int stuId) {
this.name = name;
this.age = age;
this.sex = sex;
this.stuId = stuId;
}
@Override
public String toString() {
return String.format(
"Person{name: %s, age: %d, sex: %s, stuId: %d, count: %d}\n",
name, age, sex, stuId, count
);
}
}
序列化和反序列化测试:
import java.io.*;
public class SerializableTest {
public static void main(String[] args) throws Exception {
serialPerson();
Person person = deserialPerson();
System.out.println(person);
}
/**
* Person对象序列化
* @throws IOException
*/
private static void serialPerson() throws IOException {
Person person = new Person("sss", 24, "男", 101);
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(new File("d:/person.txt"))
);
oos.writeObject(person);
System.out.println("person 对象序列化成功!");
oos.close();
}
/**
* Person对象反序列化
* @return
* @throws Exception
*/
private static Person deserialPerson() throws Exception {
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(new File("d:/person.txt"))
);
Person person = (Person)ois.readObject();
System.out.println("person 对象反序列化成功!");
return person;
}
}
从结果可以看出:
代码实现了对象的序列化和反序列化;
transient修饰的属性是不会被序列化的。person对象的stuId本来应该是101,但是现在为0;
静态变量好像也被序列化了,其实没有。
ObjectOutputStream代表对象输出流:它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中;
ObjectInputStream代表对象输入流:它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
5.静态属性是否被序列化?
如果我们修改一下main函数中的代码,不再进行序列化,只进行反序列化,并将类中count变量值改为666,如果静态属性参与了序列化,那么count的值应该是上一次序列化的结果100。而实际上结果为666,所以说静态属性不会被进行序列化。
6.SerialVersionUID
将Person类中加上private static final long serialVersionUID = 1L;,然后屏蔽掉序列化的方法serialPerson(),运行主函数。
import java.io.*;
public class SerializableTest {
public static void main(String[] args) throws Exception {
Person person = deserialPerson();
System.out.println(person);
}
/**
* Person对象反序列化
* @return
* @throws Exception
*/
private static Person deserialPerson() throws Exception {
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(new File("d:/person.txt"))
);
Person person = (Person)ois.readObject();
System.out.println("person 对象反序列化成功!");
return person;
}
}
class Person implements Serializable {
private String name;
private int age;
private String sex;
transient private int stuId;
private static int count = 666;
private static final long serialVersionUID = 1L;
public Person(String name, int age, String sex, int stuId) {
this.name = name;
this.age = age;
this.sex = sex;
this.stuId = stuId;
}
@Override
public String toString() {
return String.format(
"Person{name: %s, age: %d, sex: %s, stuId: %d, count: %d}\n",
name, age, sex, stuId, count
);
}
}
Exception in thread "main" java.io.InvalidClassException: SerialTest.Person;
local class incompatible: stream classdesc serialVersionUID = 1571553230755001122,
local class serialVersionUID = 1
从结果可以看出,当我们在类中没有指定serialVersionUID的时候,编译器会自动赋值,如果序列化是以默认的serialVersionUID,那么反序列化也是会以默认的。而我们的代码是以默认的serialVersionUID进行序列化,以自己赋值的serialVersionUID进行反序列化,这样就会出现问题。
7.总结
一个类如果想被序列化,那么需要实现一个Serialzable接口;
类中的静态变量的值是不会进行序列化的,transient修饰的属性,是不会被序列化的,内置类型为对应的0值。引用类型为null;
在实现这个Serializable接口的时候,一定要给这个serialVersionUID赋值,最好设置为1L,不同的serialVersionUID的值,会影响到反序列化。