什么是Java对象的序列化?

199 阅读3分钟

相信一些刚开始接触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接口,如果上述情况都不满足就会抛出序列化异常。 image.png 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关键字修饰。