一、概念:
1、序列化
将数据结构或对象转换成二进制串的过程。
2、反序列化
将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。
3、Serializable接口
是 Java 提供的序列化接口,它是一个空接口:
public interface Serializable {}
Serializable 用来标识当前类可以被 ObjectOutputStream 序列化,以及被 ObjectInputStream 反序列化。
Serializable 有以下几个特点:
- 可序列化类中,未实现 Serializable 的属性状态无法被序列化/反序列化;
- 也就是说,反序列化一个类的过程中,它的非可序列化的属性将会调用无参构造函数重新创建;
- 因此这个属性的无参构造函数必须可以访问,否者运行时会报错;
- 一个实现序列化的类,它的子类也是可序列化的;
4、Binder
e0b16692aec8aa7d32835068bda93e6.png 9fe4e9af2e59dbe6b493a5985019197.png5、Parcelabel 与Serializable的区别
72fbf4f1e11383a60306a6ee008a44b.png二、面试:
1、反序列化后的对象会重新调用构造函数吗?
答:不会, 因为是从二进制直接解析出来的. 适用的是 Object 进行接收再强转, 因此不是原来的那个对象
2、序列化与反序列化后的对象是什么关系,是("=="还是equal?是浅复制还是深复制?)?
答:是一个深拷贝, 前后对象的引用地址不同
3、Android 为什么要设计 bundle 而不是使用 HashMap 结构?
答:bundle 内部适用的是 ArrayMap, ArrayMap 相比 Hashmap 的优点是, 扩容方便, 每次扩容是原容量的一半, 在[百量] 级别, 通过二分法查找 key 和 value (ArrayMap 有两个数组, 一个存放 key 的 hashcode, 一个存放 key+value 的 Entry) 的效率要比 hashmap 快很多, 由于在内存中或者 Android 内部传输中一般数据量较小, 因此用 bundle 更为合适
4、serializableVersionUID 的作用是?
用于数据的版本控制, 如果反序列化后发现 ID 不一样, 认为不是之前序列化的对象
5、Android 中 intent/bundle 的通信原理以及大小限制?
答: Android 中的 bundle 实现了 parcelable 的序列化接口, 目的是为了在进程间进行通讯, 不同的进程共享一片固定大 小的内存, parcelable 利用 parcel 对象的 read/write 方法, 对需要传递的数据进行内存读写, 因此这一块共享内存不能 过大, 在利用 bundle 进行传输时, 会初始化一个 BINDER_VM_SIZE 的大小 = 1 * 1024 * 1024 - 4096 * 2, 即便通过 修改 Framework 的代码, bundle 内核的映射只有 4M, 最大只能扩展到 4M.
6、为何 Intent 不能直接在组件间传递对象而要通过序列化机制?
答: 因为 Activity 启动过程是需要与 AMS 交互, AMS 与 UI 进程是不同一个的, 因此进程间需要交互数据, 就必须序列化。
7、序列化与持久化的关系和区别?
答: 序列化是为了进程间数据交互而设计的, 持久化是为了把数据存储下来而设计的
8、什么是 serialVersionUID ?如果你不定义这个, 会发生什么?
假设你有一个类,它序列化并存储在持久性中, 然后修改了该类以添加新字 段。如果对已序列化的对象进行反序列化, 会发生什么情况?
serialVersionUID 是一个 private static final long 型 ID, 当它被印在对象上时, 它通常是
对象的哈希码,你可以使用 serialver 这个 JDK 工具来查看序列化对象的 serialVersionUID。
SerialVerionUID 用于对象的版本控制。也可以在类文件中指定 serialVersionUID。不指定
serialVersionUID的后果是,当你添加或修改类中的任何字段时, 则已序列化类将无法恢复, 因为
为新类和旧序列化对象生成的 serialVersionUID 将有所不同。Java 序列化过程依赖于正确的序
列化对象恢复状态的, ,并在序列化对象序列版本不匹配的情况下引发
9、序列化时,你希望某些成员不要序列化?你如何实现它?
有时候也会变着形式问,比如问什么是瞬态 trasient 变量, 瞬态和静态变量会不会得到序列化等,所以,如果你不希望任何字段是对象的状态的一部分, 然后声明它静态或瞬态根据你的需要, 这样就不会是在 Java 序列化过程中被包含在内
10、如果类中的一个成员未实现可序列化接口, 会发生什么情况?
如果尝试序列化实现可序列化的类的对象,但该对象包含对不可序列化类的引用,则在运行时将引发不可序列化异常 NotSerializableException.
11、如果类是可序列化的, 但其超类不是, 则反序列化后从超级类继承的实例
变量的状态如何?
Java 序列化过程仅在对象层次都是可序列化结构中继续, 即实现 Java 中的可序列化接口, 并且
从超级类继承的实例变量的值将通过调用构造函数初始化, 在反序列化过程中不可序列化的超级类
12、是否可以自定义序列化过程, 或者是否可以覆盖 Java 中的默认序列化过
程?
是的,Java 中的序列化过程是可以自定义的,可以通过实现 java.io.Serializable 接口的 writeObject 和 readObject 方法来完成自定义序列化和反序列化过程。这样可以覆盖默认的序列化过程,实现更灵活的序列化和反序列化操作。
13、假设新类的超级类实现可序列化接口, 如何避免新类被序列化?
对于序列化一个对象需调用 ObjectOutputStream.writeObject(saveThisObject), 并用
ObjectInputStream.readObject() 读取对象, 但 Java 虚拟机为你提供的还有一件事, 是定义这
两个方法。如果在类中定义这两种方法, 则 JVM 将调用这两种方法, 而不是应用默认序列化机制。你可以在此处通过执行任何类型的预处理或后处理任务来自定义对象序列化和反序列化的行为。
14、在 Java 中的序列化和反序列化过程中使用哪些方法?
Java 中的序列化和反序列化过程中,常用的方法有以下几个:
1、java.io.Serializable
接口
Java 中的序列化和反序列化操作需要使用
java.io.Serializable
接口,该接口没有任何方法,只是用于标记一个类可以被序列化。如果一个类实现了Serializable
接口,则可以将该类的对象序列化和反序列化。
2、java.io.ObjectOutputStream
和 java.io.ObjectInputStream
类java.io.ObjectOutputStream
和 java.io.ObjectInputStream
类是 Java 中用于序列化和反序列化的核心类,它们提供了一些方法来实现对象的序列化和反序列化。
比如:
-
ObjectOutputStream.writeObject(Object obj)
:将一个对象写入序列化流中。 -
ObjectInputStream.readObject()
:从序列化流中读取一个对象。java.io.Externalizable 接口
3、java.io.Externalizable
接口也可以用于自定义序列化和反序列化过程,
它提供了两个方法:
writeExternal
和readExternal
,用于自定义序列化和反序列化过程。与 Serializable 接口不同的是,实现Externalizable 接口
的类需要显式地定义无参构造函数。
总的来说,Java 中的序列化和反序列化过程是通过实现 Serializable 接口
并使用 ObjectOutputStream
和 ObjectInputStream
类来实现的。如果需要自定义序列化和反序列化过程,则可以实现 Externalizable
接口并重新定义 writeExternal
和 readExternal
方法。
网友评论