Java对象序列化(Object Serialization)

Java1.1中实现了Serializable接口的对象都可被转换成一系列字节,并可完全恢复成原状。Java中的两个特性运用了对象序列化:远程调用方法(RMI),Java Beans


要序列化一个对象(其类已实现Serializable接口),需要创建一些OutputStream对象,将其封装到ObjectOutputStream对象中,调用writeObject()方法即可将序列化的对象发生到OutputStream中,比如文件、String、网络等。当要恢复对象时,只需将InputStream封装到ObjectInputStream对象中,调用readObject()方法即可,当然此时必须上溯造型到原始类型。

1. Serializable接口实例

package com.test;

import java.io.*;

class Data implements Serializable {
	private int i;
	Data(int x) { i = x; }
	public String toString() {
		return Integer.toString(i);
	}
}

public class Serial implements Serializable {
	//Generate random int
	private static int r() {
		return (int)(Math.random() * 10);
	}
	
	private Data[] d = {new Data(r()), new Data(r()), new Data(r())};
	private Serial next;
	private char c;
	
	Serial(int i, char x) {
		System.out.println(" Serial constructor: " + i);
		c = x;
		if (--i >0)
			next = new Serial(i, (char)(x+1));
	}
	
	Serial() {
		System.out.println("Default constructor");
	}
	
	public String toString() {
		String s = ":" + c + "(";
		for (int i = 0; i < d.length; i++) {
			s += d[i].toString();
		}
		s += ")";
		if (next != null)
			s += next.toString();
		return s;
	}
	
	public static void main(String[] args) {
		Serial w = new Serial(6, 'e');
		System.out.println("w = " + w);
		try {
			//write the serialized objects to file
			ObjectOutputStream out = new ObjectOutputStream(
					new FileOutputStream("serial.out"));
			out.writeObject("Serial test");
			out.writeObject(w);
			out.close(); //will also flush output
			//read and restore serialized objects from file
			ObjectInputStream in = new ObjectInputStream(
					new FileInputStream("serial.out"));
			String s = (String)in.readObject();
			Serial w2 = (Serial)in.readObject();
			System.out.println(s + ", w2 = " + w2);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		try {
			//write the serialized objects to memory
			ByteArrayOutputStream bout = new ByteArrayOutputStream();
			ObjectOutputStream out = new ObjectOutputStream(bout);
			out.writeObject("Serial test");
			out.writeObject(w);
			out.flush();
			
			//read and restore from memory
			ObjectInputStream in = new ObjectInputStream(
					new ByteArrayInputStream(bout.toByteArray()));
			String s = (String)in.readObject();
			Serial w3 = (Serial)in.readObject();
			System.out.println(s + ", w3 = " + w3);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
在读取并恢复Serializable对象的时候不会允许构建器,而且对象序列化没有用到IO系统的Reader和Writer,而是使用Java1.0的InputStream和OutputStream体系。


2. Externalizable接口

实现了Serializable接口的对象序列化时,默认会将类中所有字段包含进去。但是有时涉及安全问题,有些字段我们不想被序列化,这时可使用Externalizable接口控制序列化过程。Externalizable接口扩展了Serializable接口,并添加了两个方法writeExternal()和readExternal()。在序列化和恢复的过程中会自动调用这两个方法,我们可以通过改写他们控制序列化和恢复过程。

实例1,实现Externalizable接口的对象在恢复是会调用类的默认构建器,注意Blip2的默认构建器不是public的,这样会导致调用权限不够而产生异常

package com.test;

import java.io.*;
import java.util.*;

class Blip1 implements Externalizable {
	public Blip1() {
		System.out.println("Blip1 constructor");
	}
	
	public void writeExternal(ObjectOutput out) throws IOException {
		System.out.println("Blip1.writeExternal");
	}
	
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
		System.out.println("Blip1.readExternal");
	}
}

class Blip2 implements Externalizable {
	Blip2() {
		System.out.println("Blip2 constructor");
	}
	
	public void writeExternal(ObjectOutput out) throws IOException {
		System.out.println("Blip2.writeExternal");
	}
	
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
		System.out.println("Blip2.readExternal");
	}
}

public class External {
	public static void main(String[] args) {
		System.out.println("Constructing objects: ");
		Blip1 b1 = new Blip1();
		Blip2 b2 = new Blip2();
		try {
			//write serialzed objects
			ObjectOutputStream out = new ObjectOutputStream(
					new FileOutputStream("Blips.out"));
			System.out.println("Saving objects: ");
			out.writeObject(b1);
			out.writeObject(b2);
			out.close();
			//read and restore objects
			ObjectInputStream in = new ObjectInputStream(
					new FileInputStream("Blips.out"));
			System.out.println("Recovering b1:");
			b1 = (Blip1)in.readObject();
			//following will throw exception 
			//as Blip2's default constructor is not public
			//b2 = (Blip2)in.readObject();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}


实例2,在readExternal和writeExternal中控制序列化过程

package com.test;

import java.io.*;
import java.util.*;

public class External2 implements Externalizable {
	int i;
	String s; //not initialize
	public External2() {
		System.out.println("External2 constructor");
	}
	
	public External2(String x, int a) {
		System.out.println("External2(String x, int a)");
		s = x;
		i = a;
	}
	
	public String toString() { return s + i; }
	
	public void writeExternal(ObjectOutput out) throws IOException {
		System.out.println("External2.writeExternal");
		//write the fields you want to serialize
		//you can only serialize some fields
		out.writeObject(s);
		out.writeObject(i);
	}
	
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
		System.out.println("External2.readExternal");
		//Restore the fields that are serialized
		s = (String)in.readObject();
		i = in.readInt();
	}
	
	public static void main(String[] args) {
		System.out.println("Constructing objects: ");
		External2 e2 = new External2("A string", 42);
		System.out.println(e2.toString());
		
		try {
			ObjectOutputStream out = new ObjectOutputStream(
					new FileOutputStream("external2.out"));
			System.out.println("Saving object: ");
			out.writeObject(e2);
			out.close();
			
			//restore object
			ObjectInputStream in = new ObjectInputStream(
					new FileInputStream("external2.out"));
			System.out.println("Recovering e2:");
			e2 = (External2)in.readObject();
			System.out.println(e2.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}


3. transient关键字

实现Serializable接口的类序列化时默认所有fields都会被序列化,transient关键字可以关闭fields的序列化。设置成transient的字段不参与序列化,恢复时简单的设置成默认值,如int 0, 对象句柄 null。


4. writeObject()和readObject()方法代替Externalizable

实现Serializable接口,并添加writeObject()和readObject()方法也可以控制序列化过程。但是这两个方法在类中都被定义成private,按理说外部类是不能调用的,这里序列化过程绕过了检查,这种绕过检查的机制我们普通用户是不可能接触到的。。。

private void writeObject(ObjectOutputStream stream) throws IOException;

private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException;


实例:

//: SerialCtl.java 
//Controlling serialization by adding your own 
//writeObject() and readObject() methods. 
import java.io.*; 

public class SerialCtl implements Serializable { 
String a; 
transient String b; 
public SerialCtl(String aa, String bb) { 
 a = "Not Transient: " + aa; 
 b = "Transient: " + bb; 
} 
public String toString() { 
 return a + "\n" + b; 
} 
private void  
 writeObject(ObjectOutputStream stream) 
   throws IOException { 
 stream.defaultWriteObject(); 
 stream.writeObject(b); 
} 
private void  
 readObject(ObjectInputStream stream) 
   throws IOException, ClassNotFoundException { 
 stream.defaultReadObject(); 
 b = (String)stream.readObject(); 
} 
public static void main(String[] args) { 
 SerialCtl sc =  
   new SerialCtl("Test1", "Test2"); 
 System.out.println("Before:\n" + sc); 
 ByteArrayOutputStream buf =  
   new ByteArrayOutputStream(); 
 try { 
   ObjectOutputStream o = 
     new ObjectOutputStream(buf); 
   o.writeObject(sc); 
   // Now get it back: 
   ObjectInputStream in = 
     new ObjectInputStream( 
       new ByteArrayInputStream( 
         buf.toByteArray())); 
   SerialCtl sc2 = (SerialCtl)in.readObject(); 
   System.out.println("After:\n" + sc2); 
 } catch(Exception e) { 
   e.printStackTrace(); 
 } 
} 
} ///:~



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章