企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## Java专题二十一:序列化与反序列化 [TOC] > 用于保存对象的状态并持久化,如:保存到磁盘、在网络上传输等 - 序列化:对象转换成字节序列的过程 - 反序列化:字节序列转换成对象的过程 ### 20.1. 实现方法 - 对象序列化必须实现`java.io.Serializable`接口,否则做序列化和反序列化操作中会抛出`java.io.NotSerializableException`异常 - 因为`java.io.Serializable`是一个空的接口,只是用来标识对象可序列化,所以实现序列化操作,对象还需额外实现`writeObject`、`readObject`方法,如`java.util.ArrayList`中 ``` public class ArrayList implements java.io.Serializable { private static final long serialVersionUID = 8683452581122892189L; private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { // Write out element count, and any hidden stuff int expectedModCount = modCount; s.defaultWriteObject(); // Write out size as capacity for behavioral compatibility with clone() s.writeInt(size); // Write out all elements in the proper order. for (int i=0; i<size; i++) { s.writeObject(elementData[i]); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } } private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in size, and any hidden stuff s.defaultReadObject(); // Read in capacity s.readInt(); // ignored if (size > 0) { // like clone(), allocate array based upon size not capacity SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, size); Object[] elements = new Object[size]; // Read in all elements in the proper order. for (int i = 0; i < size; i++) { elements[i] = s.readObject(); } elementData = elements; } else if (size == 0) { elementData = EMPTY_ELEMENTDATA; } else { throw new java.io.InvalidObjectException("Invalid size: " + size); } } } ``` - `java.io.ObjectOutputStream`用于将对象写入字节流中,一些重要方法如下: | 方法 | 说明 | | --- | --- | | `void defaultWriteObject()`<br> `throws IOException` | 写入当前类的非static、非transient字段到流中 | | `void writeObject(Object obj)`<br> `throws IOException` | 写入特定的对象到流中 | | `void writeInt(int val)`<br> `throws IOException` | 写入32bit的int型数据到流中 | | `void writeChars(String s) throws IOException` | 写入字符数据到流中 | - `java.io.ObjectInputStream`用于从流中读取对象,一些重要方法如下: | 方法 | 说明 | | --- | --- | | `void defaultReadObject()`<br> `throws IOException, ClassNotFoundException` | 从流中读取非static、非transient字段到当前类中 | | ` Object readObject()`<br> `throws IOException, ClassNotFoundException` | 将特定的对象写入流中 | | `int readInt()`<br> `throws IOException, ClassNotFoundException` | 将32bit的int型数据写入流中 | 如在ArrayList中`writeObject`方法: - `s.defaultWriteObject()`:默认第一步都调用该方法 - `s.writeInt(size)`:先写入数组中的元素个数,便于后面按个数调用`readObject`方法读取数组中每个元素 - `s.writeObject(elementData[i])`:因为ArrayList内部就是一个对象构成的数组,这里遍历数组写入每个元素 `readObject`方法: - `s.defaultReadObject()`:默认第一步都调用该方法 - `s.readInt()`:读取元素的个数 - `s.readObject()`:读取每个元素 ### 20.2. serialVersionUID serialVersionUID将每个可序列化的类与一个版本号关联起来,因为我们写的类不是一成不变的,会随着需求的变化而改变 如果在反序列化中,反序列化后的类与当前类的serialVersionUID不同时,会抛出InvalidClassException异常 一般声明一个long类型的数值,并用`private`、`static`、`final`修饰: ~~~ private static final long serialVersionUID = 8683452581122892189L; ~~~ ### 20.3. transient 用于修饰对象的成员变量,表示不参与序列化过程,不会保存到字节序列中