一、readObject和writeObject
通过上个章节的Java序列化与反序列化入门理解,对序列化和反序列化应该有了比较基本的认识。回顾一下,之前的序列化和反序列化,只是简单的处理,如果需要二次加工需要如何处理?比如序列化的时候需要对数据进行加密操作,对应的反序列化时候需要进行解密操作等。先看下面的例子:
1package com.test; 2 3import java.io.IOException; 4import java.io.ObjectInputStream; 5import java.io.ObjectOutputStream; 6import java.io.Serializable; 7 8public class Person implements Serializable{ 9 10 private static final long serialVersionUID = 3482314192692351792L; 11 private int weight; 12 private int age; 13 private String name; 14 public int getWeight() { 15 return weight; 16 } 17 public void setWeight(int weight) { 18 this.weight = weight; 19 } 20 public int getAge() { 21 return age; 22 } 23 public void setAge(int age) { 24 this.age = age; 25 } 26 public String getName() { 27 return name; 28 } 29 public void setName(String name) { 30 this.name = name; 31 } 32 33 /** 34 * 新增加的方法,序列化时会调用 35 * @param stream 36 * @throws IOException 37 */ 38 private void writeObject(ObjectOutputStream stream)throws IOException{ 39 System.out.println("invoke writeObject......"); 40 //此时进行对象的序列化 41 stream.defaultWriteObject(); 42 //额外序列化的数值,反序列化的时候会使用 43 stream.writeInt(12345); 44 } 45 46 /** 47 * 新增加的方法,反序列化时会调用 48 * @param stream 49 * @throws IOException 50 * @throws ClassNotFoundException 51 */ 52 private void readObject(ObjectInputStream stream)throws IOException,ClassNotFoundException{ 53 System.out.println("invoke readObject......"); 54 //此时进行默认的序列化 55 stream.defaultReadObject(); 56 System.out.print("默认初始化后的对象:"); 57 System.out.println(this); 58 this.age = stream.readInt(); 59 System.out.print("对象的age进行加工后:"); 60 } 61 62 @Override 63 public String toString() { 64 return "[weight:"+weight+",name:"+name+",age:"+age+"]"; 65 } 66} 67 68测试代码: 69import java.io.FileInputStream; 70import java.io.FileOutputStream; 71import java.io.ObjectInputStream; 72import java.io.ObjectOutputStream; 73import java.util.Arrays; 74 75import com.test.Person; 76import com.test.User; 77 78public class TestPerson { 79 80 /** 81 * 序列化 82 * 83 * @param filePath 84 * 序列化要写入的文件路径 85 * @throws Exception 86 */ 87 public static void writeObject(String filePath) throws Exception { 88 Person p = new Person(); 89 p.setAge(18); 90 p.setName("testPersonName"); 91 p.setWeight(120); 92 93 ObjectOutputStream oos = null; 94 try { 95 oos = new ObjectOutputStream(new FileOutputStream(filePath)); 96 oos.writeObject(p); 97 oos.flush(); 98 } finally { 99 if (oos != null) { 100 oos.close(); 101 } 102 } 103 } 104 105 /** 106 * 反序列化 107 * 108 * @param filePath 109 * 序列化的文件 110 * @throws Exception 111 */ 112 public static void readObject(String filePath) throws Exception { 113 ObjectInputStream ois = null; 114 try { 115 ois = new ObjectInputStream(new FileInputStream(filePath)); 116 Person p = (Person) ois.readObject(); 117 System.out.println(p); 118 } finally { 119 if (ois != null) { 120 ois.close(); 121 } 122 } 123 } 124 125 public static void test(long num) { 126 byte[] byteNum = new byte[8]; 127 for (int ix = 0; ix < 8; ++ix) { 128 int offset = 64 - (ix + 1) * 8; 129 byteNum[ix] = (byte) ((num >> offset) & 0xff); 130 } 131 System.out.println(Arrays.toString(byteNum)); 132 } 133 134 public static void main(String[] args) throws Exception { 135 String filePath = "f:/obj.out"; 136 writeObject(filePath); 137 readObject(filePath); 138 } 139}
通过以上的例子可以看出,在序列化的时候程序执行了对象中定义的私有方法writeObject,反序列化的时候则执行了readObject。由于方法都是私有的,所以不难猜测,肯定是用了反射的方式实现。查看ObjectOutputStream中的writeSerialData方法,以及ObjectInputStream中的readSerialData方法即可发现反射的调用。

所以,通过在writeObject和readObject中二次处理,可以达到加工的目的。
二、Externalizable
对象序列化可以继承Serializable,也可以继承Externalizable,两者有何区别呢?我们试着将上面Person的继承修改成Externalizable并运行,如下:
1package com.test; 2 3import java.io.Externalizable; 4import java.io.IOException; 5import java.io.ObjectInput; 6import java.io.ObjectInputStream; 7import java.io.ObjectOutput; 8import java.io.ObjectOutputStream; 9 10public class Person implements Externalizable{ 11 12 private static final long serialVersionUID = 3482314192692351792L; 13 private int weight; 14 private int age; 15 private String name; 16 public Person(){ 17 System.out.println("invoke constructor!"); 18 } 19 20 public int getWeight() { 21 return weight; 22 } 23 public void setWeight(int weight) { 24 this.weight = weight; 25 } 26 public int getAge() { 27 return age; 28 } 29 public void setAge(int age) { 30 this.age = age; 31 } 32 public String getName() { 33 return name; 34 } 35 public void setName(String name) { 36 this.name = name; 37 } 38 39 /** 40 * 新增加的方法,序列化时会调用 41 * @param stream 42 * @throws IOException 43 */ 44 private void writeObject(ObjectOutputStream stream)throws IOException{ 45 System.out.println("invoke writeObject......"); 46 //此时进行对象的序列化 47 stream.defaultWriteObject(); 48 //额外序列化的数值,反序列化的时候会使用 49 stream.writeInt(12345); 50 } 51 52 /** 53 * 新增加的方法,反序列化时会调用 54 * @param stream 55 * @throws IOException 56 * @throws ClassNotFoundException 57 */ 58 private void readObject(ObjectInputStream stream)throws IOException,ClassNotFoundException{ 59 System.out.println("invoke readObject......"); 60 //此时进行默认的序列化 61 stream.defaultReadObject(); 62 System.out.print("默认初始化后的对象:"); 63 System.out.println(this); 64 this.age = stream.readInt(); 65 System.out.print("对象的age进行加工后:"); 66 } 67 68 @Override 69 public String toString() { 70 return "[weight:"+weight+",name:"+name+",age:"+age+"]"; 71 } 72 @Override 73 public void readExternal(ObjectInput in) throws IOException, 74 ClassNotFoundException { 75 76 } 77 @Override 78 public void writeExternal(ObjectOutput out) throws IOException { 79 80 } 81} 82 83重新运行测试代码,结果如下: 84invoke constructor! 85invoke constructor! 86[weight:0,name:null,age:0]
通过以上的例子,说明程序并没有进行序列化;另外,无参构造函数调用了两次,说明序列化和反序列化的时候各调用了一次。重新修改代码,使其可以序列化,如下:
1package com.test; 2 3import java.io.Externalizable; 4import java.io.IOException; 5import java.io.ObjectInput; 6import java.io.ObjectInputStream; 7import java.io.ObjectOutput; 8import java.io.ObjectOutputStream; 9 10public class Person implements Externalizable{ 11 12 private static final long serialVersionUID = 3482314192692351792L; 13 private int weight; 14 private int age; 15 private String name; 16 public Person(){ 17 System.out.println("invoke constructor!"); 18 } 19 20 public int getWeight() { 21 return weight; 22 } 23 public void setWeight(int weight) { 24 this.weight = weight; 25 } 26 public int getAge() { 27 return age; 28 } 29 public void setAge(int age) { 30 this.age = age; 31 } 32 public String getName() { 33 return name; 34 } 35 public void setName(String name) { 36 this.name = name; 37 } 38 39 /** 40 * 新增加的方法,序列化时会调用 41 * @param stream 42 * @throws IOException 43 */ 44 private void writeObject(ObjectOutputStream stream)throws IOException{ 45 System.out.println("invoke writeObject......"); 46 //此时进行对象的序列化 47 stream.defaultWriteObject(); 48 //额外序列化的数值,反序列化的时候会使用 49 stream.writeInt(12345); 50 } 51 52 /** 53 * 新增加的方法,反序列化时会调用 54 * @param stream 55 * @throws IOException 56 * @throws ClassNotFoundException 57 */ 58 private void readObject(ObjectInputStream stream)throws IOException,ClassNotFoundException{ 59 System.out.println("invoke readObject......"); 60 //此时进行默认的序列化 61 stream.defaultReadObject(); 62 System.out.print("默认初始化后的对象:"); 63 System.out.println(this); 64 this.age = stream.readInt(); 65 System.out.print("对象的age进行加工后:"); 66 } 67 68 @Override 69 public String toString() { 70 return "[weight:"+weight+",name:"+name+",age:"+age+"]"; 71 } 72 @Override 73 public void readExternal(ObjectInput in) throws IOException, 74 ClassNotFoundException { 75 this.name = (String)in.readObject(); 76 } 77 @Override 78 public void writeExternal(ObjectOutput out) throws IOException { 79 out.writeObject(this.name); 80 } 81}
此时name字段可以正常序列化。使用Externalizable时切记要提供无参构造!
三、readResolve方法的使用
根据测试,每次反序列的对象应该是为重新创建的,所以执行==操作时会返回false。那么如果我们需要对象是单例的情况下,该怎么操作呢?readResolve()可以很好的解决这个问题。先看一下未使用之前,Person类如下:
1package com.test; 2 3import java.io.IOException; 4import java.io.ObjectInputStream; 5import java.io.ObjectOutputStream; 6import java.io.Serializable; 7 8public class Person implements Serializable{ 9 10 private static final Person PERSON = new Person(1,2,"123"); 11 private static final long serialVersionUID = 3482314192692351792L; 12 private int weight; 13 private int age; 14 private String name; 15 public Person(){ 16 17 } 18 19 public Person(int age,int weight,String name){ 20 this.age = age; 21 this.weight = weight; 22 this.name = name; 23 } 24 25 public static Person getSingleton(){ 26 return PERSON; 27 } 28 29 public int getWeight() { 30 return weight; 31 } 32 public void setWeight(int weight) { 33 this.weight = weight; 34 } 35 public int getAge() { 36 return age; 37 } 38 public void setAge(int age) { 39 this.age = age; 40 } 41 public String getName() { 42 return name; 43 } 44 public void setName(String name) { 45 this.name = name; 46 } 47 48 /** 49 * 新增加的方法,序列化时会调用 50 * @param stream 51 * @throws IOException 52 */ 53 private void writeObject(ObjectOutputStream stream)throws IOException{ 54 //此时对静态的Person进行序列化 55 stream.writeObject(PERSON); 56 } 57 58 /** 59 * 新增加的方法,反序列化时会调用 60 * @param stream 61 * @throws IOException 62 * @throws ClassNotFoundException 63 */ 64 private void readObject(ObjectInputStream stream)throws IOException,ClassNotFoundException{ 65 //反序列化Person 66 stream.readObject(); 67 } 68 69 @Override 70 public String toString() { 71 return "[weight:"+weight+",name:"+name+",age:"+age+"]"; 72 } 73}
测试方法进行了调整,增加了对Person.PERSON和反序列化完的对象的比较:
1import java.io.FileInputStream; 2import java.io.FileOutputStream; 3import java.io.ObjectInputStream; 4import java.io.ObjectOutputStream; 5import java.util.Arrays; 6 7import com.test.Person; 8import com.test.User; 9 10public class TestPerson { 11 12 /** 13 * 序列化 14 * 15 * @param filePath 16 * 序列化要写入的文件路径 17 * @throws Exception 18 */ 19 public static void writeObject(String filePath) throws Exception { 20 Person p = new Person(); 21 p.setAge(18); 22 p.setName("testPersonName"); 23 p.setWeight(120); 24 25 ObjectOutputStream oos = null; 26 try { 27 oos = new ObjectOutputStream(new FileOutputStream(filePath)); 28 oos.writeObject(p); 29 oos.flush(); 30 } finally { 31 if (oos != null) { 32 oos.close(); 33 } 34 } 35 } 36 37 /** 38 * 反序列化 39 * 40 * @param filePath 41 * 序列化的文件 42 * @throws Exception 43 */ 44 public static void readObject(String filePath) throws Exception { 45 ObjectInputStream ois = null; 46 try { 47 ois = new ObjectInputStream(new FileInputStream(filePath)); 48 Person p = (Person) ois.readObject(); 49 System.out.println(p == Person.getSingleton()); 50 } finally { 51 if (ois != null) { 52 ois.close(); 53 } 54 } 55 } 56 57 public static void test(long num) { 58 byte[] byteNum = new byte[8]; 59 for (int ix = 0; ix < 8; ++ix) { 60 int offset = 64 - (ix + 1) * 8; 61 byteNum[ix] = (byte) ((num >> offset) & 0xff); 62 } 63 System.out.println(Arrays.toString(byteNum)); 64 } 65 66 public static void main(String[] args) throws Exception { 67 String filePath = "f:/obj.out"; 68 writeObject(filePath); 69 readObject(filePath); 70 } 71}
运行,结果为false,说明不是同一个对象。接下来对Person进行调整,新增readResolve方法,如下:
1package com.test; 2 3import java.io.IOException; 4import java.io.ObjectInputStream; 5import java.io.ObjectOutputStream; 6import java.io.ObjectStreamException; 7import java.io.Serializable; 8 9public class Person implements Serializable{ 10 11 private static final Person PERSON = new Person(1,2,"123"); 12 private static final long serialVersionUID = 3482314192692351792L; 13 private int weight; 14 private int age; 15 private String name; 16 public Person(){ 17 18 } 19 20 public Person(int age,int weight,String name){ 21 this.age = age; 22 this.weight = weight; 23 this.name = name; 24 } 25 26 public static Person getSingleton(){ 27 return PERSON; 28 } 29 30 public int getWeight() { 31 return weight; 32 } 33 public void setWeight(int weight) { 34 this.weight = weight; 35 } 36 public int getAge() { 37 return age; 38 } 39 public void setAge(int age) { 40 this.age = age; 41 } 42 public String getName() { 43 return name; 44 } 45 public void setName(String name) { 46 this.name = name; 47 } 48 49 /** 50 * 新增加的方法,序列化时会调用 51 * @param stream 52 * @throws IOException 53 */ 54 private void writeObject(ObjectOutputStream stream)throws IOException{ 55 //此时进行对象的序列化 56 stream.writeObject(PERSON); 57 } 58 59 /** 60 * 新增加的方法,反序列化时会调用 61 * @param stream 62 * @throws IOException 63 * @throws ClassNotFoundException 64 */ 65 private void readObject(ObjectInputStream stream)throws IOException,ClassNotFoundException{ 66 stream.readObject(); 67 } 68 69 private Object readResolve() throws ObjectStreamException { 70 //直接返回静态的Person 71 return PERSON; 72 } 73 74 @Override 75 public String toString() { 76 return "[weight:"+weight+",name:"+name+",age:"+age+"]"; 77 } 78}
此时再次执行测试代码,返回true,说明为同一个对象。同样的,查看ObjectInputStream,可以查看发射调用了readResolve方法。
上一篇:Java序列化与反序列化入门理解