一、readObject和writeObject
通过上个章节的Java序列化与反序列化入门理解,对序列化和反序列化应该有了比较基本的认识。回顾一下,之前的序列化和反序列化,只是简单的处理,如果需要二次加工需要如何处理?比如序列化的时候需要对数据进行加密操作,对应的反序列化时候需要进行解密操作等。先看下面的例子:
package com.test; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class Person implements Serializable{ private static final long serialVersionUID = 3482314192692351792L; private int weight; private int age; private String name; public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } /** * 新增加的方法,序列化时会调用 * @param stream * @throws IOException */ private void writeObject(ObjectOutputStream stream)throws IOException{ System.out.println("invoke writeObject......"); //此时进行对象的序列化 stream.defaultWriteObject(); //额外序列化的数值,反序列化的时候会使用 stream.writeInt(12345); } /** * 新增加的方法,反序列化时会调用 * @param stream * @throws IOException * @throws ClassNotFoundException */ private void readObject(ObjectInputStream stream)throws IOException,ClassNotFoundException{ System.out.println("invoke readObject......"); //此时进行默认的序列化 stream.defaultReadObject(); System.out.print("默认初始化后的对象:"); System.out.println(this); this.age = stream.readInt(); System.out.print("对象的age进行加工后:"); } @Override public String toString() { return "[weight:"+weight+",name:"+name+",age:"+age+"]"; } } 测试代码: import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Arrays; import com.test.Person; import com.test.User; public class TestPerson { /** * 序列化 * * @param filePath * 序列化要写入的文件路径 * @throws Exception */ public static void writeObject(String filePath) throws Exception { Person p = new Person(); p.setAge(18); p.setName("testPersonName"); p.setWeight(120); ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(new FileOutputStream(filePath)); oos.writeObject(p); oos.flush(); } finally { if (oos != null) { oos.close(); } } } /** * 反序列化 * * @param filePath * 序列化的文件 * @throws Exception */ public static void readObject(String filePath) throws Exception { ObjectInputStream ois = null; try { ois = new ObjectInputStream(new FileInputStream(filePath)); Person p = (Person) ois.readObject(); System.out.println(p); } finally { if (ois != null) { ois.close(); } } } public static void test(long num) { byte[] byteNum = new byte[8]; for (int ix = 0; ix < 8; ++ix) { int offset = 64 - (ix + 1) * 8; byteNum[ix] = (byte) ((num >> offset) & 0xff); } System.out.println(Arrays.toString(byteNum)); } public static void main(String[] args) throws Exception { String filePath = "f:/obj.out"; writeObject(filePath); readObject(filePath); } }
通过以上的例子可以看出,在序列化的时候程序执行了对象中定义的私有方法writeObject,反序列化的时候则执行了readObject。由于方法都是私有的,所以不难猜测,肯定是用了反射的方式实现。查看ObjectOutputStream中的writeSerialData方法,以及ObjectInputStream中的readSerialData方法即可发现反射的调用。
所以,通过在writeObject和readObject中二次处理,可以达到加工的目的。
二、Externalizable
对象序列化可以继承Serializable,也可以继承Externalizable,两者有何区别呢?我们试着将上面Person的继承修改成Externalizable并运行,如下:
package com.test; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; public class Person implements Externalizable{ private static final long serialVersionUID = 3482314192692351792L; private int weight; private int age; private String name; public Person(){ System.out.println("invoke constructor!"); } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } /** * 新增加的方法,序列化时会调用 * @param stream * @throws IOException */ private void writeObject(ObjectOutputStream stream)throws IOException{ System.out.println("invoke writeObject......"); //此时进行对象的序列化 stream.defaultWriteObject(); //额外序列化的数值,反序列化的时候会使用 stream.writeInt(12345); } /** * 新增加的方法,反序列化时会调用 * @param stream * @throws IOException * @throws ClassNotFoundException */ private void readObject(ObjectInputStream stream)throws IOException,ClassNotFoundException{ System.out.println("invoke readObject......"); //此时进行默认的序列化 stream.defaultReadObject(); System.out.print("默认初始化后的对象:"); System.out.println(this); this.age = stream.readInt(); System.out.print("对象的age进行加工后:"); } @Override public String toString() { return "[weight:"+weight+",name:"+name+",age:"+age+"]"; } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { } @Override public void writeExternal(ObjectOutput out) throws IOException { } } 重新运行测试代码,结果如下: invoke constructor! invoke constructor! [weight:0,name:null,age:0]
通过以上的例子,说明程序并没有进行序列化;另外,无参构造函数调用了两次,说明序列化和反序列化的时候各调用了一次。重新修改代码,使其可以序列化,如下:
package com.test; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; public class Person implements Externalizable{ private static final long serialVersionUID = 3482314192692351792L; private int weight; private int age; private String name; public Person(){ System.out.println("invoke constructor!"); } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } /** * 新增加的方法,序列化时会调用 * @param stream * @throws IOException */ private void writeObject(ObjectOutputStream stream)throws IOException{ System.out.println("invoke writeObject......"); //此时进行对象的序列化 stream.defaultWriteObject(); //额外序列化的数值,反序列化的时候会使用 stream.writeInt(12345); } /** * 新增加的方法,反序列化时会调用 * @param stream * @throws IOException * @throws ClassNotFoundException */ private void readObject(ObjectInputStream stream)throws IOException,ClassNotFoundException{ System.out.println("invoke readObject......"); //此时进行默认的序列化 stream.defaultReadObject(); System.out.print("默认初始化后的对象:"); System.out.println(this); this.age = stream.readInt(); System.out.print("对象的age进行加工后:"); } @Override public String toString() { return "[weight:"+weight+",name:"+name+",age:"+age+"]"; } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { this.name = (String)in.readObject(); } @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(this.name); } }
此时name字段可以正常序列化。使用Externalizable时切记要提供无参构造!
三、readResolve方法的使用
根据测试,每次反序列的对象应该是为重新创建的,所以执行==操作时会返回false。那么如果我们需要对象是单例的情况下,该怎么操作呢?readResolve()可以很好的解决这个问题。先看一下未使用之前,Person类如下:
package com.test; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class Person implements Serializable{ private static final Person PERSON = new Person(1,2,"123"); private static final long serialVersionUID = 3482314192692351792L; private int weight; private int age; private String name; public Person(){ } public Person(int age,int weight,String name){ this.age = age; this.weight = weight; this.name = name; } public static Person getSingleton(){ return PERSON; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } /** * 新增加的方法,序列化时会调用 * @param stream * @throws IOException */ private void writeObject(ObjectOutputStream stream)throws IOException{ //此时对静态的Person进行序列化 stream.writeObject(PERSON); } /** * 新增加的方法,反序列化时会调用 * @param stream * @throws IOException * @throws ClassNotFoundException */ private void readObject(ObjectInputStream stream)throws IOException,ClassNotFoundException{ //反序列化Person stream.readObject(); } @Override public String toString() { return "[weight:"+weight+",name:"+name+",age:"+age+"]"; } }
测试方法进行了调整,增加了对Person.PERSON和反序列化完的对象的比较:
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Arrays; import com.test.Person; import com.test.User; public class TestPerson { /** * 序列化 * * @param filePath * 序列化要写入的文件路径 * @throws Exception */ public static void writeObject(String filePath) throws Exception { Person p = new Person(); p.setAge(18); p.setName("testPersonName"); p.setWeight(120); ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(new FileOutputStream(filePath)); oos.writeObject(p); oos.flush(); } finally { if (oos != null) { oos.close(); } } } /** * 反序列化 * * @param filePath * 序列化的文件 * @throws Exception */ public static void readObject(String filePath) throws Exception { ObjectInputStream ois = null; try { ois = new ObjectInputStream(new FileInputStream(filePath)); Person p = (Person) ois.readObject(); System.out.println(p == Person.getSingleton()); } finally { if (ois != null) { ois.close(); } } } public static void test(long num) { byte[] byteNum = new byte[8]; for (int ix = 0; ix < 8; ++ix) { int offset = 64 - (ix + 1) * 8; byteNum[ix] = (byte) ((num >> offset) & 0xff); } System.out.println(Arrays.toString(byteNum)); } public static void main(String[] args) throws Exception { String filePath = "f:/obj.out"; writeObject(filePath); readObject(filePath); } }
运行,结果为false,说明不是同一个对象。接下来对Person进行调整,新增readResolve方法,如下:
package com.test; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamException; import java.io.Serializable; public class Person implements Serializable{ private static final Person PERSON = new Person(1,2,"123"); private static final long serialVersionUID = 3482314192692351792L; private int weight; private int age; private String name; public Person(){ } public Person(int age,int weight,String name){ this.age = age; this.weight = weight; this.name = name; } public static Person getSingleton(){ return PERSON; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } /** * 新增加的方法,序列化时会调用 * @param stream * @throws IOException */ private void writeObject(ObjectOutputStream stream)throws IOException{ //此时进行对象的序列化 stream.writeObject(PERSON); } /** * 新增加的方法,反序列化时会调用 * @param stream * @throws IOException * @throws ClassNotFoundException */ private void readObject(ObjectInputStream stream)throws IOException,ClassNotFoundException{ stream.readObject(); } private Object readResolve() throws ObjectStreamException { //直接返回静态的Person return PERSON; } @Override public String toString() { return "[weight:"+weight+",name:"+name+",age:"+age+"]"; } }
此时再次执行测试代码,返回true,说明为同一个对象。同样的,查看ObjectInputStream,可以查看发射调用了readResolve方法。
上一篇:Java序列化与反序列化入门理解
下一篇:java序列化与反序列化进阶(二)