// serialization/Worm.java// Demonstrates object serializationimportjava.io.*;importjava.util.*;classDataimplementsSerializable{privateint n;Data(intn){this.n= n;}@OverridepublicStringtoString(){returnInteger.toString(n);}}publicclassWormimplementsSerializable{privatestaticRandom rand =newRandom(47);privateData[] d ={newData(rand.nextInt(10)),newData(rand.nextInt(10)),newData(rand.nextInt(10))};privateWorm next;privatechar c; // Value of i == number of segmentspublicWorm(inti,charx){System.out.println("Worm constructor: "+ i); c = x;if(--i >0) next =newWorm(i,(char)(x +1));}publicWorm(){System.out.println("No-arg constructor");}@OverridepublicStringtoString(){StringBuilder result =newStringBuilder(":");result.append(c);result.append("(");for(Data dat : d)result.append(dat);result.append(")");if(next !=null)result.append(next);returnresult.toString();}publicstaticvoidmain(String[]args)throwsClassNotFoundException,IOException{Worm w =newWorm(6,'a');System.out.println("w = "+ w);try(ObjectOutputStream out =newObjectOutputStream(new FileOutputStream("worm.dat"))){out.writeObject("Worm storage\n");out.writeObject(w);}try(ObjectInputStream in =newObjectInputStream(new FileInputStream("worm.dat"))){String s =(String)in.readObject();Worm w2 =(Worm)in.readObject();System.out.println(s +"w2 = "+ w2);}try(ByteArrayOutputStream bout =newByteArrayOutputStream();ObjectOutputStream out2 =newObjectOutputStream(bout)){out2.writeObject("Worm storage\n");out2.writeObject(w);out2.flush();try(ObjectInputStream in2 =newObjectInputStream(new ByteArrayInputStream(bout.toByteArray()))){String s =(String)in2.readObject();Worm w3 =(Worm)in2.readObject();System.out.println(s +"w3 = "+ w3);}}}}
其中,字段 s 和 i 只在第二个构造器中初始化,而不是在默认的构造器中初始化。这意味着假如不在 readExternal() 中初始化 s 和 i,s 就会为 null,而 i 就会为零(因为在创建对象的第一步中将对象的存储空间清理为 0)。如果注释掉跟随于"You must do this”后面的两行代码,然后运行程序,就会发现当对象被还原后,s 是 null,而 i 是零。
对象序列化的一个重要限制是它只是 Java 的解决方案:只有 Java 程序才能反序列化这种对象。一种更具互操作性的解决方案是将数据转换为 XML 格式,这可以使其被各种各样的平台和语言使用。
因为 XML 十分流行,所以用它来编程时的各种选择不胜枚举,包括随 JDK 发布的 javax.xml.*类库。我选择使用 Elliotte Rusty Harold 的开源 XOM 类库(可从 www.xom.nu 下载并获得文档),因为它看起来最简单,同时也是最直观的用 Java 产生和修改 XML 的方式。另外,XOM 还强调了 XML 的正确性。
作为一个示例,假设有一个 APerson 对象,它包含姓和名,你想将它们序列化到 XML 中。下面的 APerson 类有一个 getXML() 方法,它使用 XOM 来产生被转换为 XML 的 Element 对象的 APerson 数据;还有一个构造器,接受 Element 并从中抽取恰当的 APerson 数据(注意,XML 示例都在它们自己的子目录中):
输出为:
XOM 的方法都具有相当的自解释性,可以在 XOM 文档中找到它们。XOM 还包含一个 Serializer 类,你可以在 format() 方法中看到它被用来将 XML 转换为更具可读性的格式。如果只调用 toXML(),那么所有东西都会混在一起,因此 Serializer 是一种便利工具。
从 XML 文件中反序列化 Person 对象也很简单:
People 构造器使用 XOM 的 Builder.build() 方法打开并读取一个文件,而 getChildElements() 方法产生了一个 Elements 列表(不是标准的 Java List,只是一个拥有 size() 和 get() 方法的对象,因为 Harold 不想强制人们使用特定版本的 Java,但是仍旧希望使用类型安全的容器)。在这个列表中的每个 Element 都表示一个 Person 对象,因此它可以传递给第二个 Person 构造器。注意,这要求你提前知道 XML 文件的确切结构,但是这经常会有些问题。如果文件结构与你预期的结构不匹配,那么 XOM 将抛出异常。对你来说,如果你缺乏有关将来的 XML 结构的信息,那么就有可能会编写更复杂的代码去探测 XML 文档,而不是只对其做出假设。
为了获取这些示例去编译它们,你必须将 XOM 发布包中的 JAR 文件放置到你的类路径中。
这里只给出了用 Java 和 XOM 类库进行 XML 编程的简介,更详细的信息可以浏览 www.xom.nu 。
// serialization/Blip3.java
// Reconstructing an externalizable object
import java.io.*;
public class Blip3 implements Externalizable {
private int i;
private String s; // No initialization
public Blip3() {
System.out.println("Blip3 Constructor");
// s, i not initialized
}
public Blip3(String x, int a) {
System.out.println("Blip3(String x, int a)");
s = x;
i = a;
// s & i initialized only in non-no-arg constructor.
}
@Override
public String toString() { return s + i; }
@Override
public void writeExternal(ObjectOutput out)
throws IOException {
System.out.println("Blip3.writeExternal");
// You must do this:
out.writeObject(s);
out.writeInt(i);
}
@Override
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
System.out.println("Blip3.readExternal");
// You must do this:
s = (String)in.readObject();
i = in.readInt();
}
public static void main(String[] args) {
System.out.println("Constructing objects:");
Blip3 b3 = new Blip3("A String ", 47);
System.out.println(b3);
try(
ObjectOutputStream o = new ObjectOutputStream(
new FileOutputStream("Blip3.serialized"))
) {
System.out.println("Saving object:");
o.writeObject(b3);
} catch(IOException e) {
throw new RuntimeException(e);
}
// Now get it back:
System.out.println("Recovering b3:");
try(
ObjectInputStream in = new ObjectInputStream(
new FileInputStream("Blip3.serialized"))
) {
b3 = (Blip3)in.readObject();
} catch(IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
System.out.println(b3);
}
}
Constructing objects:
Blip3(String x, int a)
A String 47
Saving object:
Blip3.writeExternal
Recovering b3:
Blip3 Constructor
Blip3.readExternal
A String 47
// serialization/Logon.java
// Demonstrates the "transient" keyword
import java.util.concurrent.*;
import java.io.*;
import java.util.*;
import onjava.Nap;
public class Logon implements Serializable {
private Date date = new Date();
private String username;
private transient String password;
public Logon(String name, String pwd) {
username = name;
password = pwd;
}
@Override
public String toString() {
return "logon info: \n username: " +
username + "\n date: " + date +
"\n password: " + password;
}
public static void main(String[] args) {
Logon a = new Logon("Hulk", "myLittlePony");
System.out.println("logon a = " + a);
try(
ObjectOutputStream o =
new ObjectOutputStream(
new FileOutputStream("Logon.dat"))
) {
o.writeObject(a);
} catch(IOException e) {
throw new RuntimeException(e);
}
new Nap(1);
// Now get them back:
try(
ObjectInputStream in = new ObjectInputStream(
new FileInputStream("Logon.dat"))
) {
System.out.println(
"Recovering object at " + new Date());
a = (Logon)in.readObject();
} catch(IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
System.out.println("logon a = " + a);
}
}
logon a = logon info:
username: Hulk
date: Tue May 09 06:07:47 MDT 2017
password: myLittlePony
Recovering object at Tue May 09 06:07:49 MDT 2017
logon a = logon info:
username: Hulk
date: Tue May 09 06:07:47 MDT 2017
password: null
// serialization/SerialCtl.java
// Controlling serialization by adding your own
// writeObject() and readObject() methods
import java.io.*;
public class SerialCtl implements Serializable {
private String a;
private transient String b;
public SerialCtl(String aa, String bb) {
a = "Not Transient: " + aa;
b = "Transient: " + bb;
}
@Override
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);
try (
ByteArrayOutputStream buf =
new ByteArrayOutputStream();
ObjectOutputStream o =
new ObjectOutputStream(buf);
) {
o.writeObject(sc);
// Now get it back:
try (
ObjectInputStream in =
new ObjectInputStream(
new ByteArrayInputStream(
buf.toByteArray()));
) {
SerialCtl sc2 = (SerialCtl)in.readObject();
System.out.println("After:\n" + sc2);
}
} catch(IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
Before:
Not Transient: Test1
Transient: Test2
After:
Not Transient: Test1
Transient: Test2
o.writeObject(sc);
// serialization/MyWorld.java
import java.io.*;
import java.util.*;
class House implements Serializable {}
class Animal implements Serializable {
private String name;
private House preferredHouse;
Animal(String nm, House h) {
name = nm;
preferredHouse = h;
}
@Override
public String toString() {
return name + "[" + super.toString() +
"], " + preferredHouse + "\n";
}
}
public class MyWorld {
public static void main(String[] args) {
House house = new House();
List<Animal> animals = new ArrayList<>();
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);
try(
ByteArrayOutputStream buf1 =
new ByteArrayOutputStream();
ObjectOutputStream o1 =
new ObjectOutputStream(buf1)
) {
o1.writeObject(animals);
o1.writeObject(animals); // Write a 2nd set
// Write to a different stream:
try(
ByteArrayOutputStream buf2 = new ByteArrayOutputStream();
ObjectOutputStream o2 = new ObjectOutputStream(buf2)
) {
o2.writeObject(animals);
// Now get them back:
try(
ObjectInputStream in1 =
new ObjectInputStream(
new ByteArrayInputStream(
buf1.toByteArray()));
ObjectInputStream in2 =
new ObjectInputStream(
new ByteArrayInputStream(
buf2.toByteArray()))
) {
List
animals1 = (List)in1.readObject(),
animals2 = (List)in1.readObject(),
animals3 = (List)in2.readObject();
System.out.println(
"animals1: " + animals1);
System.out.println(
"animals2: " + animals2);
System.out.println(
"animals3: " + animals3);
}
}
} catch(IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
animals: [Bosco the dog[Animal@15db9742],
House@6d06d69c
, Ralph the hamster[Animal@7852e922], House@6d06d69c
, Molly the cat[Animal@4e25154f], House@6d06d69c
]
animals1: [Bosco the dog[Animal@7ba4f24f],
House@3b9a45b3
, Ralph the hamster[Animal@7699a589], House@3b9a45b3
, Molly the cat[Animal@58372a00], House@3b9a45b3
]
animals2: [Bosco the dog[Animal@7ba4f24f],
House@3b9a45b3
, Ralph the hamster[Animal@7699a589], House@3b9a45b3
, Molly the cat[Animal@58372a00], House@3b9a45b3
]
animals3: [Bosco the dog[Animal@4dd8dc3],
House@6d03e736
, Ralph the hamster[Animal@568db2f2], House@6d03e736
, Molly the cat[Animal@378bf509], House@6d03e736
]
// serialization/AStoreCADState.java
// Saving the state of a fictitious CAD system
import java.io.*;
import java.util.*;
import java.util.stream.*;
enum Color { RED, BLUE, GREEN }
abstract class Shape implements Serializable {
private int xPos, yPos, dimension;
private static Random rand = new Random(47);
private static int counter = 0;
public abstract void setColor(Color newColor);
public abstract Color getColor();
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 Color color = Color.RED;
Circle(int xVal, int yVal, int dim) {
super(xVal, yVal, dim);
}
public void setColor(Color newColor) {
color = newColor;
}
public Color getColor() { return color; }
}
class Square extends Shape {
private static Color color = Color.RED;
Square(int xVal, int yVal, int dim) {
super(xVal, yVal, dim);
}
public void setColor(Color newColor) {
color = newColor;
}
public Color getColor() { return color; }
}
class Line extends Shape {
private static Color color = Color.RED;
public static void
serializeStaticState(ObjectOutputStream os)
throws IOException { os.writeObject(color); }
public static void
deserializeStaticState(ObjectInputStream os)
throws IOException, ClassNotFoundException {
color = (Color)os.readObject();
}
Line(int xVal, int yVal, int dim) {
super(xVal, yVal, dim);
}
public void setColor(Color newColor) {
color = newColor;
}
public Color getColor() { return color; }
}
public class AStoreCADState {
public static void main(String[] args) {
List<Class<? extends Shape>> shapeTypes =
Arrays.asList(
Circle.class, Square.class, Line.class);
List<Shape> shapes = IntStream.range(0, 10)
.mapToObj(i -> Shape.randomFactory())
.collect(Collectors.toList());
// Set all the static colors to GREEN:
shapes.forEach(s -> s.setColor(Color.GREEN));
// Save the state vector:
try(
ObjectOutputStream out =
new ObjectOutputStream(
new FileOutputStream("CADState.dat"))
) {
out.writeObject(shapeTypes);
Line.serializeStaticState(out);
out.writeObject(shapes);
} catch(IOException e) {
throw new RuntimeException(e);
}
// Display the shapes:
System.out.println(shapes);
}
}
[class Circlecolor[GREEN] xPos[58] yPos[55] dim[93]
, class Squarecolor[GREEN] xPos[61] yPos[61] dim[29]
, class Linecolor[GREEN] xPos[68] yPos[0] dim[22]
, class Circlecolor[GREEN] xPos[7] yPos[88] dim[28]
, class Squarecolor[GREEN] xPos[51] yPos[89] dim[9]
, class Linecolor[GREEN] xPos[78] yPos[98] dim[61]
, class Circlecolor[GREEN] xPos[20] yPos[58] dim[16]
, class Squarecolor[GREEN] xPos[40] yPos[11] dim[22]
, class Linecolor[GREEN] xPos[4] yPos[83] dim[6]
, class Circlecolor[GREEN] xPos[75] yPos[10] dim[42]
]
// serialization/RecoverCADState.java
// Restoring the state of the fictitious CAD system
// {RunFirst: AStoreCADState}
import java.io.*;
import java.util.*;
public class RecoverCADState {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
try(
ObjectInputStream in =
new ObjectInputStream(
new FileInputStream("CADState.dat"))
) {
// 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);
} catch(IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
[class Circlecolor[RED] xPos[58] yPos[55] dim[93]
, class Squarecolor[RED] xPos[61] yPos[61] dim[29]
, class Linecolor[GREEN] xPos[68] yPos[0] dim[22]
, class Circlecolor[RED] xPos[7] yPos[88] dim[28]
, class Squarecolor[RED] xPos[51] yPos[89] dim[9]
, class Linecolor[GREEN] xPos[78] yPos[98] dim[61]
, class Circlecolor[RED] xPos[20] yPos[58] dim[16]
, class Squarecolor[RED] xPos[40] yPos[11] dim[22]
, class Linecolor[GREEN] xPos[4] yPos[83] dim[6]
, class Circlecolor[RED] xPos[75] yPos[10] dim[42]
]
// serialization/APerson.java
// Use the XOM library to write and read XML
// nu.xom.Node comes from http://www.xom.nu
import nu.xom.*;
import java.io.*;
import java.util.*;
public class APerson {
private String first, last;
public APerson(String first, String last) {
this.first = first;
this.last = last;
}
// Produce an XML Element from this APerson object:
public Element getXML() {
Element person = new Element("person");
Element firstName = new Element("first");
firstName.appendChild(first);
Element lastName = new Element("last");
lastName.appendChild(last);
person.appendChild(firstName);
person.appendChild(lastName);
return person;
}
// Constructor restores a APerson from XML:
public APerson(Element person) {
first = person
.getFirstChildElement("first").getValue();
last = person
.getFirstChildElement("last").getValue();
}
@Override
public String toString() {
return first + " " + last;
}
// Make it human-readable:
public static void
format(OutputStream os, Document doc)
throws Exception {
Serializer serializer =
new Serializer(os,"ISO-8859-1");
serializer.setIndent(4);
serializer.setMaxLength(60);
serializer.write(doc);
serializer.flush();
}
public static void main(String[] args) throws Exception {
List<APerson> people = Arrays.asList(
new APerson("Dr. Bunsen", "Honeydew"),
new APerson("Gonzo", "The Great"),
new APerson("Phillip J.", "Fry"));
System.out.println(people);
Element root = new Element("people");
for(APerson p : people)
root.appendChild(p.getXML());
Document doc = new Document(root);
format(System.out, doc);
format(new BufferedOutputStream(
new FileOutputStream("People.xml")), doc);
}
}
// serialization/People.java
// nu.xom.Node comes from http://www.xom.nu
// {RunFirst: APerson}
import nu.xom.*;
import java.io.File;
import java.util.*;
public class People extends ArrayList<APerson> {
public People(String fileName) throws Exception {
Document doc =
new Builder().build(new File(fileName));
Elements elements =
doc.getRootElement().getChildElements();
for(int i = 0; i < elements.size(); i++)
add(new APerson(elements.get(i)));
}
public static void main(String[] args) throws Exception {
People p = new People("People.xml");
System.out.println(p);
}
}
/* Output:
[Dr. Bunsen Honeydew, Gonzo The Great, Phillip J. Fry]
*/