一、什么是序列化和反序列化
Java序列化就是指把Java对象转换为字节序列的过程
Java反序列化就是指把字节序列恢复为Java对象的过程。
二、为什么要把一个对象序列化
正常情况下,Java new出的对象,是保存在内存当中的,是不能持久化保存的,也不能直接在网络中传输,如何解决呢?就是把Java对象转换为byte字节数据,以字节的方式去实现持久化保存和网络传输。而反序列化,就是把须列化后的数据,重新恢复成内存中的Java对象。
总之,序列化,就是对一个Java对象的保存和重建的过程。
三、Java实现序列化的过程
为了让一个Java对象能序列化,我们需要为这个Java对象实现Serializable接口,像下面这样,这个方法和普通的Bean来说,多继承了这个接口,这个接口没有实现实现任何方法,它是为什么告诉编译器,这个接口可以序列化。
@Data
public class User implements Serializable {
private String name;
private int age;
private String sex;
private String phone;
}
这个类可以序列化了,但如何序列化让它能本地存储呢?执行下面的write方法,将会在D盘根目录下创建User类的序列化文件,执行read方法可以把序列化文件转变成对象。
public class Client {
@Test
public void write() throws Exception{
User user = new User();
user.setAge(15);
user.setName("张三");
user.setPhone("123");
FileOutputStream fileOutputStream = new FileOutputStream("D://out.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(user);
}
@Test
public void read() throws Exception{
FileInputStream fileInputStream = new FileInputStream("D://out.txt");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
User o = (User)objectInputStream.readObject();
System.out.println(o.toString());
}
}
read方法运行结果,序列化出来的对象,拥有序列化之前对象的值。
四、不能被序列化的对象
Java对象中,如果一个字段被transient修饰,或者这个字段是static静态字段,这个类将不会被序列化,意味着,这个对象被序列化后,再次反序列化成Java对象后,对于的字段值为null。
五、序列号的作用
我们的类是不断的变动的,我们把一个对象序列化到本地文件后,有一天,这个对象的类发送了改变,我们用这个改变后的类接收序号化文件,会发生什么?把name和age注释掉,然后去反序列化。
public class User implements Serializable {
// private String name;
//
// transient private int age;
private String sex;
private String phone;
}
public class Client {
@Test
public void write() throws Exception{
User user = new User();
// user.setAge(15);
// user.setName("张三");
user.setPhone("123");
FileOutputStream fileOutputStream = new FileOutputStream("D://out.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(user);
}
@Test
public void read() throws Exception{
FileInputStream fileInputStream = new FileInputStream("D://out.txt");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
User o = (User)objectInputStream.readObject();
System.out.println(o.toString());
}
}
结果就是
java.io.InvalidClassException: com.example.service.demo01.User; local class incompatible: stream classdesc serialVersionUID = -118799779803260725, local class serialVersionUID = 1681826866445576943
这个报错是什么意思?是这个类和序列化文件里的类的序列号不一样,无法反序列化。
这里出现了一个问题,什么是序列号?
序列号是一个长整型的数据,一个类的序列号,是根据这个类的成员变量计算出来,如果成员变量发生改变,序列号也会发生改变,只有序列化文件的序列号和类的序列号相同,才能反序列化成功。
所以这个报错就很容易理解了,在改动前,类有三个变量,序列号的值假设是3,序列化的文件也是3,删除一个变量后,只剩两个变量,假设序列号是2,这样和序列化文件的序列号就不一样了,所以无法反序列化。
有时候,我们希望,即使类发生了改变,也能反序列化成功,比如之前有现在只剩3个字段,序列化的文件有4个字段,我们只序列化现在的类有的字段,那该怎么做?
我们可以手动为一个类设置序列号,像这样:
这样,即使类发生了改变,但是序列号我们已经手动写死了,之后类再怎么发生变化,反序列化也不会报错,只会序列那化些还能对应上的值。