我们知道对象的持持久化有三种方式:
1: 对象序列化
2: XML
3: 数据库技术
序列化可以帮助使得对象的生命周期不取决与程序是否正在执行,它可以生存于程序的调用之间。 只要将任何对象序列化到单一流中,就可以恢复出与我们写出时一样的对象网,并且没有任何重复复制的对象。
序列化技术包括序列化和饭序列化
我们可以通过实现Serializable接口来实现对象的序列化。
class Data implements Serializable{ private int n; public Data(int i) { n = i; } @Override public String toString() { return Integer.toString(n); } } /* * 由于Java序列化似乎找不出什么缺点,所以尽量不要自己动手,让它利用优化的算法自动维护整个对象网 */ public class Test implements Serializable{ private static Random random = new Random(47); private Data[] data = { new Data(random.nextInt(10)), new Data(random.nextInt(10)), new Data(random.nextInt(10)) }; private char c; private Test next; public Test() { System.out.println("default constructor ..."); } public Test(int i, char x){ System.out.println("Test constructor : " + i + " " + x); c = x; if (--i > 0) { next = new Test(i, (char)(x + 1)); } } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(c + "("); for (Data d : data) { sb.append(d); } sb.append(")"); if (next != null) { sb.append(next); } return sb.toString(); } public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException{ Test test = new Test(6, 'a'); System.out.println("test : " + test); //序列化到磁盘文件 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Ser.out")); oos.writeObject("Storage ..."); oos.writeObject(test); oos.close(); //从磁盘文件中反序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Ser.out")); String s = (String)ois.readObject(); Test t1 = (Test)ois.readObject(); /* * 通过输出我们可以看到序列化特别聪明的一个地方是它不仅保存了对象的全景图,而且还能追踪对象内部所包含的所有引用,保存那些对象; * 接着又能对对象内部包含的每个这样的引用进行追踪了,以此类推,通过优化的算法来维护整个对象网 * 序列化的对象在进行反序列化时,内有调用任何构造器。整个对象都是通过InputStrean中去的数据恢复的。 */ System.out.println("header : " + s); System.out.println(t1); //序列化到字节数组 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos2 = new ObjectOutputStream(baos); oos2.writeObject("Storage2 ..."); oos2.writeObject(t1); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois2 = new ObjectInputStream(bais); s = (String) ois2.readObject(); t1 = (Test)ois2.readObject(); System.out.println("header : " + s); System.out.println(t1); } } 输出: Test constructor : 6 a Test constructor : 5 b Test constructor : 4 c Test constructor : 3 d Test constructor : 2 e Test constructor : 1 f test : a(853)b(119)c(802)d(788)e(199)f(881) header : Storage ... a(853)b(119)c(802)d(788)e(199)f(881) header : Storage2 ... a(853)b(119)c(802)d(788)e(199)f(881)
序列化的控制:
1:实现Externalizable接口来对序列化过程进行控制(Externalizable 继承了 Serializable接口)
public class Test implements Externalizable{ private int i; private String s; public Test() { System.out.println("default constructor ...."); } public Test(int i,String s){ this.i = i; this.s = s; } @Override public String toString() { return s + i; } /* * 通过实现Externalizable, 我们要重写下面两个方法,然后在序列化和反序列化的过程中被自动调用, * 以便完成一些操作 */ @Override public void writeExternal(ObjectOutput out) throws IOException { System.out.println("Test writeExternal"); out.writeObject(s); out.writeObject(i); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { System.out.println("Test readExternal"); s = (String)in.readObject(); i = (int)in.readObject(); } public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException { Test test = new Test(47, "a String number"); System.out.println(test); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Ext.out")); System.out.println("Saving Object..."); oos.writeObject(test); oos.close(); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Ext.out")); System.out.println("Recovering Object..."); /* * 对于Serializable对象,对象完全以它存储的二进制位为基础,我而不调用构造器 * 而对于Externalizable对象,所有普通默认的构造器都会被调用,然后调用readExternal */ Test tmp = (Test)ois.readObject(); System.out.println(tmp); } } 输出: a String number47 Saving Object... Test writeExternal Recovering Object... default constructor .... Test readExternal a String number47
2:transient关键字
transient通常与Serializable一起使用,因为Externalizable直接就可以控制我们要序列化的部分,而Serializable默认的是全部序列化。
使用transient可以逐个字段地关闭序列化,它的意思是“不用麻烦你保存或恢复数据,我自己会处理”
public class Test implements Serializable{ private Date date = new Date(); private String username; private transient String password; public Test() {} public Test(String un, String pd) { username = un; password = pd; } @Override public String toString() { return "info : " + "date : " + date + " username : " + username + "password : " + password; } public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException{ Test test = new Test("gbx", "gbx"); System.out.println("saving : " + test); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tran.out")); oos.writeObject(test); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("tran.out")); Test t1 = (Test)ois.readObject(); //从输出中我们可以看到password为null System.out.println("recovering : " + t1); } } 输出: saving : info : date : Thu Nov 28 19:25:30 CST 2013 username : gbxpassword : gbx recovering : info : date : Thu Nov 28 19:25:30 CST 2013 username : gbxpassword : null
3: 实现Serializable, 然后自己添加
private void writeObject(ObjectOutputStream stream) throws IOException{ } private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException{ }
public class Test implements Serializable{ private String a; private transient String b; public Test(String ai, String bi) { a = "transient : " + ai; b = "not transient" + bi; } @Override public String toString() { return a + "\n" + b; } private void writeObject(ObjectOutputStream stream) throws IOException{ System.out.println("invok writeObject"); //调用默认的writeObject stream.defaultWriteObject(); //如果不进行手动的处理这里的b是不会序列化的 stream.writeObject(b); } private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException{ System.out.println("invok readObject"); stream.defaultReadObject(); //手动处理 b = (String)stream.readObject(); } public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException { Test test = new Test("test1", "test2"); System.out.println("saving : \n" + test); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("rw.out")); oos.writeObject(test); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("rw.out")); Test t1 = (Test)ois.readObject(); System.out.println("recovering : " + t1); } } 输出: saving : transient : test1 not transienttest2 invok writeObject invok readObject recovering : transient : test1 null
对于静态数据 我们必须手动去实现才行。
序列化时,并不保存静态变量,这其实比较容易理解,序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量,所以我们要收到实现
public class Test implements Serializable{ public static int a = 5; private static int b = 5; @Override public String toString() { return a + " " + b; } public static void writeVarB(ObjectOutputStream oos) throws IOException { oos.writeInt(b); } public static void readVarB(ObjectInputStream ois) throws IOException { b = ois.readInt(); } public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException { Test test = new Test(); System.out.println(test); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.out")); Test.writeVarB(oos); oos.writeObject(test); test.a = 10; test.b = 10; ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.out")); Test.readVarB(ois); Test t1 = (Test)ois.readObject(); /* * 通过输出可以发现a没有被保存,所以他的值会改变, b被保存了,所以我们读出来的值是我们之前保存的值 */ System.out.println(t1); } } 输出: 5 5 10 5