说简单点,Java中反序列化最主要的作用就是创建一个对象
来,回顾下Java里面创建对象的四种方式:
new
关键字创建clone()
方法创建- 通过反射方式创建
- 反序列化(反串行化)创建
好了,清楚了。
既然反序列化是拿到一个对象,那么序列化其实是借助 I/O 流将我们的这个可序列化的类的信息存储起来 -> 永久的保存在磁盘上
借助到了 I/O 流,肯定是通过write
方法写入文件,再用read
方法读出文件中的数据,拿到这个类的一个对象。
好了,说一下具体的细节要点:
1、可序列化的类
一个普通的Java类是什么样子呢?就是那个样子,还能是啥。
可序列化的类长啥样子呢?☟
class Boy implements Serializable
,可序列化的类实现了这么一个接口;这个接口是一个标识接口,表明实现了它的子类可以进行序列化。
标识接口Serializable
:
public interface Serializable {
}
里面啥也没有。
此外Java还有另一个比较重要的标识接口Cloneable
2、序列化ID
private static final long serialVersionUID = 2344444499999999999L;
就是这个东西。
为什么要有这个东西:我通过序列化将一个类保存起来,那我反序列化的时候,如何知道我拿到的就是我反序列化的对象呢。
当序列化ID跟反序列化ID一致时那就是了。
所以,第一点:要实现序列化的类中,可以给定一个long
类型的ID,没给也没关系,系统会自动生成一个;
第二点:当这个序列化ID变化的时候,就会反序列化失败。常见错误包括
- 序列化之后,改变了这个序列化ID的值
- 序列化时,自己没提供;反序列化时,又加了一个序列化ID
- 序列化时自己提供;反序列化时删掉了
3、序列化和反序化的实现
首先,一个实现了Serializable
的类:
//类里面的其它信息我就不写了,也不重要
class Boy implements Serializable, Cloneable{
//随便给定一个序列化ID
private static final long serialVersionUID = 2344444499999999999L;
}
实现序列化和反序列化借助的类:
ObjectOutputStream
、输出流、写入文件
ObjectInputStream
、输入流、读文件内容
这里用的是writeObject()、readObject()
实现序列化
File file = new File("F:/Test/serialize.txt");
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(file));
//写入这个对象
outputStream.writeObject(new Boy());
//写入一些信息
outputStream.writeObject(new Boy("tony", 22));
//再比如一个方法的内容(当前这个方法返回一个类的实例)
outputStream.writeObject(new Boy().getInstance());
outputStream.close();
实现反序列化
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(file));
//返回一个 Object 类型的实例
Object o = inputStream.readObject();
//也可以强转
Boy boy = (Boy) inputStream.readObject();
System.out.println(o);
System.out.println(boy);
inputStream.close();
输出:
本来我想说都可以,都是一样的。
上面序列化写入文件,保存了三个信息,它依次读取到了其中的前两个
也可以这么搞:
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(file));
Boy tmp = null;
while ((tmp = (Boy) inputStream.readObject()) != null) {
System.out.println(tmp);
}
inputStream.close();
输出:
还顺便抛了个异常出来,查了下,这个异常表明流读取到了末尾。我们主要使用序列化拿到一个对象,或者我们要保存起来的信息,所以不影响的~我主要是想试试把写入的对象都拿出来
还有:
Object o = inputStream.readObject();
这样做,虽然也输出了,但是这算是一个父类,父类是拿不到子类的方法的:
Boy boy = (Boy) inputStream.readObject();
强转才是王道:
4、transient
关键字
用来修饰成员变量,在此次序列化中不被序列化。
我们实现序列化,就是将对象保存在文件中。同时也会将这个类中的其他东西保存。
如果我们有一个数组,只用了一部分,那么序列化的时候就会整个的序列化掉,是一种浪费;因此我们不让它序列化,就能够减少一些浪费。
5、序列化目的
- 永久保存对象在磁盘上
- 在网络中可以采用序列化机制直接传输对象