序列化及反序列化的底层原理今天我们深入分析一下JAVA序列化及反序列化的原理 。
为了方便读者理解,下面通过ArrayList的序列化来展开介绍Java是如何实现序列化及反序列化的 。
在介绍ArrayList序列化之前,先考虑一个问题:
如何自定义序列化和反序列化的策略?带着这个问题,我们看一下java.util.ArrayList的源码:
public class ArrayList<E> extends AbstractList<E>implements List<E>, Randomaccess, Cloneable, java.io.Serializable{private static final long serialVersionUID = 8683452581122892189L;transient Object[] elementData; // non-private to simplify nested class accessprivate int size;}
上面的代码中忽略了其他成员变量,ArrayList实现了java.io.Serializable接口,我们对它进行序列化及反序列化 。
我们看到,ArrayList中的elementData被定义为transient类型,而被定义为transient类型的成员变量不会被序列化而保留下来 。
我们写一个Demo,验证一下我们的想法:
public static void main(String[] args) throws IOException, ClassNotFoundException {List<String> stringList = new ArrayList<String>();stringList.add("hello");stringList.add("world");stringList.add("hollis");stringList.add("chuang");System.out.println("init StringList" + stringList);ObjectOutputStream objectOutputStream = new ObjectOutputStream(newFileOutputStream("stringlist"));objectOutputStream.writeObject(stringList);IOUtils.close(objectOutputStream);File file = new File("stringlist");ObjectInputStream objectInputStream = new ObjectInputStream(newFileInputStream(file));List<String> newStringList = (List<String>)objectInputStream.readObject();IOUtils.close(objectInputStream);if(file.exists()){file.delete();}System.out.println("new StringList" + newStringList);}// init StringList[hello, world, hollis, chuang]// new StringList[hello, world, hollis, chuang]
了解ArrayList的读者都知道,ArrayList底层是通过数组实现的 。那么数组elementData其实就是用来保存列表中的元素的 。通过该属性的声明方式我们知道,它是无法通过序列化持久化下来的 。
那么为什么上面代码的结果却通过序列化和反序列化把List中的元素保留下来了呢?
1. writeObject 和readObject 方法在ArrayList中定义了两个方法:writeObject和readObject 。
这里先给出结论:在序列化过程中,如果被序列化的类中定义了writeObject和readObject方法,那么虚拟机会试图调用对象类中的writeObject和readObject方法进行用户自定义的序列化和反序列化操作 。
如果没有这样的方法,则默认调用的是ObjectOutputStream的defaultWriteObject方法和ObjectInputStream的defaultReadObject方法 。
用户自定义的writeObject和readObject方法允许用户控制序列化的过程,比如可以在序列化的过程中动态改变序列化的数值 。
下面看一下这两个方法的具体实现:
private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {elementData = https://www.isolves.com/it/cxkf/yy/JAVA/2022-08-10/EMPTY_ELEMENTDATA;// Read in size, and any hidden stuffs.defaultReadObject();// Read in capacitys.readInt(); // ignoredif (size > 0) {// be like clone(), allocate array based upon size not capacityensureCapacityInternal(size);Object[] a = elementData;// Read in all elements in the proper order.for (int i=0; i
2. 为什么使用transientArrayList实际上是动态数组,每次在放满以后自动增长设定的长度值,如果数组自动增长的长度设为100,而实际只放了1个元素,那么就会序列化99个null元素 。为了保证不会对这么多null元素同时进行序列化,ArrayList把元素数组设置为transient 。
3. 为什么重写writeObject 和readObject前面说过,为了防止一个包含大量空对象的数组被序列化,以及优化存储,ArrayList使用transient来声明elementData 。
但是,作为一个集合,在序列化过程中还必须保证其中的元素可以被持久化下来,所以,通过重写writeObject和readObject方法的方式把其中的元素保留下来 。
● writeObject方法把elementData数组中的元素遍历地保存到输出流(ObjectOutputStream)中 。
推荐阅读
- |职场中有哪些常见的套路?
- 腾讯|福布斯2022中国数字经济100强出炉:腾讯第一 小米第十
- 幼儿园中班计划精华篇 中班班务计划
- 中学生原创科幻作文?梦回唐朝 梦回唐朝作文
- |29岁失业中年郎的自述:职场遇到不公
- 老干妈油辣椒的做法
- 鸭血吃多了会怎么样
- 油闷辣椒的做法
- 推荐几部我心中好看的穿越剧 比较好看的穿越剧
- |职场中偷懒懒惰的人,都是这样做的