前言
以前看别人博客,设计到操作,都会让实现 Serializable,知道这叫序列化与反序列化,但什么是序列化与反序列化,不得而知,最近在深入学习IO专题,也就学习下序列化与反序列化。也接触到ArrayList源码,再一次佩服写jdk的那些大神。
概要
序列化与反序列化,应该叫对象的序列化与反序列化。
对象的序列化,就是将Object转换成byte序列,反之叫对象的反序列化。
序列化流(ObjectOutputStream),是过滤流 ----> writeObject 反序列化流(ObjectInputStream) -----> readObject
序列化接口(Serializable)
对象必须实现序列化接口,才能实现序列化,否则将出现异常。
这个接口,里面没有任何方法,这只是一个标准。
对象序列化
1、序列化代码
// 序列化public static void test1(String file) { try { ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream(file)); Student student = new Student(1, "张三", 20); oos.writeObject(student); oos.flush(); oos.close(); } catch (IOException e) { e.printStackTrace(); }}
2、前面说到必须实现序列化接口,那我们实现看看会有什么异常
package com.fengwenyi.demo.io.serialize;/** * @author Wenyi Feng */public class Student { private String no; private String name; private int age; public Student() { } public Student(String no, String name, int age) { this.no = no; this.name = name; this.age = age; } // getter , setter and toString}
测试结果:
3、实现序列化接口并完善
对象实现序列化
package com.fengwenyi.demo.io.serialize;import java.io.Serializable;/** * @author Wenyi Feng */public class Student implements Serializable { private static final long serialVersionUID = 1591370122074648558L; private String no; private String name; private int age; public Student() { } public Student(String no, String name, int age) { this.no = no; this.name = name; this.age = age; } // getter , setter and toString}
反序列化
// 反序列化public static void test2(String file) { try { ObjectInputStream ois = new ObjectInputStream( new FileInputStream(file)); Student student = (Student) ois.readObject(); System.out.println(student.toString()); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); }}
测试代码:
public static void main(String[] args) { String file = "E:\\Workspace\\IdeaStudio\\io-demo\\serialize-demo\\demo\\obj.dat"; test1(file); test2(file);}
测试结果:
通过ArrayList源码学习transient关键字
transient关键字引入
/** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. */transient Object[] elementData; // non-private to simplify nested class access
序列化:
/** * Save the state of the ArrayList instance to a stream (that * is, serialize it). * * @serialData The length of the array backing the ArrayList * instance is emitted (int), followed by all of its elements * (each an Object) in the proper order. */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
反序列化:
/** * Reconstitute the ArrayList 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 stuff s.defaultReadObject(); // Read in capacity s.readInt(); // ignored if (size > 0) { // be like clone(), allocate array based upon size not capacity int 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 i=0; i
为什么要这么做呢?
有两点:
1、String 已实现序列化接口,Ineger并没有实现
public final class String implements java.io.Serializable, Comparable, CharSequence {public final class Integer extends Number implements Comparable {
是不是就是这个原因呢?显然不是,因为刚刚我们已经证明了。
2、数组需要提前预定数组大小,当然,我们设定的大小并没有完全填满,这就不需要都去序列化。
for (int i=0; i
继承
情形一:父类实现序列化接口
class A implements Serializable { public A() { System.out.println("A...."); }}class A1 extends A { public A1() { System.out.println("A1..."); }}class A2 extends A1 { public A2() { System.out.println("A2..."); }}
测试代码:
/** * A:父类实现序列化 */public static void testA() { try { String fileA = "E:\\Workspace\\IdeaStudio\\io-demo\\serialize-demo\\demo\\fileA.dat"; ObjectOutputStream oosA = new ObjectOutputStream(new FileOutputStream(fileA)); A2 a2 = new A2(); oosA.writeObject(a2); System.out.println("------------------------------"); ObjectInputStream oisA = new ObjectInputStream(new FileInputStream(fileA)); A2 aa2 = (A2) oisA.readObject(); System.out.println(aa2); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); }}
测试截图:
情形二:子类实现序列化接口1
class B { public B() { System.out.println("B...."); }}class B1 extends B implements Serializable { public B1() { System.out.println("B1..."); }}class B2 extends B1 { public B2() { System.out.println("B2..."); }}
测试代码:
/** * B1:子类实现序列化 */public static void testB1() { try { String fileB1 = "E:\\Workspace\\IdeaStudio\\io-demo\\serialize-demo\\demo\\fileB1.dat"; ObjectOutputStream oosB = new ObjectOutputStream(new FileOutputStream(fileB1)); B2 b2 = new B2(); oosB.writeObject(b2); System.out.println("------------------------------"); ObjectInputStream oisB = new ObjectInputStream(new FileInputStream(fileB1)); B2 bb2 = (B2) oisB.readObject(); System.out.println(bb2); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); }}
测试结果:
情形三:子类实现序列化2
class B3 extends B { public B3() { System.out.println("B3..."); }}class B4 extends B3 implements Serializable { public B4() { System.out.println("B4..."); }}
测试代码:
/** * B2:子类实现序列化 */public static void testB2() { try { String fileB2 = "E:\\Workspace\\IdeaStudio\\io-demo\\serialize-demo\\demo\\fileB2.dat"; ObjectOutputStream oosB = new ObjectOutputStream(new FileOutputStream(fileB2)); B4 b4 = new B4(); oosB.writeObject(b4); System.out.println("------------------------------"); ObjectInputStream oisB = new ObjectInputStream(new FileInputStream(fileB2)); B4 bb4 = (B4) oisB.readObject(); System.out.println(bb4); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); }}
测试结果:
总结一下:
序列化,不论是父类实现序列化接口,还是子类实现序列化结果,都会调用构成方法。
反序列化,父类实现序列化接口,则不调用构造方法;子类实现序列化接口,父类(所有父类)都会调用构造方法。
后记
序列化与反序列化到底有什么用,还不清楚,还得继续学习。
1、本节测试代码:
2、学习视频: