1.序列化的含义和意义:
对象序列化的目标是将对象保存到磁盘中,或允许在网络中直接传输对象。对象序列化机制允许把内存中的java对象转换成与平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上。内存中的数据对象只有转换成二进制流才可以进行数据持久化和网络传输。
为什么需要序列化与反序列化
我们知道,当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等, 而这些数据都会以二进制序列的形式在网络上传送。那么当两个Java进程进行通信时,能否实现进程间的对象传送呢?答案是可以的。如何做到呢?这就需要Java序列化与反序列化了。换句话说,一方面,发送方需要把这个Java对象转换为字节序列,然后在网络上传送;另一方面,接收方需要从字节序列中恢复出Java对象。
当我们明晰了为什么需要Java序列化和反序列化后,我们很自然地会想Java序列化的好处。其好处一是实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上(通常存放在文件里),二是,利用序列化实现远程通信,即在网络上传送对象的字节序列。
序列化:将数据对象转换成二进制流的过程称为对象的序列化
反序列化:将二进制流回复为数据对象的过程称为反序列化
为了是对象支持序列化机制,该类必须实现如下两个接口之一:
- Serializable
- Externalizable
2.使用对象流实现序列化:
代码示例:
/**
* @ClassName: JavaFile
* @Description:实现Serializable接口表明该对象是可序列化的
* @author: lsh
* @date: 2019年6月11日
*/
public class JavaFile implements Serializable {
/*
* SerialVersionUID是一个标识符,当它通常使用对象的哈希码序列化时会标记在对象上。我们可以通过Java中serialver工具找到该对象的serialVersionUID。
*/
private static final long serialVersionUID = 1L;
private String name;
public JavaFile(String name) {
this.name = name;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
}
将JavaFile对象写入磁盘:
// 对象序列化:将一个类对象写入磁盘文件
public static void main(String[] args) {
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:/text.txt"));
{
JavaFile jf = new JavaFile("Hello World");
oos.writeObject(jf);
}
} catch (Exception e) {
// TODO: handle exception
}
}
从磁盘中读取JavaFile对象:
反序列化:从二进制流中恢复java对象
*/
public static void main(String[] args) {
try {
// 创建一个ObjectInputStream输入流(处理流)
ObjectInputStream ojs = new ObjectInputStream(new FileInputStream("E:/text.txt"));
{
// 从输入流中读取一个java对象,并将其强制类型转换成JavaFile类
JavaFile j = (JavaFile)ojs.readObject();
System.out.println(j.getName());
}
} catch (Exception e) {
// TODO: handle exception
}
}
运行结果:Hello World
注意:反序列化读取的仅仅是java对象的数据,而不是java类,因此采用反序列化恢复java对象时,必须提供Java对象所属类的class文件,否则会引发ClassNotFountException异常
3.序列化ID的作用
可以看到,我们在进行序列化时,加了一个serialVersionUID字段,这便是序列化ID
private static final long serialVersionUID = 1L;
这个序列化ID起着关键的作用,它决定着是否能够成功反序列化!java的序列化机制是通过判断运行时类的serialVersionUID来验证版本一致性的,在进行反序列化时,JVM会把传进来的字节流中的serialVersionUID与本地实体类中的serialVersionUID进行比较,如果相同则认为是一致的,便可以进行反序列化,否则就会报序列化版本不一致的异常。
即序列化ID是为了保证成功进行反序列化
4.默认的序列化ID
当我们一个实体类中没有显式的定义一个名为“serialVersionUID”、类型为long的变量时,Java序列化机制会根据编译时的class自动生成一个serialVersionUID作为序列化版本比较,这种情况下,只有同一次编译生成的class才会生成相同的serialVersionUID。譬如,当我们编写一个类时,随着时间的推移,我们因为需求改动,需要在本地类中添加其他的字段,这个时候再反序列化时便会出现serialVersionUID不一致,导致反序列化失败。那么如何解决呢?便是在本地类中添加一个“serialVersionUID”变量,值保持不变,便可以进行序列化和反序列化。
如果没有显示指定serialVersionUID,会自动生成一个。
只有同一次编译生成的class才会生成相同的serialVersionUID
但是如果出现需求变动,Bean类发生改变,则会导致反序列化失败。为了不出现这类的问题,所以我们最好还是显式的指定一个serialVersionUID。
5.对象引用的序列化:
当一个类(Teacher类)持有另一个类(Person)的引用时,只有Person类是可序列化的,Teacher类才是可序列化的。若Person类不可序列化,那Teacher类无论是否实现Serilizable,Externalizable接口,Teacher类都是不可序列化的。
6.自定义序列化:
Transient 关键字
transient修饰符仅适用于变量,不适用于方法和类。在序列化时,如果我们不想序列化特定变量以满足安全约束,那么我们应该将该变量声明为transient。执行序列化时,JVM会忽略transient变量的原始值并将默认值保存到文件中。因此,transient意味着不要序列化。
7.总结:
1)Java序列化就是把对象转换成字节序列,而Java反序列化就是把字节序列还原成Java对象。
2)采用Java序列化与反序列化技术,一是可以实现数据的持久化,在MVC模式中很是有用;二是可以对象数据的远程通信。