java 序列化机制深度解析
来源:互联网 发布:mysql insert 双引号 编辑:程序博客网 时间:2024/06/01 23:47
概要
- 序列化机制允许将实现序列化的Java对象转换为字节序列,这些字节序列可以被保存在磁盘上或通过网络传输,以备以后重新恢复原来的对象,序列化机制使得对象可以脱离程序的运行而独立存在
- 可序列化的类包括:实现了Serializable的类,数组,枚举,String类也是可序列化对象
- 由于序列化保存的是对象的状态,因此不会保存类的静态变量
-通过ObjectOutputStream和ObjectInputStream对对象进行序列化及反序列化 - 虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID)
- Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中
- 反序列化对象时必须有序列化对象的class文件
- 通过文件、网络来读取序列化的对象时,必须按实际写入顺序读取;
不被序列化的内容
- 静态变量不会被序列化
- transient类型成员变量不会被序列化
可序列化的类型
- 实现Serializable接口的类
- String类
- java基本类型
- Enum 枚举类型
- 数组类型
public class Demo { public static void main(String[] args) throws Exception { ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("tempFile")); ObjectInputStream in = new ObjectInputStream(new FileInputStream("tempFile")); out.writeObject("hello world"); out.writeObject(new int[]{1, 2, 3}); out.writeObject(Color.BLACK); out.writeObject(new Data(1, "one")); out.writeObject(new Integer(1)); System.out.println(in.readObject()); System.out.println(Arrays.toString((int[]) (in.readObject()))); System.out.println((Color) in.readObject()); Data data = (Data) in.readObject(); System.out.println(data); System.out.println(in.readObject()); }}enum Color { BLACK, RED}class Data implements Serializable { int x; String s; public Data(int x, String s) { this.x = x; this.s = s; } @Override public String toString() { return x + " " + s; }}/* 结果:hello world[1, 2, 3]BLACK1 one1 */
递归序列化
- 当某个对象进行序列化的时候,系统会自动把该对象的所有Field依次进行序列化
- 如果某个Field引用到另一个对象,则被引用的对象也会被序列化
- 如果被引用的对象的Field也引用了其它对象,则被引用的对象也会被序列化。可序列化的类的成员变量也必须是可序列化的,否则在序列化过程中抛出异常
public class Demo { public static void main(String[] args) throws Exception { ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("tempFile")); ObjectInputStream in = new ObjectInputStream(new FileInputStream("tempFile")); out.writeObject(new Data(1, "one")); Data data = (Data) in.readObject(); System.out.println(data); }} class Data implements Serializable { int x; String s; SubData subdata = new SubData(1); public Data(int x, String s) { this.x = x; this.s = s; } @Override public String toString() { return x + " " + s + subdata.x; }}class SubData { int x; public SubData(int x) { this.x = x; }}
定制序列化
在序列化过程中,如果被序列化的类中定义了writeObject 和 readObject 方法,虚拟机会试图调用对象类里的 writeObject 和 readObject 方法,进行用户自定义的序列化和反序列化。如果没有这样的方法,则默认调用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。用户自定义的 writeObject 和 readObject 方法可以允许用户控制序列化的过程,比如可以在序列化的过程中动态改变序列化的数值。
实例 ArrayList的序列化
- ArrayList实现了Serializable接口默认会将类中所有对象都出到输出流中去
- ArrayList用来保存元素的数组被声明为transient,也就是不会被序列化
- ArrayList重写了writeObject与ReadObject方法,对于数组元素,只输出实例元素,不会输出因为扩容而导致的数组末尾的null值,提高了序列化的效率
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 behavioural 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 { elementData = EMPTY_ELEMENTDATA; // Read in size, and any hidden stuff s.defaultReadObject(); // Read in capacity s.readInt(); // ignored if (size > 0) { // be like clone(), allocate array based upon size not capacity ensureCapacityInternal(size); Object[] a = elementData; // Read in all elements in the proper order. for (int i=0; i<size; i++) { a[i] = s.readObject(); } } }
替换序列化对象
wirteReplace():只要该方法存在,由序列化机制调用,在序列化对象时将该对象替换成其它对象;
public class Person4 implements Serializable { private String name; private int age; public Person4(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } private Object writeReplace() throws ObjectStreamException { ArrayList<Object> list = new ArrayList<Object>(); list.add(name); list.add(age); return list; } public static void main(String[] args) { try { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("replace.txt")); ObjectInputStream ooi = new ObjectInputStream(new FileInputStream("replace.txt")); Person4 per = new Person4("孙悟空", 500); // 系统将per对象转换成字节序列并输出 oos.writeObject(per); // 反序列化读取得到的是ArrayList ArrayList list = (ArrayList) ooi.readObject(); System.out.println(list.toString()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } }}
序列化机制深入分析
- 首先序列化与反序列化是通过ObjectOutputStream和ObjectInputStream的writeObject和readObject方法实现的
- Serializable是一个空的标志接口,序列化的机制在于ObjectOutputStream的writeObject方法中,下面的代码可以看出可序列化的类型包含String,数组,枚举,以及实现了Serializable的类
if (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()); } }
- 实现了Serializable接口的类的序列化的方法调用过程
writeObject ---> writeObject0 --->writeOrdinaryObject--->writeSerialData
- 序列化类中存在WriteObject方法时,writeSerialData方法会通过反射机制来调用WriteObject方法
if (slotDesc.hasWriteObjectMethod()) { ... slotDesc.invokeWriteObject(obj, this);
serialVersonUID
序列化版本兼容
反序列化Java对象时,必须提供该对象的class文件,随着项目的升级,系统的class文件也会升级。如果保持两个class文件兼容性:
- Java的序列化机制允许为序列化类提供一个private static final的serialVersonUID值,该Field值用于标识该Java类的序列化版本;
- 最好在每个要序列化的类中加入private static final long serialVersionUID这个Field,具体数值自己定义。这样,即使对象被序列化之后,它所对应的类修改了,该对象也依然可以被正确反序列化;
如果不显示定义serialVersionUID值:
- 该Field值将由JVM根据类的相关信息计算,而修改后的类计算结果与修改前的类计算结果往往不同,从而造成对象的反序列化因为类版本的不兼容失败;
- 不利于程序在不同的JVM之间移植,因为不同的编译器计算该Field的值计算策略可能不同,从而造成虽然类完全没有改变,但是因为JVM不同,也会出现序列化版本不兼容而无法正确反序列化的现象;
多次序列化同一对象
- 当程序视图序列化一个对象时,程序将先检查该对象是否已经被序列化过,只有该对象从未(在本次虚拟机)中序列化过,系统才会将该对象转换成字节序列并输出; - 如果某个对象已经序列化过,程序将只是直接输出一个序列化编号,而不是再次重新序列化该对象,因此当重复序列化同一个对象时即便变量域已经改变,反序列化时还是只会保存第一个序列化对象的状态```javapublic class SerializaMutable { public static void main(String[] args) { try { // 创建一个ObjectOutputStream对象 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("mutable.txt")); // 创建一个ObjectInputStream对象 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("mutable.txt")); Person per = new Person("孙悟空", 500); // 系统将per对象转换成字节序列并输出 oos.writeObject(per); per.setName("猪八戒"); // 系统只是输出序列化编号,所以改变后的name不会被序列化 oos.writeObject(per); Person p1 = (Person) ois.readObject(); Person p2 = (Person) ois.readObject(); // 下面输出true,即反序列化后p1等于p2 System.out.println(p1 == p2); // 下面依然看到输出“孙悟空”,即改变后的Field没有被序列化 System.out.println(p2.getName()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } }}class Person implements Serializable { String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; }} /*结果:true孙悟空 */ <div class="se-preview-section-delimiter"></div>
Externalizable 序列化机制
- 自定义序列化的成员变量
- void readExternal(ObjectInput in):实现反序列化,调用DataInput(ObjectInput的父接口)的方法来恢复基本类型的Field值,调用ObjectInput的readObject()方法来恢复引用类型的Field值;
- void writeExternal(ObjectOutput out):实现序列化,调用DataOutput(ObjectOutput的父接口)的方法来保存基本类型的Field值,调用ObjectOutput的writeObject()方法来保存引用类型的Field值;
public class Person5 implements Externalizable { private String name; private int age; // 必须提供无参数的构造函数,否则报java.io.InvalidClassException public Person5() { super(); } // 注意此处没有提供无参数的构造器 public Person5(String name, int age) { super(); System.out.println("有参数的构造器"); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // 将去读的字符串反转后赋值给name Field this.name = ((StringBuffer) in.readObject()).reverse().toString(); this.age = in.readInt(); } @Override public void writeExternal(ObjectOutput out) throws IOException { // 将name Field值反转后写入二进制流 out.writeObject(new StringBuffer(name).reverse()); out.writeInt(age); } public static void main(String[] args) { try { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("external.txt")); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("external.txt")); Person5 per = new Person5("孙悟空", 500); oos.writeObject(per); Person5 p = (Person5) ois.readObject(); System.out.println(p.getName() + ":" + p.getAge()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } }}
序列化机制对比
- 实现Serializable接口:
- 系统自动存储必要的信息;
- Java内建支持,抑郁实现,只需要实现该接口即可,无需任何代码支持;
性能略差;
- 实现Externalizable接口:
- 程序员决定存储那些信息;
- 仅仅提供两个空方法,实现该接口必须为两个空方法提供实现;
- 性能略好
- java 序列化机制深度解析
- Java 序列化机制解析
- 解析java序列化机制
- 深度解析java接口回调机制
- java 深度解析JVM内存分布机制
- java序列化机制
- Java序列化机制
- java序列化机制
- Java序列化机制
- Java 序列化机制
- Java序列化机制
- Java序列化机制
- java序列化深度克隆
- 【Java】Java序列化机制
- JAVA对象序列化机制
- 浅析Java序列化机制
- java序列化机制学习
- JAVA对象序列化机制
- Semantic Web 参考资料
- Html5 postmessage 子父窗口传值
- ORA-01000: 超出打开游标的最大数(解决及原因)
- Map的迭代方式
- npm vs Bower区别
- java 序列化机制深度解析
- EditText不自动获得输入焦点(以及 获得焦点但不弹出输入法软键盘的方法)
- 获取android 屏幕分辨率的两种方式
- Android中自定义标题栏样式的两种方法
- 补做实验1
- QuartZ CronTrigger表达式
- 10个学习Android开发的网站推荐
- 欢迎使用CSDN-markdown编辑器
- 图片拉伸变形的解决办法(气泡)