如何将Java序列化对象写入和读取出文件

68
我将写入多个对象到文件中,然后在代码的另一部分中检索它们。我的代码没有错误,但它不能正常工作。请帮我找出代码哪里有问题。 我从不同的网站阅读了不同的代码,但是没有一个对我起作用! 以下是我将对象写入文件的代码: MyClassList是一个包含我的类对象(必须写入文件)的ArrayList。
for (int cnt = 0; cnt < MyClassList.size(); cnt++) {
    FileOutputStream fout = new FileOutputStream("G:\\address.ser", true);
    ObjectOutputStream oos = new ObjectOutputStream(fout);
    oos.writeObject(MyClassList.get(cnt));
}

我在输出流的构造函数中添加了"true",因为我想将每个对象添加到文件的末尾。这样做正确吗?

以下是我的代码以从文件中读取对象:

 try {
     streamIn = new FileInputStream("G:\\address.ser");
     ObjectInputStream objectinputstream = new ObjectInputStream(streamIn);
     MyClass readCase = (MyClass) objectinputstream.readObject();
     recordList.add(readCase);
     System.out.println(recordList.get(i));
 } catch (Exception e) {
     e.printStackTrace();
 }

最终只打印出一个对象。现在,我不知道是我写得不对还是读得不对!


2
你在循环中写入对象,但却没有在循环中读取它们...? - Sikorski
你的 MyClass 类实现了 Serializable 接口吗? - morgano
你不能在使用ObjectOutputStream写入文件时追加内容,至少不是没有采取特殊措施的情况下。但是在循环中保持打开和关闭同一个文件是没有必要的。 - user207421
1
构造函数中的布尔值是用于自动刷新的,当你将其设置为true时,你不需要每次写入"fout.flush()"。 - BilalDja
5个回答

87

为什么不一次序列化整个列表呢?

FileOutputStream fout = new FileOutputStream("G:\\address.ser");
ObjectOutputStream oos = new ObjectOutputStream(fout);
oos.writeObject(MyClassList);

当然,前提是 MyClassList 是一个 ArrayList 或者 LinkedList,或者是另外一个实现了 Serializable 接口的集合。

在读取数据时,如果您只读取了一个数据项,则没有循环来获取所有已写入的数据项。


14
别忘了关闭fout和oos!! [fout.close(); oos.close();] - wea
3
通过在FileOutputStreamObjectOutputStream之间使用BufferedOutputStream,可以大大提高性能。 - Augustin
3
这个问题是关于阅读和写作的,但是这个答案只涉及写作部分。 - User

54

正如其他人建议的那样,您可以一次性将整个列表序列化和反序列化,这更简单,并且似乎完全符合您的意图。

在这种情况下,序列化代码变为

ObjectOutputStream oos = null;
FileOutputStream fout = null;
try{
    fout = new FileOutputStream("G:\\address.ser", true);
    oos = new ObjectOutputStream(fout);
    oos.writeObject(myClassList);
} catch (Exception ex) {
    ex.printStackTrace();
} finally {
    if(oos != null){
        oos.close();
    } 
}

反序列化变成了(假设myClassList是一个列表并希望您使用泛型):

ObjectInputStream objectinputstream = null;
try {
    FileInputStream streamIn = new FileInputStream("G:\\address.ser");
    objectinputstream = new ObjectInputStream(streamIn);
    List<MyClass> readCase = (List<MyClass>) objectinputstream.readObject();
    recordList.add(readCase);
    System.out.println(recordList.get(i));
} catch (Exception e) {
    e.printStackTrace();
} finally {
    if(objectinputstream != null){
        objectinputstream .close();
    } 
}

您还可以从文件中反序列化多个对象,就像您原本打算的那样:

ObjectInputStream objectinputstream = null;
try {
    streamIn = new FileInputStream("G:\\address.ser");
    objectinputstream = new ObjectInputStream(streamIn);
    MyClass readCase = null;
    do {
        readCase = (MyClass) objectinputstream.readObject();
        if(readCase != null){
            recordList.add(readCase);
        } 
    } while (readCase != null)        
    System.out.println(recordList.get(i));
} catch (Exception e) {
    e.printStackTrace();
} finally {
    if(objectinputstream != null){
        objectinputstream .close();
    } 
}
请不要忘记在finally子句中关闭流对象(注意:它可能会抛出异常)。 编辑 如评论中所建议的,最好使用try with resources,这样代码会变得更简单。
以下是序列化列表:
try(
    FileOutputStream fout = new FileOutputStream("G:\\address.ser", true);
    ObjectOutputStream oos = new ObjectOutputStream(fout);
){
    oos.writeObject(myClassList);
} catch (Exception ex) {
    ex.printStackTrace();
}

6
由于你实现了 try/catch,本回答应该被接受为正确答案。 - starscream_disco_party
3
如果你使用的是Java 8,你可以使用try-with-resources。 - zurbergram
@zurbergram 你说得对,这是更好的选择。我只是在写这个答案的时候没有机会使用它。 - C.Champagne

4
简单的程序,用于将对象写入文件并从文件中读取对象。

package program;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class TempList {

  public static void main(String[] args) throws Exception {
    Counter counter = new Counter(10);

    File f = new File("MyFile.txt");
    FileOutputStream fos = new FileOutputStream(f);
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    oos.writeObject(counter);
    oos.close();

    FileInputStream fis = new FileInputStream(f);
    ObjectInputStream ois = new ObjectInputStream(fis);
    Counter newCounter = (Counter) ois.readObject();
    System.out.println(newCounter.count);
    ois.close();
  }

}

class Counter implements Serializable {

  private static final long serialVersionUID = -628789568975888036 L;

  int count;

  Counter(int count) {
    this.count = count;
  }
}

运行程序后,您的控制台窗口将输出10,并且您可以通过单击下面图像中显示的图标找到Test文件夹中的文件。

enter image description here


0
如果您序列化整个列表,那么在读取文件时还必须将其反序列化为列表。这意味着您不可避免地会加载一个大文件到内存中,这可能是昂贵的。如果您有一个大文件,并且需要逐行(->对象)分块处理它,那么请继续执行您最初的想法。
序列化:
LinkedList<YourObject> listOfObjects = <something>;
try {
    FileOutputStream file = new FileOutputStream(<filePath>);
    ObjectOutputStream writer = new ObjectOutputStream(file);
    for (YourObject obj : listOfObjects) {
        writer.writeObject(obj);
    }
    writer.close();
    file.close();
} catch (Exception ex) {
    System.err.println("failed to write " + filePath + ", "+ ex);
}

反序列化:

try {
    FileInputStream file = new FileInputStream(<filePath>);
    ObjectInputStream reader = new ObjectInputStream(file);
    while (true) {
        try { 
            YourObject obj = (YourObject)reader.readObject();
            System.out.println(obj)
        } catch (Exception ex) {
            System.err.println("end of reader file ");
            break;
        }
    }
} catch (Exception ex) {
    System.err.println("failed to read " + filePath + ", "+ ex);
}

0

我认为你需要将每个对象写入一个独立的文件,或者在读取时将其拆分。 你也可以尝试对列表进行序列化,并在反序列化时检索它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接