java序列化与反序列化进阶(一)

一、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方法即可发现反射的调用。

java序列化与反序列化进阶(一)_第1张图片

所以,通过在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序列化与反序列化进阶(二)

你可能感兴趣的:(java序列化与反序列化进阶(一))