2

我正在尝试编写一个程序,用户可以:1)将一个人添加到联系人(姓名,电话,电子邮件),2)从联系人中删除一个人,3)从联系人中读取所有内容。

我这样做的方式是要求用户做出选择并分别做任何事情。对于写作,我只是将一个对象写入文件。对于删除,我想我会询问用户“姓氏”,它将用作键(因为我使用的是 TreeMap)并将删除键处的值(对象)。

所以我在这里阅读有问题。我正在尝试像这样读取对象:

public void readContact()
{
  TreeMap<String, Contact> contactMap = new TreeMap<String, Contact>();
  try 
  {
    ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(
                                                   new FileInputStream(file)));

    while( in.available() > 0 ) //This line does NOT read  
    {
      Contact c = (Contact)in.readObject();
      contactMap.put(c.getLastName(), c);           
    }

    for(Map.Entry contact : contactMap.entrySet() ) 
    {
      Contact con = contactMap.get( contact.getKey() );
      System.out.println( con.getLastName() + ", " + con.getFirstName() + ": " + con.getPhoneNumber() + "\t" + con.getEmail());
    }
  }
  catch(Exception e)
  {
    System.out.println("Exception caught");
  } 
}

请不要建议做类似的事情,while(true)直到我得到EOFException因为:

  1. 我相信这不是异常处理的目的
  2. 在此之后我还有更多事情要做,所以我不能让程序终止'
4

7 回答 7

7

在我得到 EOFException 之前,请不要建议做类似 while(true) 的事情

这正是我的建议。当您搜索答案时,根据这样的任意标准限制解决方案空间会适得其反。

因为:

我相信这不是异常处理的目的

当您调用的 API 抛出异常时,就像这个一样,您别无选择,只能捕获它。无论您对“异常处理的用途”有何看法,您都受制于 API 的设计者设计 API时的想法

在此之后我还有更多事情要做,所以我不能让程序终止'

所以不要终止它。抓住EOFException,关闭输入,然后跳出循环。

我已经看到浪费在“异常处理的用途”上的昂贵的编程时间比我真正相信的要多。

于 2012-12-31T03:57:52.070 回答
2

我知道您正在寻找不使用异常处理的答案,但我相信在这种情况下使用EOFException来确定何时读取所有输入是正确的方法。

EOFException的 JavaDoc指出

此异常主要由数据输入流用来表示流结束。请注意,许多其他输入操作在流结束时返回一个特殊值,而不是引发异常。

因此,有些输入流使用其他方式发出文件结束信号,但ObjectInputStream#readObject用于ObjectInputStream$BlockDataInputStream#peekByte确定是否有更多数据要读取,并在到达流结束时peekByte抛出一个。EOFException

因此,可以将此异常用作已到达文件末尾的指示符。

为了在不中断程序流程的情况下处理异常,一些可能的异常应该在层次结构中向上传递。它们可以由try - catch调用readContact().

EOFException可以简单地用作我们已完成读取对象的指示。

public TreeMap<String, Contact> readContact() throws FileNotFoundException,
            IOException, ClassNotFoundException {

    TreeMap<String, Contact> contactMap = new TreeMap<String, Contact>();

    // The following call can throw a FileNotFoundException or an IOException.
    // Since this is probably better dealt with in the calling function, 
    // readContact is made to throw these exceptions instead.
    ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(
                new FileInputStream(file)));

    while (true) {
        try {
            // Read the next object from the stream. If there is none, the
            // EOFException will be thrown.
            // This call might also throw a ClassNotFoundException, which can be passed
            // up or handled here.
            Contact c = (Contact) in.readObject();
            contactMap.put(c.getLastName(), c);

            for (Map.Entry<String, Contact> contact : contactMap.entrySet()) {
                Contact con = contact.getValue();
                System.out.println(con.getLastName() + ", "
                          + con.getFirstName() + ": " + con.getPhoneNumber()
                          + "\t" + con.getEmail());
            }
        } catch (EOFException e) {
            // If there are no more objects to read, return what we have.
            return contactMap;
        } finally {
            // Close the stream.
            in.close();
        }
    }
}
于 2012-12-31T02:55:41.997 回答
2
  1. 'ObjectInputStream.available 返回 0' 是一个已知问题,请参阅http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4954570,由于我们无法使用它,我认为EOFException在您的情况下这是一种合理的方法。捕获EOFExcepion不会终止您的程序。
  2. 您可以使用 将对象数量写入文件ObjectOutputStream.writeInt,然后您将使用 读取此数字ObjectInputStream.readInt并知道要读取多少对象
  3. 您可以null用作 EOF 标记。
  4. 您可以将对象保存为数组或 List 甚至 Map,然后使用 one 读取它们readObject
于 2012-12-31T02:58:02.463 回答
2

为什么从文件中读取对象时这么麻烦,只需将哈希映射保存到文件中并从文件中读取一次,然后执行任何操作。

另外,我建议使用任何一种面向对象的数据库,如Db4o来快速执行此操作,然后您就不必担心文件结束异常

于 2012-12-31T03:19:48.160 回答
2

- 不仅Exceptions用于在调用方法时出现问题时发出警报,而且还用于各种其他用途。ThreadsIO

-您可以使用Exception来表示文件的结束

-使用try-catch组合与上述一起工作,以保持程序流畅。

于 2012-12-31T03:04:54.170 回答
0

你发现了什么

即使文件中有未读字节,您也发现FileInputStream.available()返回 0 !为什么是这样?这可能会发生(由于以下几个原因导致不可靠:FileInputStream.available()

  1. 根据此文档FileInputStream.available() 近似于可以在不阻塞的情况下读取的字节数
  2. 硬盘驱动器本身可能会在读取中更改其操作(从旋转变为不旋转)
  3. 您尝试访问的文件是远程系统上的文件或设备文件:FileInputStream.available 可能会愚弄我吗?
  4. 可能会FileInputStream被阻止

一些替代方法

作为依靠 EOFException 关闭文件的替代方法,您可以使用(非常小的!)二进制文件来跟踪文件中的对象数量。(从您的代码来看,您似乎只是将对象写入文件。)我使用它的方式只是为了

  1. 存储数字本身将消耗的字节数
  2. 使用该字节数,存储数字本身

比如第一次创建序列化文件,我可以做二进制文件存储1 1(指定序列化文件中的Objects个数占1个字节,这个个数为1)。这样,在 255 个 Objects 之后(记住,一个无符号字节最多只能存储 2 8-1 == 255),如果我写另一个 Object(对象编号 256 到 256 2-1 == 65535),二进制文件将有,作为内容,2 1 0它指定数字占用2个字节并且是1 * 256 1 + 0 == 256。前提是序列化是可靠的(祝你好运:http: //www.ibm.com/ developerworks/library/j-serialtest/index.html),此方法可让您存储(和检测)多达 256个255-1 字节(这几乎意味着这种方法可以无限期地工作)。


代码本身

如何实现这样的事情将是这样的:

ObjectOutputStream yourOutputStream = new ObjectOutputStream(new FileOutputStream(workingDirectory + File.separatorChar + yourFileName);  //The outputStream
File binaryFile = new File(workingDirectory + File.separatorChar + nameOfFile); //the binary file
int numOfObjects = 0, numOfBytes; //The number of Objects in the file
//reading the number of Objects from the file (if the file exists)
try
{
    FileInputStream byteReader = new FileInputStream(binaryFile);
    numOfBytes = byteReader.read();
    //Read the rest of the bytes (the number itself)
    for (int exponent = numOfBytes; exponent >= 0; exponent--)
    {
        numOfObjects += byteReader.read() * Math.pow(256,exponent);
    }
}
catch (IOException exception)
{
    //if an exception was thrown due to the file not existing
    if (exception.getClass() == FileNotFoundException.class)
    {
        //we simply create the file (as mentioned earlier in this answer)
         try 
         {
             FileOutputStream fileCreator = new FileOutputStream(binaryFile);
             //we write the integers '1','1' to the file 
             for (int x = 0; x < 2; x++) fileCreator.write(1);
             //attempt to close the file
             fileCreator.close();
         }
         catch (IOException innerException)
         {
              //here, we need to handle this; something went wrong
              innerException.printStackTrace();
              System.exit(-1);
         }
    }
    else
    {
         exception.printStackTrace();
         System.exit(-2);
    }
}

现在,我们有了文件中对象的数量(我留给你弄清楚如何更新字节以指示在yourOutputStream调用时又写入了一个对象writeObject(yourObject);;我必须上班。)

编辑:yourOutputStream要么将所有数据写入其中,要么将binaryFile数据附加到其中。我刚刚发现这RandomAccessFile将是一种将数据插入文件中任何位置的方法。再一次,我把细节留给你。但是,您想这样做。

于 2013-08-06T04:12:05.003 回答
-1

您可以将最后一个对象写为 null。然后迭代直到你在阅读端得到一个空值。例如

while ((object = inputStream.readObject()) != null) {
  // ...
}
于 2014-11-17T10:22:56.397 回答