网站建设上海,网站添加手机站,效果图网址大全,昆山有建设网站的吗Java 序列化机制是一种将对象转换为字节流的过程#xff0c;以便在网络上传输或保存到文件中#xff0c;并能在需要时将字节流还原为对象。这一机制通过实现 java.io.Serializable 接口来实现#xff0c;同时涉及到一些关键概念和注意事项。 Serializable 接口
Serializabl…Java 序列化机制是一种将对象转换为字节流的过程以便在网络上传输或保存到文件中并能在需要时将字节流还原为对象。这一机制通过实现 java.io.Serializable 接口来实现同时涉及到一些关键概念和注意事项。 Serializable 接口
Serializable 接口是 Java 提供的标记接口没有包含任何需要实现的方法。实现了这个接口的类表明其对象是可序列化的可以被转换为字节流。
public interface Serializable {
}通过实现 Serializable 接口标识类的对象可以被序列化。这使得对象可以在网络上传输或保存到文件中而不失去其状态和结构。
序列化过程
序列化是将对象的状态字段值转换为字节流的过程。这个过程由 ObjectOutputStream 类来完成。序列化使得对象可以以字节流的形式进行存储或传输便于在不同系统之间进行数据交换。如下我们列举几个重要的方法的源码
writeObject方法 public final void writeObject(Object obj) throws IOException {if (enableOverride) {writeObjectOverride(obj);return;}try {writeObject0(obj, false);} catch (IOException ex) {if (depth 0) {writeFatalException(ex);}throw ex;}
}enableOverride 表示是否启用了对象写入的覆盖机制。如果启用会调用 writeObjectOverride 方法来执行对象的特定写入逻辑。
如果没有启用覆盖机制则调用 writeObject0 方法执行实际的对象序列化。
writeObject0 方法负责处理对象的序列化其中第二个参数 false 表示不使用不共享的方式进行序列化。
如果在序列化过程中抛出 IOException 异常会捕获该异常。如果当前深度为0表示不在嵌套序列化过程中则调用 writeFatalException 方法来处理异常否则将异常重新抛出。
writeObject0 private void writeObject0(Object obj, boolean unshared)throws IOException
{boolean oldMode bout.setBlockDataMode(false);depth;try {// handle previously written and non-replaceable objectsint h;if ((obj subs.lookup(obj)) null) {writeNull();return;} else if (!unshared (h handles.lookup(obj)) ! -1) {writeHandle(h);return;} else if (obj instanceof Class) {writeClass((Class) obj, unshared);return;} else if (obj instanceof ObjectStreamClass) {writeClassDesc((ObjectStreamClass) obj, unshared);return;}// check for replacement objectObject orig obj;Class? cl obj.getClass();ObjectStreamClass desc;for (;;) {// REMIND: skip this check for strings/arrays?Class? repCl;desc ObjectStreamClass.lookup(cl, true);if (!desc.hasWriteReplaceMethod() ||(obj desc.invokeWriteReplace(obj)) null ||(repCl obj.getClass()) cl){break;}cl repCl;}if (enableReplace) {Object rep replaceObject(obj);if (rep ! obj rep ! null) {cl rep.getClass();desc ObjectStreamClass.lookup(cl, true);}obj rep;}// if object replaced, run through original checks a second timeif (obj ! orig) {subs.assign(orig, obj);if (obj null) {writeNull();return;} else if (!unshared (h handles.lookup(obj)) ! -1) {writeHandle(h);return;} else if (obj instanceof Class) {writeClass((Class) obj, unshared);return;} else if (obj instanceof ObjectStreamClass) {writeClassDesc((ObjectStreamClass) obj, unshared);return;}}// remaining casesif (obj instanceof String) {writeString((String) obj, unshared);} else if (cl.isArray()) {writeArray(obj, desc, unshared);} else if (obj instanceof Enum) {writeEnum((Enum?) obj, desc, unshared);} else if (obj instanceof Serializable) {writeOrdinaryObject(obj, desc, unshared);} else {if (extendedDebugInfo) {throw new NotSerializableException(cl.getName() \n debugInfoStack.toString());} else {throw new NotSerializableException(cl.getName());}}} finally {depth--;bout.setBlockDataMode(oldMode);}
}反序列化过程
当需要从字节流中恢复对象时Java 序列化机制会将字节流还原为对象的状态。这个过程由 ObjectInputStream 类来完成。如下我们列举几个重要的方法的源码
readObject() private final Object readObject(Class? type)throws IOException, ClassNotFoundException
{if (enableOverride) {return readObjectOverride();}if (! (type Object.class || type String.class))throw new AssertionError(internal error);// if nested read, passHandle contains handle of enclosing objectint outerHandle passHandle;try {Object obj readObject0(type, false);handles.markDependency(outerHandle, passHandle);ClassNotFoundException ex handles.lookupException(passHandle);if (ex ! null) {throw ex;}if (depth 0) {vlist.doCallbacks();}return obj;} finally {passHandle outerHandle;if (closed depth 0) {clear();}}
}这段代码的主要作用是根据给定的类型 (type) 进行对象的反序列化。在这个过程中它使用了一些状态变量如 enableOverride、passHandle、handles、depth、vlist 等来管理反序列化的过程。在处理嵌套对象时它通过 markDependency 方法标记了当前对象与封闭对象的依赖关系。在深度为 0 时执行了clear方法。具体反序列化执行的核心方法是readObject0()
readObject0() private Object readObject0(Class? type, boolean unshared) throws IOException {boolean oldMode bin.getBlockDataMode();if (oldMode) {int remain bin.currentBlockRemaining();if (remain 0) {throw new OptionalDataException(remain);} else if (defaultDataEnd) {/** Fix for 4360508: stream is currently at the end of a field* value block written via default serialization; since there* is no terminating TC_ENDBLOCKDATA tag, simulate* end-of-custom-data behavior explicitly.*/throw new OptionalDataException(true);}bin.setBlockDataMode(false);}byte tc;while ((tc bin.peekByte()) TC_RESET) {bin.readByte();handleReset();}depth;totalObjectRefs;try {switch (tc) {case TC_NULL:return readNull();case TC_REFERENCE:// check the type of the existing objectreturn type.cast(readHandle(unshared));case TC_CLASS:if (type String.class) {throw new ClassCastException(Cannot cast a class to java.lang.String);}return readClass(unshared);case TC_CLASSDESC:case TC_PROXYCLASSDESC:if (type String.class) {throw new ClassCastException(Cannot cast a class to java.lang.String);}return readClassDesc(unshared);case TC_STRING:case TC_LONGSTRING:return checkResolve(readString(unshared));case TC_ARRAY:if (type String.class) {throw new ClassCastException(Cannot cast an array to java.lang.String);}return checkResolve(readArray(unshared));case TC_ENUM:if (type String.class) {throw new ClassCastException(Cannot cast an enum to java.lang.String);}return checkResolve(readEnum(unshared));case TC_OBJECT:if (type String.class) {throw new ClassCastException(Cannot cast an object to java.lang.String);}return checkResolve(readOrdinaryObject(unshared));case TC_EXCEPTION:if (type String.class) {throw new ClassCastException(Cannot cast an exception to java.lang.String);}IOException ex readFatalException();throw new WriteAbortedException(writing aborted, ex);case TC_BLOCKDATA:case TC_BLOCKDATALONG:if (oldMode) {bin.setBlockDataMode(true);bin.peek(); // force header readthrow new OptionalDataException(bin.currentBlockRemaining());} else {throw new StreamCorruptedException(unexpected block data);}case TC_ENDBLOCKDATA:if (oldMode) {throw new OptionalDataException(true);} else {throw new StreamCorruptedException(unexpected end of block data);}default:throw new StreamCorruptedException(String.format(invalid type code: %02X, tc));}} finally {depth--;bin.setBlockDataMode(oldMode);}
}serialVersionUID
serialVersionUID 是用于版本控制的序列化版本号。它是一个长整型数值用于标识类的版本。通过显式声明 serialVersionUID可以在类结构发生变化时依然能够正确地进行反序列化。
Data
public class LoginUserInfo implements Serializable {private static final long serialVersionUID 1L;...
}如果在类中没有明确声明 serialVersionUIDJava 运行时系统会根据类的结构自动生成一个。这种自动生成的 serialVersionUID 是基于类的各个方面的包括字段、方法、父类等。如果类的结构发生变化可能导致自动生成的 serialVersionUID 发生变化。这可能会导致在反序列化时类的版本不一致从而导致 InvalidClassException 异常。
所以显式声明 serialVersionUID是确保反序列化过程正确的关键避免因类结构变化而导致的问题。
transient 关键字
关键字 transient 用于标记字段表示在对象序列化的过程中这个字段应该被忽略。例如如果一个类有一个不希望被序列化的缓存字段可以使用 transient 关键字来避免将其写入序列化数据。例如ArrayList、LinkedList 等类中的一些属性就是使用transient修饰的 自定义序列化和反序列化
有时候可能需要自定义序列化和反序列化的过程以满足特定需求。可以通过实现 writeObject 和 readObject 方法来实现自定义逻辑。如ArrayList类中就是通过自定义的序列化和反序列化方法:
private void writeObject(java.io.ObjectOutputStream s)throws java.io.IOException{// Write out element count, and any hidden stuffint expectedModCount modCount;s.defaultWriteObject();// Write out size as capacity for behavioural compatibility with clone()s.writeInt(size);// Write out all elements in the proper order.for (int i0; isize; i) {s.writeObject(elementData[i]);}if (modCount ! expectedModCount) {throw new ConcurrentModificationException();}
}/*** Reconstitute the ttArrayList/tt instance from a stream (that is,* deserialize it).*/
private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {elementData 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 capacityint capacity calculateCapacity(elementData, size);SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);ensureCapacityInternal(size);Object[] a elementData;// Read in all elements in the proper order.for (int i0; isize; i) {a[i] s.readObject();}}
}
序列化不保存静态变量
对象状态 vs. 类状态
序列化的主要目的是保存对象的状态即对象的实例变量。静态变量是类级别的它们对于每个对象实例都是相同的。序列化关注的是对象的实例状态因为这是对象在不同环境中重建时所需的关键信息。
节省空间
静态变量通常用于表示类级别的常量或共享数据这些数据在所有对象实例之间是相同的。如果每个对象的静态变量都被序列化并存储将导致冗余浪费存储空间。序列化的目标之一是尽可能紧凑地保存对象的状态因此不保存静态变量是一种优化。
不需要还原
静态变量在类加载时初始化并在整个应用程序的生命周期内保持不变。因此在反序列化时不需要重新初始化静态变量。序列化和反序列化的目标是保存和还原对象的动态状态而不是类级别的静态状态。
序列化的安全性和性能考虑
在实际应用中需要注意序列化的安全性和性能。反序列化过程中可能存在安全风险因此要谨慎处理来自不受信任源的序列化数据。此外对于大量数据的序列化可能会影响系统性能可以考虑使用更高效的序列化工具或压缩算法。
总结
综合来看Java 序列化的核心思想是将对象的状态转换为字节流并通过 ObjectOutputStream 类完成这一过程。该类在内部处理了对象引用的记录、对象字段的写入、自定义写入方法的执行等。在实际应用中我们需要注意序列化版本控制、对象字段的 transient 关键字的处理以及序列化性能等方面的问题。
请注意Java 序列化机制在现代应用中可能会遇到一些挑战包括性能问题、安全性问题以及与其他语言的兼容性等。因此在一些场景下开发者可能会考虑使用其他序列化框架如 JSON 或 Protocol Buffers以满足不同的需求。