【问题标题】:Unable to read serializable class from .dat file无法从 .dat 文件中读取可序列化类
【发布时间】:2012-06-07 21:12:17
【问题描述】:

我尝试编写一个密钥持有者,我想使用 ObjectOutputStream 将密码写入 .dat 文件,然后使用 ObjectInputStream 读取它们。这是我编写对象的代码:

public void toFile()
{    
    try
    {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("passwords.dat"));     
        for(int i = 0; i<this.nrOfPW; i++)
        {
            if(this.PWlist[i] instanceof longPW)
            {
                oos.writeObject((longPW)this.PWlist[i]);
            }
            else
            {
                oos.writeObject((PinPW)this.PWlist[i]);
            }   
        }
        oos.close();
    }
    catch(IOException e)
    {
        e.getStackTrace();
    }
}

这似乎可行,但是当我尝试再次读取文件并将对象放入我的 PWlist 数组中时,它说 PinPW 不可序列化,即使 PinPW 实现了 Serializable 并且已导入。 PinPW (Info) 的基类也实现了 Serializable 并导入它。这是我读取文件的代码:

public void fromFile() 
{
    try 
    {
        ObjectInputStream objIn =  new ObjectInputStream(new FileInputStream("passwords.dat"));
        while(objIn.readObject() != null)
        {
            if(this.nrOfPW == this.PWlist.length)
            {
                expand(10);
            }
            if(objIn.readObject() instanceof PinPW)
            {
                this.PWlist[this.nrOfPW] = (PinPW)objIn.readObject();
                this.nrOfPW++;
            }
            else
            {
                this.PWlist[this.nrOfPW] = (longPW)objIn.readObject();
                this.nrOfPW++;
            }
        }
        objIn.close();
    }
    catch(EOFException e)
    {
        e.getStackTrace();
    }
    catch(IOException ex)   
    {
        ex.printStackTrace();   
    }
    catch(ClassNotFoundException ex)
    {
        ex.printStackTrace();   
    }
}

PWlist数组是Info数组,PinPW和longPW扩展了Info。

我该怎么做才能解决这个问题?

【问题讨论】:

  • 您能否发布生成的异常和堆栈跟踪 - 请参阅我的答案以了解可能解决此问题的方法以及 确定 修复错误。
  • [Off-topic] 按照惯例,Java 类使用 UppercaseStartingCamelCase 命名——这意味着您的 longPW 类应该是 LongPW。同样,变量使用小写StartingCamelCase 命名,因此您的PWList 变量应称为pwList
  • 您可以简化创建文件的方法。调用writeObject时不需要检查对象的类型,也不需要强制转换,因为这里的参数是Object。您必须确保所有传递的对象都实现接口java.io.Serializable,因为这不是编译器强制执行的(它是由序列化输出流强制执行的)。

标签: java objectoutputstream objectinputstream


【解决方案1】:

让我们修复“第一个错误,首先”......

在这段代码中:

while(objIn.readObject() != null)                         // reads object, tests then *discards* it
{
  ...

  if(objIn.readObject() instanceof PinPW)                 // reads object, tests then *discards* it
  {
    this.PWlist[this.nrOfPW] = (PinPW)objIn.readObject(); // conditionally read an object
    this.nrOfPW++;
  }
  else
  {
    this.PWlist[this.nrOfPW] = (longPW)objIn.readObject(); // conditionally read an object
    this.nrOfPW++;
  }
}

每次循环迭代时,您实际上都会读取 3 个对象。第一次读取对象以检查流中是否存在对象,下次读取对象并确定其类型时,将其丢弃。 然后你读取第三个对象并根据丢弃对象的类型进行转换。

此外,正如EJP 正确指出的那样,确定 ObjectInputStream 的流结束的正确方法是捕获文件结束异常。

您想改为这样做:

 try
 {
   while (true)
   {
     final Object o = objIn.readObject();            // read the object from the stream

     ...

     if (o instanceof PinPW)
     {
       this.PWlist[this.nrOfPW] = (PinPW) o;         // cast to correct type
       this.nrOfPW++;
     }
     else
     {
       this.PWlist[this.nrOfPW] = (longPW) o;        // cast to correct type
       this.nrOfPW++;
     }
   }
 }
 catch (EOFException e)
 {
   // end of stream reached ...
   // ... close the file descriptor etc ...
 }

【讨论】:

  • @Psyberion 根本没有理由测试 null,除非您在序列化时写入 null。在序列化流上对 EOS 的正确测试是捕获 EOFException
【解决方案2】:

你这里有问题。

while(objIn.readObject() != null)
    {
        if(this.nrOfPW == this.PWlist.length)
        {
            expand(10);
        }
        if(objIn.readObject() instanceof PinPW)
        {
            this.PWlist[this.nrOfPW] = (PinPW)objIn.readObject();
            this.nrOfPW++;
        }
        else
        {
            this.PWlist[this.nrOfPW] = (longPW)objIn.readObject();
            this.nrOfPW++;
        }
    }

您正在多次阅读一个对象。尝试保存它,然后使用它。 if(objIn.readObject() instanceof PinPW) 读取一次,读取两次,this.PWlist[this.nrOfPW] = (PinPW)objIn.readObject();读了三遍,而应该只有一次。 PS:在一段时间内使用 Greg Kopff 语法并且不使用 final 关键字,因为您想在其中保存更多对象。

【讨论】:

    【解决方案3】:

    我只是想指出 toFile() 函数中的 if-else 块完全没有意义。 writeObject() 接受一个 Object 参数。它不关心它是什么类型的 Object,只要它是可序列化的。

    虽然您的fromFile() 方法存在严重缺陷,但它不会导致NotSerializableException。我相信你提到的异常实际上发生在toFile()

    原因很简单:你没有完全阅读和理解ObjectOutputStream的文档。具体来说,对象及其所有非瞬态字段及其祖先类中的所有非瞬态字段都必须实现可序列化。它还必须有一个公共的无参数构造函数。

    【讨论】:

    • “它必须有一个公共的无参数构造函数” - 对于 Externalizable 类是正确的,但对于 Serializable 类则不然。
    • 嗯,你说得对,这不是一个严格的要求,但我一直都有这些要求,因为如果没有子分类,就会出现并发症。懒得记住所有的规则,所以我就这样统一起来:)
    • 你在想this: "为了允许不可序列化类的子类型被序列化,子类型可能会负责保存和恢复超类型的公共、受保护的状态, 和(如果可访问)包字段。仅当它扩展的类具有可访问的无参数构造函数来初始化类的状态时,子类型才可以承担此责任。”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-19
    • 1970-01-01
    • 2015-01-16
    • 1970-01-01
    相关资源
    最近更新 更多