大可制作:QQ群:31564239(asp|jsp|php|mysql)

Java Gossip: ObjectInputStream、ObjectOutputStream

在Java这样支持对象导向的程序中撰写程序,很多数据都是以对象的方式存在,在程序运行过后,您会希望将这些数据加以储存,以供下次执行程序时使用,这时您可以使用ObjectInputStream、ObjectOutputStream来进行这项工作。

要被储存的对象必须实现Serializable接口,说是实现,其实Serializable中并没有规范任何必须实现的方法,所以这边所谓实现的意义,其实像是对对象贴上一个标志,代表该对象是可以序列化的(Serializable)

一个实现的例子如下所示:

  • Student.java
package onlyfun.caterpillar;

import java.io.*;

public class Student implements Serializable {
private static final long serialVersionUID = 1L;

private String name;
private int score;

public Student() {
name = "N/A";
}

public Student(String name, int score) {
this.name = name;
this.score = score;
}

public void setName(String name) {
this.name = name;
}

public void setScore(int score) {
this.score = score;
}

public String getName() {
return name;
}

public int getScore() {
return score;
}

public void showData() {
System.out.println("name: " + name);
System.out.println("score: " + score);
}
}

您要注意到serialVersionUID,这代表了可序列化对象的版本, 如果您没有提供这个版本讯息,则会自动依类名称、实现的接口、成员等讯息来产生,如果是自动产生的,则下次您更改了Student类,则自动产生的 serialVersionUID也会跟着变更,当反序列化时两个serialVersionUID不相同的话,就会丢出 InvalidClassException,如果您想要维持版本讯息的一致,则要显式声明serialVersionUID。

ObjectInputStream、ObjectOutputStream为InputStream、OutputStream加上了可以让使用者写入 对象、读出对象的功能,在写入对象时,我们使用writeObject()方法,读出对象时我们使用readObject()方法,被读出的对象都是以 Object的类型传回,您必须将之转换为对象原来的类型,才能正确的操作被读回的对象,下面这个程序示范了如何简单的储存对象至文件中,并将之再度读 回

  • ObjectStreamDemo.java
package onlyfun.caterpillar;

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

public class ObjectStreamDemo {
public static void writeObjectsToFile(
Object[] objs, String filename) {
File file = new File(filename);

try {
ObjectOutputStream objOutputStream =
new ObjectOutputStream(
new FileOutputStream(file));
for(Object obj : objs) {
objOutputStream.writeObject(obj);
}
objOutputStream.close();
}
catch(IOException e) {
e.printStackTrace();
}
}

public static Object[] readObjectsFromFile(
String filename)
throws FileNotFoundException {
File file = new File(filename);

if(!file.exists())
throw new FileNotFoundException();

List list = new ArrayList();

try {
FileInputStream fileInputStream =
new FileInputStream(file);
ObjectInputStream objInputStream =
new ObjectInputStream(fileInputStream);

while(fileInputStream.available() > 0) {
list.add(objInputStream.readObject());
}
objInputStream.close();
}
catch(ClassNotFoundException e) {
e.printStackTrace();
}
catch(IOException e) {
e.printStackTrace();
}

return list.toArray();
}

public static void appendObjectsToFile(
Object[] objs, String filename)
throws FileNotFoundException {

File file = new File(filename);

if(!file.exists())
throw new FileNotFoundException();

try {
ObjectOutputStream objOutputStream =
new ObjectOutputStream(
new FileOutputStream(file, true)) {
protected void writeStreamHeader()
throws IOException {}
};

for(Object obj : objs) {
objOutputStream.writeObject(obj);
}
objOutputStream.close();
}
catch(IOException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
Student[] students = {new Student("caterpillar", 90),
new Student("justin", 85)};
// 写入新档
writeObjectsToFile(students, "data.dat");
try {
// 读取文件数据
Object[] objs = readObjectsFromFile("data.dat");
for(Object obj : objs) {
((Student) obj).showData();
}
System.out.println();

students = new Student[2];
students[0] = new Student("momor", 100);
students[1] = new Student("becky", 100);

// 附加至文件
appendObjectsToFile(students, "data.dat");

// 读取文件数据
objs = readObjectsFromFile("data.dat");
for(Object obj : objs) {
((Student) obj).showData();
}
}
catch(FileNotFoundException e) {
e.printStackTrace();
}
}
}

对象被写出时,会写入对象的类类型、类署名(Class signature),static与被标志为transient的成员则不会被写入。

在这边注意到以附加的形式写入数据至文件时,在试图将对象附加至一个先前已写入对象的文件时,由于ObjectOutputStream在 写入数据时,还会加上一个特别的标示头,而读取文件时会检查这个标示头,如果一个文件中被多次附加对象,那么该文件中会有多个标示头,如此读取检查时就会 发现不一致,这会丢出StreamCorrupedException,为此,您覆盖ObjectOutputStream的writeStreamHeader()方法,如果是以附加的方式来写入对象,就不写入标示头:
ObjectOutputStream objOutputStream =
    new ObjectOutputStream(
        new FileOutputStream(file, true)) {
            protected void writeStreamHeader()
                                 throws IOException {}
        }; 
 
将对象写出或读入并不仅限于文件存取,您也可以用于网路的数据传送,例如传送整个对象数据或是影像文件。