java 序列化

工作有几个年头了,也想写些东西。把我所了解的java序列化写出来,知识共享。

java序列化:
   当2个java进程进行远程通信的时候,需要发送方把对象转换为字节序列(对象的序列化),然后在网络上传输。
   接收方需要把接受到的字节序列恢复成java对象(对象的反序列化)后,使用该对象。


实现:
   要想实现java的序列化,需要实现(implements)java.io.Serializable接口或者java.io.Externalizable接口
   实现序列化。

简单的例子,创建Customer对象储存本地customer.dat文件中。然后把序列化的字节恢复成Customer对象。

 

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;


public class Customer implements java.io.Serializable {
	
	/**
	 * serialVersionUID.
	 */
	private static final long serialVersionUID = 4186775250920519367L;
	
	/**
	 * customer'name property.
	 */
	private String name;
	
	/**
	 * customer' address property. 
	 */
	private String address;
	
	/**
	 * construct a new Customer instance with name and address argument.
	 * @param name - customer's name.
	 * @param address - customer'address.
	 */
	public Customer(String name,String address) {
		this.name = name;
		this.address = address;
	}

	/**
	 * get customer's name.
	 * @return customer's name.
	 */
	public String getName() {
		return name;
	}

	/**
	 * set customer's name property.
	 * @param name - name.
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * get customer's address.
	 * @return - address.
	 */
	public String getAddress() {
		return address;
	}

	/**
	 * set customer's address.
	 * @param address -address.
	 */
	public void setAddress(String address) {
		this.address = address;
	}
	
	/**
	 * 
	 */
	public boolean equals(Object obj) {
		if(obj == this) {
			return true;
		}
			
		if(!(obj instanceof Customer)) {
			return false;
		}
		
		Customer c = (Customer) obj;
		
		return (name.equals(c.getName()) && address.equals(c.getAddress()));
	}
	
	public int hashCode() {
		return name.hashCode() + address.hashCode();
	}
	
	public String toString() {
		return "[" + name + "," + address + "]";
	}
	
	private void readObject(ObjectInputStream objectinputstream) throws ClassNotFoundException, IOException {
	    objectinputstream.defaultReadObject();
	}
	
	private void writeObject(ObjectOutputStream objectOutputStream) throws IOException {
		objectOutputStream.defaultWriteObject();
	}
	
	public static void main(String[] args) {
		Customer c = new Customer("zc","liaoning.dl");
		//Serialize
		System.out.println("before serialize:" + c);
		ObjectOutputStream oos = null;
		try {
			oos = new ObjectOutputStream(new FileOutputStream("c:\\customer.dat"));
			oos.writeObject(c);
		} catch (FileNotFoundException e) {
			//ignore
		} catch (IOException e) {
			//ignore
		} finally {
			try {
				if(oos != null) {
					oos.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		//deserialize
		ObjectInputStream ois = null;
		Customer c2 = null;
		try {
			ois = new ObjectInputStream(new FileInputStream("c:\\customer.dat"));
			c2 = (Customer) ois.readObject();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			try {
				if(ois != null) {
					ois.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		System.out.println("deserialize:" + c2);
		System.out.println("compare result:" + c.equals(c2));
	}

}

 

执行结果:

before serialize:[zc,liaoning.dl]
deserialize:[zc,liaoning.dl]
compare result:true

 上面就是简单的序列化例子。

 

 

从上面例子可以看出序列化前后对象比较是equal,真正意义上2个对象,只是内容相同而已。如何能让序列化后的对象绝对相等(==比较)?

也就是说单例如何序列化后的对象是一个?(用java中反射获得的单例对象,其实也是一个新对象)

 

下面是一个最简单的单例模式:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Singleton implements java.io.Serializable {

	private static Singleton instance = new Singleton();

	private Singleton() {}

	public static Singleton getInstance() {
		return instance;
	}

	public static void main(String[] args) {
		Singleton singleton = Singleton.getInstance();
		ObjectOutputStream oos = null;
		try {
			oos = new ObjectOutputStream(new FileOutputStream("c:\\singleton.dat"));
			oos.writeObject(singleton);
		} catch (FileNotFoundException e) {
			//ignore
		} catch (IOException e) {
			//ignore
		} finally {
			try {
				if(oos != null) {
					oos.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		//deserialize
		ObjectInputStream ois = null;
		Singleton singleton2 = null;
		try {
			ois = new ObjectInputStream(new FileInputStream("c:\\singleton.dat"));
			singleton2 = (Singleton) ois.readObject();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			try {
				if(ois != null) {
					ois.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		System.out.println(singleton == singleton2);
	}
}
 

执行结果:

false
 

如果在单例中实现如下方法:

	private Object readResolve() {
		return instance;
	}

 执行结果:

true

 

 

readResolve方法把序列化后的对象覆盖,返回readResolve方法值。

 

在实现序列化的过程中,有许多需要注意的地方。

在网络的传输的过程中,有可能把传输的字节码修改。这对于类中对某一个属性有限制(比如年龄<120),修改成非法的字节码。

详细的内容请查看 effective java的第10章序列化中的内容。

 

 

;