Java对象为啥要实现Serializable接口?
Serializable接口概述
Serializable是java.io包中定义的、用于实现Java类的序列化操作而提供的一个语义级别的接口。Serializable序列化接口没有任何方法或者字段,只是用于标识可序列化的语义。实现了Serializable接口的类可以被ObjectOutputStream转换为字节流,同时也可以通过ObjectInputStream再将其解析为对象。例如,我们可以将序列化对象写入文件后,再次从文件中读取它并反序列化成对象,也就是说,可以使用表示对象及其数据的类型信息和字节在内存中重新创建对象。
而这一点对于面向对象的编程语言来说是非常重要的,因为无论什么编程语言,其底层涉及IO操作的部分还是由操作系统其帮其完成的,而底层IO操作都是以字节流的方式进行的,所以写操作都涉及将编程语言数据类型转换为字节流,而读操作则又涉及将字节流转化为编程语言类型的特定数据类型。而Java作为一门面向对象的编程语言,对象作为其主要数据的类型载体,为了完成对象数据的读写操作,也就需要一种方式来让JVM知道在进行IO操作时如何将对象数据转换为字节流,以及如何将字节流数据转换为特定的对象,而Serializable接口就承担了这样一个角色。
下面我们可以通过例子来实现将序列化的对象存储到文件,然后再将其从文件中反序列化为对象,代码示例如下:
先定义一个序列化对象User:
1. public class User implements Serializable {
2. private static final long serialVersionUID = 1L;
3.
4. private String userId;
5. private String userName;
6.
7. public User(String userId, String userName) {
8. this.userId = userId;
9. this.userName = userName;
10. }
11. }
然后我们编写测试类,来对该对象进行读写操作,我们先测试将该对象写入一个文件:
1. public class SerializableTest {
2.
3. /**
4. * 将User对象作为文本写入磁盘
5. */
6. public static void writeObj() {
7. User user = new User("1001", "Joe");
8. try {
9. ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("/Users/guanliyuan/user.txt"));
10. objectOutputStream.writeObject(user);
11. objectOutputStream.close();
12. } catch (IOException e) {
13. e.printStackTrace();
14. }
15. }
16.
17. public static void main(String args[]) {
18. writeObj();
19. }
20. }
运行上述代码,我们就将User对象及其携带的数据写入了文本user.txt中,我们可以看下user.txt中存储的数据此时是个什么格式:
1. java.io.NotSerializableException: cn.wudimanong.serializable.User
2. at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
3. at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
4. at cn.wudimanong.serializable.SerializableTest.writeObj(SerializableTest.java:19)
5. at
cn.wudimanong.serializable.SerializableTest.main(SerializableTest.java:27)
我们看到对象数据以二进制文本的方式被持久化到了磁盘文件中。在进行反序列化测试之前,我们可以尝试下将User实现Serializable接口的代码部分去掉,看看此时写操作是否还能成功,结果如下:
结果不出所料,果然是不可以的,抛出了NotSerializableException异常,提示非可序列化异常,也就是说没有实现Serializable接口的对象是无法通过IO操作持久化的。
接下来,我们继续编写测试代码,尝试将之前持久化写入user.txt文件的对象数据再次转化为Java对象,代码如下:
1. public class SerializableTest {
2. /**
3. * 将类从文本中提取并赋值给内存中的类
4. */
5. public static void readObj() {
6. try {
7. ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("/Users/guanliyuan/user.txt"));
8. try {
9. Object object = objectInputStream.readObject();
10. User user = (User) object;
11. System.out.println(user);
12. } catch (ClassNotFoundException e) {
13. e.printStackTrace();
14. }
15. } catch (IOException e) {
16. e.printStackTrace();
17. }
18. }
19.
20.
21. public static void main(String args[]) {
22. readObj();
23. }
24. }
通过反序列化操作,可以再次将持久化的对象字节流数据通过IO转化为Java对象,结果如下:
- cn.wudimanong.serializable.User@6f496d9f
此时,如果我们再次尝试将User实现Serializable接口的代码部分去掉,发现也无法再文本转换为序列化对象,报错信息为:
1. ava.io.InvalidClassException: cn.wudimanong.serializable.User; class invalid for deserialization
2. at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:157)
3. at java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:862)
4. at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2038)
5. at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1568)
6. at java.io.ObjectInputStream.readObject(ObjectInputStream.java:428)
7. at cn.wudimanong.serializable.SerializableTest.readObj(SerializableTest.java:31)
8. at cn.wudimanong.serializable.SerializableTest.main(SerializableTest.java:44)
提示非法类型转换异常,说明在Java中如何要实现对象的IO读写操作,都必须实现Serializable接口,否则代码就会报错!
序列化&反序列化
通过上面的阐述和示例,相信大家对Serializable接口的作用是有了比较具体的体会了,接下来我们上层到理论层面,看下到底什么是序列化/反序列化。序列化是指把对象转换为字节序列的过程,我们称之为对象的序列化,就是把内存中的这些对象变成一连串的字节(bytes)描述的过程。
而反序列化则相反,就是把持久化的字节文件数据恢复为对象的过程。那么什么情况下需要序列化呢?大概有这样两类比较常见的场景:1)、需要把内存中的对象状态数据保存到一个文件或者数据库中的时候,这个场景是比较常见的,例如我们利用mybatis框架编写持久层insert对象数据到数据库中时;2)、网络通信时需要用套接字在网络中传送对象时,如我们使用RPC协议进行网络通信时;
作者:风平浪静如马
链接:https://juejin.im/post/5de7aa16f265da339b4fe327
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。