IO学霸3 -- Serializable

来源:互联网 发布:二维数组是什么 编辑:程序博客网 时间:2024/05/16 05:19

1. 压缩

CheckedInputStream: GetCheckSum()为任何InputStream()产生校验和。

CheckOutputStream: GetCheckSum()为任何OutputStream产生校验和。

DeflaterOutputStream: 压缩类的基类。

ZipOutputStream: 一个DeflaterOutputStream,用于将数据压缩成Zip文件格式。

GZIPOutputStream: 一个DeflaterOutputStream,用于将数据压缩成GZIP文件格式。

DeflaterInputStream: 解压缩类的基类。

ZipInputStream: 一个DeflaterInputStream,用于解压缩Zip文件格式。

GZIPInputStream: 一个DeflaterInputStream,用于解压缩GZIP文件格式。


2. Serializable

Java的对象序列化将那些实现了Serializable接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象。

这一过程甚至可以通过网络进行,这意味着序列化机制能自动弥补不同操作系统之间的差异。

对象序列化主要有两种特性:一是Java远程调用。使存活于其他计算机上的对象使用起来就像存活于本机上一样,当向远程对象发送消息时,需要通过对象序列化来传输参数和返回值。

二是JavaBeans序列化配置。

import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;import java.util.Random;class Data implements Serializable{private int n;public Data(int n){this.n=n;}public String toString(){return Integer.toString(n);}}public class Worm implements Serializable {private static Random rand = new Random(47);private Data[] d = {new Data(rand.nextInt(10)),new Data(rand.nextInt(10)),new Data(rand.nextInt(10))};private Worm next;private char c;public Worm(){System.out.println("Default constructor");}public Worm(int i, char x){System.out.println("Worm constructor: "+i);c=x;if(--i>0)next = new Worm(i, (char)(x+1));}public String toString(){StringBuffer result = new StringBuffer();result.append(c);result.append("(");for(Data data:d){result.append(data);}result.append(")");if(next!=null){result.append(next);}return result.toString();}public static void main(String[] args) throws Exception, IOException {Worm w = new Worm(6,'a');System.out.println("w="+w);ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("worm.out"));out.writeObject("Worm storage\n");out.writeObject(w);out.close();ObjectInputStream in = new ObjectInputStream(new FileInputStream("worm.out"));String s = (String) in.readObject();Worm w2 = (Worm) in.readObject();System.out.println(s+"w2="+w2);ByteArrayOutputStream bout = new ByteArrayOutputStream();ObjectOutputStream out2 = new ObjectOutputStream(bout);out2.writeObject("Worm storage\n");out2.writeObject(w);out2.flush();ObjectInputStream in2 = new ObjectInputStream(new ByteArrayInputStream(bout.toByteArray()));s=(String) in2.readObject();Worm w3 = (Worm) in2.readObject();System.out.println(s+"w3="+w3);}}//*Output:Worm constructor: 6Worm constructor: 5Worm constructor: 4Worm constructor: 3Worm constructor: 2Worm constructor: 1w=a(853)b(119)c(802)d(788)e(199)f(881)Worm storagew2=a(853)b(119)c(802)d(788)e(199)f(881)Worm storagew3=a(853)b(119)c(802)d(788)e(199)f(881)*//
对一个Serializable还原中,没有调用任何构造器包括默认构造器,整个对象都是从InputStream中取得数据恢复而来。


Serializable恢复,需要寻找相关的类:

import java.io.FileOutputStream;import java.io.ObjectOutputStream;import java.io.Serializable;public class FreezeAlien {public static void main(String[] args) throws Exception {// TODO Auto-generated method stubObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("X.flie"));Alien quellek = new Alien();out.writeObject(quellek);}}class Alien implements Serializable{}

//: ../ThawAlien.javaimport java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.ObjectInputStream;public class ThawAlien {public static void main(String[] args) throws IOException, Exception {// TODO Auto-generated method stubObjectInputStream in = new ObjectInputStream(new FileInputStream(new File("..", "X.flie")));Object o = in.readObject();System.out.println(o.getClass());}}

打开文件和读取mystery对象中的内容都要需要Alien的Class对象;而Java虚拟机找不到Alien.class,必须保证JVM能找到相关.class文件。


2.1 序列化控制

Externalizable代替Serializable进行序列化控制。Externalizble接口继承Serializable,并添加writeExternal()和readExternal(),这两个方法在序列化和反序列化还原过程中被自动调用。

import java.io.Externalizable;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInput;import java.io.ObjectInputStream;import java.io.ObjectOutput;import java.io.ObjectOutputStream;class Blip1 implements Externalizable{public Blip1(){System.out.println("Blip1 Con");}@Overridepublic void readExternal(ObjectInput arg0) throws IOException,ClassNotFoundException {System.out.println("Blip1 readExternal");}@Overridepublic void writeExternal(ObjectOutput arg0) throws IOException {System.out.println("Blip1 writeExternal");}}class Blip2 implements Externalizable{ Blip2(){System.out.println("Blip2 Con");}@Overridepublic void readExternal(ObjectInput arg0) throws IOException,ClassNotFoundException {System.out.println("Blip2 readExternal");}@Overridepublic void writeExternal(ObjectOutput arg0) throws IOException {System.out.println("Blip2 writeExternal");}}public class Blips {public static void main(String[] args) throws IOException, Exception {System.out.println("Constructing Objects:");Blip1 b1 = new Blip1();Blip2 b2 = new Blip2();ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("Blips.out"));System.out.println("Saving Objects:");o.writeObject(b1);o.writeObject(b2);o.close();ObjectInputStream in = new ObjectInputStream(new FileInputStream("Blips.out"));System.out.println("Recovering b1:");b1 = (Blip1) in.readObject();System.out.println("Recovering b2:");b2 = (Blip2) in.readObject();}}//*Output:Constructing Objects:Blip1 ConBlip2 ConSaving Objects:Blip1 writeExternalBlip2 writeExternalRecovering b1:Blip1 ConBlip1 readExternalRecovering b2:Exception in thread "main" java.io.InvalidClassException: com.ld.tij.xfiles.Blip2; com.ld.tij.xfiles.Blip2; no valid constructorat java.io.ObjectStreamClass.checkDeserialize(Unknown Source)at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)at java.io.ObjectInputStream.readObject0(Unknown Source)at java.io.ObjectInputStream.readObject(Unknown Source)at com.ld.tij.xfiles.Blips.main(Blips.java:63)//
因为Blip2构造器不是公有,在恢复Bilp2时候出现异常。

恢复Serializable对象时,对象完全以存储二进制为基础来构造,而不是构造器。

而Externalizable所有普通默认构造器都会被调用,然后调用readExternal()。


transient关键字

某个特定子对象不想让Java序列化自动保存与恢复。

import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;import java.util.Date;import java.util.concurrent.TimeUnit;public class Logon implements Serializable {private Date date = new Date();private String username;private transient String password;public Logon(String name, String pwd){this.username=name;this.password=pwd;}public String toString(){return "logon info: \n username: "+username+"\n date: "+date+"\n password: "+password;}public static void main(String[] args) throws  IOException, Exception {Logon a = new Logon("Dog", "lucky");System.out.println("Logon a="+a);ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("Logon.out"));o.writeObject(a);o.close();TimeUnit.SECONDS.sleep(1);ObjectInputStream in = new ObjectInputStream(new FileInputStream("Logon.out"));System.out.println("Recovering objec at "+ new Date());a=(Logon) in.readObject();System.out.println("logon a="+a);}}//*Output:Logon a=logon info:  username: Dog date: Mon Oct 21 10:51:51 CST 2013 password: luckyRecovering objec at Mon Oct 21 10:51:52 CST 2013logon a=logon info:  username: Dog date: Mon Oct 21 10:51:51 CST 2013 password: null//
Serializable还有一种办法是添加writeObject()和readObject()方法,这样对象一旦被序列化或者反序列化都会自动调用这两个方法。
如果是Externalizable,可以在writeExternal()内部只对需要部分进行序列化即可。


2.2 持久性

import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;import java.util.ArrayList;import java.util.List;class House implements Serializable{}class Animal implements Serializable{private String name;private House house;Animal(String n, House h){name=n;house=h;}public String toString(){return name+"["+super.toString()+"], "+house+"\n";}}public class MyWorld {public static void main(String[] args) throws Exception {House house = new House();List<Animal> animals = new ArrayList<Animal>();animals.add(new Animal("Bosco the dog", house));animals.add(new Animal("Ralph the hamster", house));animals.add(new Animal("Molly the cat", house));System.out.println("animals: "+animals);ByteArrayOutputStream buf1 = new ByteArrayOutputStream();ObjectOutputStream o1 = new ObjectOutputStream(buf1);o1.writeObject(animals);o1.writeObject(animals);ByteArrayOutputStream buf2 = new ByteArrayOutputStream();ObjectOutputStream o2 = new ObjectOutputStream(buf2);o2.writeObject(animals);ObjectInputStream in1 = new ObjectInputStream(new ByteArrayInputStream(buf1.toByteArray()));ObjectInputStream in2 = new ObjectInputStream(new ByteArrayInputStream(buf2.toByteArray()));List animal1 = (List) in1.readObject();List animal2 = (List) in1.readObject();List animal3 = (List) in2.readObject();System.out.println("animal1: "+animal1);System.out.println("animal2: "+animal2);System.out.println("animal3: "+animal3);}}//*Output:animals: [Bosco the dog[com.ld.tij.Animal@3d4b7453], com.ld.tij.House@24c21495, Ralph the hamster[com.ld.tij.Animal@41d5550d], com.ld.tij.House@24c21495, Molly the cat[com.ld.tij.Animal@1cc2ea3f], com.ld.tij.House@24c21495]animal1: [Bosco the dog[com.ld.tij.Animal@28df8ff1], com.ld.tij.House@6d632c2d, Ralph the hamster[com.ld.tij.Animal@9e97676], com.ld.tij.House@6d632c2d, Molly the cat[com.ld.tij.Animal@3e60420f], com.ld.tij.House@6d632c2d]animal2: [Bosco the dog[com.ld.tij.Animal@28df8ff1], com.ld.tij.House@6d632c2d, Ralph the hamster[com.ld.tij.Animal@9e97676], com.ld.tij.House@6d632c2d, Molly the cat[com.ld.tij.Animal@3e60420f], com.ld.tij.House@6d632c2d]animal3: [Bosco the dog[com.ld.tij.Animal@219106c7], com.ld.tij.House@38540408, Ralph the hamster[com.ld.tij.Animal@13d4c61c], com.ld.tij.House@38540408, Molly the cat[com.ld.tij.Animal@761a626f], com.ld.tij.House@38540408]*//
这个例子中,Animal对象包含House类型字段,在main方法中创建一个Animal列表并将其两次序列化,分别推送到不同的流中。

当其被反序列化还原打印时,我们期望反序列化后对象地址与原来不同,但是animal1和animal2地址却相同,包括二者共享House对象的引用。

而恢复animal3时,系统无法知道另一个流内的对象是第一个流内对象的别名,因此产生完全不同的对象网。

如果想保存系统状态,最安全的做法是将其作为 原子 操作进行序列化。 如果我们序列化了某些东西,再去做其他工作,然后才序列化,那么将无法安全保存系统状态。正确的做法应该是:将保存系统状态的所有对象都放在一个单一容器,一个操作中将其直接写出,然后同样一个操作调用,即可恢复。

import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;import java.util.ArrayList;import java.util.List;import java.util.Random;abstract class Shape implements Serializable{public static final int RED=1, BULE=2, GREEN=3;private int xPos, yPos, dimension;private static Random rand = new Random(47);private static int counter=0;public abstract void setColor(int newColor);public abstract int getColor();public Shape(int xVal, int yVal, int dim){xPos=xVal;yPos=yVal;dimension=dim;}public String toString(){return getClass() + "color["+getColor()+"] xPos["+xPos+"] yPos["+yPos+"] dim["+dimension+"]\n";}public static Shape randomFactory(){int xVal = rand.nextInt(100);int yVal = rand.nextInt(100);int dim = rand.nextInt(100);switch(counter++%3){default:case 0:return new Circle(xVal,yVal,dim);case 1:return new Square(xVal,yVal,dim);case 2:return new Line(xVal,yVal,dim);}}}class Circle extends Shape{private static int color = RED;public Circle(int xVal, int yVal, int dim){super(xVal,yVal,dim);}public void setColor(int newColor){color = newColor;}public int getColor(){return color;}}class Square extends Shape{private static int color;public Square(int xVal, int yVal, int dim){super(xVal,yVal,dim);color = RED;}public void setColor(int newColor){color = newColor;}public int getColor(){return color;}}class Line extends Shape{private static int color = RED;public static void serializeStaticState(ObjectOutputStream os) throws IOException{os.writeInt(color);}public static void deserializeStaticState(ObjectInputStream os) throws IOException{color = os.readInt();}public Line(int xVal, int yVal, int dim){super(xVal,yVal,dim);}public void setColor(int newColor){color = newColor;}public int getColor(){return color;}}public class StoreCADState {public static void main(String[] args) throws  IOException {List<Class<? extends Shape>> shapeTypes = new ArrayList<Class<? extends Shape>>();shapeTypes.add(Circle.class);shapeTypes.add(Square.class);shapeTypes.add(Line.class);List<Shape> shapes = new ArrayList<Shape>();for(int i=0;i<10;i++){shapes.add(Shape.randomFactory());}for(int i=0;i<10;i++){((Shape)shapes.get(i)).setColor(Shape.GREEN);}ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("CADState.out"));out.writeObject(shapeTypes);Line.serializeStaticState(out);out.writeObject(shapes);System.out.println(shapes);}}//*Output[class com.ld.tij.Circlecolor[3] xPos[58] yPos[55] dim[93], class com.ld.tij.Squarecolor[3] xPos[61] yPos[61] dim[29], class com.ld.tij.Linecolor[3] xPos[68] yPos[0] dim[22], class com.ld.tij.Circlecolor[3] xPos[7] yPos[88] dim[28], class com.ld.tij.Squarecolor[3] xPos[51] yPos[89] dim[9], class com.ld.tij.Linecolor[3] xPos[78] yPos[98] dim[61], class com.ld.tij.Circlecolor[3] xPos[20] yPos[58] dim[16], class com.ld.tij.Squarecolor[3] xPos[40] yPos[11] dim[22], class com.ld.tij.Linecolor[3] xPos[4] yPos[83] dim[6], class com.ld.tij.Circlecolor[3] xPos[75] yPos[10] dim[42]]*//

恢复对象:

import java.io.*;import java.util.*;public class RecoverCADState {  @SuppressWarnings("unchecked")  public static void main(String[] args) throws Exception {    ObjectInputStream in = new ObjectInputStream(      new FileInputStream("CADState.out"));    // Read in the same order they were written:    List<Class<? extends Shape>> shapeTypes =      (List<Class<? extends Shape>>)in.readObject();    Line.deserializeStaticState(in);    List<Shape> shapes = (List<Shape>)in.readObject();    System.out.println(shapes);  }}//*Output:[class com.ld.tij.Circlecolor[1] xPos[58] yPos[55] dim[93], class com.ld.tij.Squarecolor[0] xPos[61] yPos[61] dim[29], class com.ld.tij.Linecolor[3] xPos[68] yPos[0] dim[22], class com.ld.tij.Circlecolor[1] xPos[7] yPos[88] dim[28], class com.ld.tij.Squarecolor[0] xPos[51] yPos[89] dim[9], class com.ld.tij.Linecolor[3] xPos[78] yPos[98] dim[61], class com.ld.tij.Circlecolor[1] xPos[20] yPos[58] dim[16], class com.ld.tij.Squarecolor[0] xPos[40] yPos[11] dim[22], class com.ld.tij.Linecolor[3] xPos[4] yPos[83] dim[6], class com.ld.tij.Circlecolor[1] xPos[75] yPos[10] dim[42]]*//
可以看到,xPos, yPox, dim的值都被成功保存和恢复,但是static却出现了问题,所有恢复的static都应该是3,只有Line的color恢复正确。

Line中serializeStaticState()和deserializeStaticState()两个方法作为存储和读取过程的一部分被显示的调用。

以上例子正确修改方式:

1)为其他几何图形添加serializeStaticState()和deserializeStaticState()两个方法。

2)移除ArrayList shapeTypes相关代码。

3)添加对几何图形新的序列化和反序列化的显示调用。

因为序列化也会对private保存下来,如果是涉及安全问题还需要标记transient。


IO End~:)









原创粉丝点击