相信一些刚开始接触Java的同学一定对Java对象的序列化这一概念难以理解,这篇博客我们就从Java语言的层面快速地学习理解Java对象的序列化
概念:
序列化就是把Java对象转换成字节序列,这样一来方便Java对象永久地保存在磁盘中,避免程序运行结束后对象就在内存中消失了,二来方便Java对象在网络中传输。
- 序列化:将Java对象转换为字节序列
- 反序列化:将字节序列恢复为Java对象
什么是Java对象?
在Java中,一个类的实例就是一个对象
public class Student {
private Integer id;
private String name;
private Integer age;
//此处省略getter、setter、toString及构造方法
}
Student student=new Student(1,"张三",18);
这里的student就是一个java对象,把student对象保存在文件中的过程就是序列化,那么我们应该怎么做呢?
序列化
首先想到的是用输出流,那我们试着借助ObjectOutputStream将student对象输出到一个文件会发生什么?
public static void serializable() throws IOException {
Student student = new Student(1,"zhangsan",18);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("F:\Download\Student.txt"));
objectOutputStream.writeObject(student);
objectOutputStream.close();
System.out.println("序列化完成");
}
执行serializable方法,控制台输出Exception in thread "main" java.io.NotSerializableException: com.atguigu.crud.entity.Student,这就意味这我们将student对象保存在文件中失败了。根据报错提示信息我们得知是因为student对象是不可序列的,如果需要被保存得实现Serializable接口。
public interface Serializable {
}
将Student类实现Serializable接口后再执行serializable方法,控制台输出“序列化完成”。 打开F盘Download文件夹中的Student.txt文件后,内容如下:主要记录一些类信息、属性的值以及序列号
ͭ sr com.atguigu.crud.entity.Student��Ǘ` L aget Ljava/lang/Integer;L idq ~ L namet Ljava/lang/String;xpsr java.lang.Integer⠤�� I valuexr java.lang.NumberƬ՝ՠ˂ xp sq ~ t zhangsan
在序列化时我们对ObjectOutputStream类debug时发现,在执行writeObject0方法时,会先判断被序列化对象是否是字符串、数组、枚举类型,最后判断是否实现了serializable接口,如果上述情况都不满足就会抛出序列化异常。
Serializable接口中没有任何方法,它只作为一个标志存在,实现了该接口的类就意味着是可序列化的;
反序列化
通过实验我们发现,一个实现了Serializable接口的对象就是可序列化的,那我们如何进行反序列操作呢?
public static void deSerializable() throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("F:\Download\Student.txt"));
Student student = (Student)objectInputStream.readObject();
objectInputStream.close();
System.out.println(student);
}
控制台输出:Student(id=1, name=zhangsan, age=18),说明我们已经反序列化成功了。
序列号唯一地标识被序列化后的对象
这里的序列号是实现Serializable接口后自动生成的,我们也可以自定义序列号。如果一个对象序列化后,这个对象对应的类如果发生变化的话,这个对象在执行反序列化操作时会因为序列号不一致发生异常。这也是为我们反序列时的安全考虑的。
Exception in thread "main" java.io.InvalidClassException: com.atguigu.crud.entity.Student; local class incompatible: stream classdesc serialVersionUID = -280451491803209719, local class serialVersionUID = 7380281698269029540
当我们自定义序列化号后,即使类发生变化,只要序列化前后的序列号不变,反序列化就是成功的。
不可序列化的两种情况
- static属性不能被序列化,因为序列化是针对Java对象的,不针对类。
- 被transient关键字修饰后的属性不能被序列化,比如密码等一些重要信息不想被序列化就用transient关键字修饰。