一、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序列化与反序列化入门理解