2012-09-05 53 views
5

我一直在尝试做一个需要可追加ObjectOutputStream的小项目。 我已经经历了几个解决方案,我发现this它似乎首先解决了我的问题。但是在我的项目的进一步发展中,我开始意想不到的例外。 以下是我的课程。当追加对象OutputStream时出现ClassCastException

public class PPAccount implements Serializable 
{ 
    private Profile profile; 
    private String email; 
    private float accountBal; 
    private boolean isActivated; 
    private String activationCode; 
    private ArrayList<Transaction> transactions; 

    //a few functions 
} 
public class PPRestrictedAccount extends PPAccount { 
    private String parentEmail; 
    private float withdrawLimit; 

     //a few functions 
} 
public class PPBusinessAccount extends PPAccount { 
    private ArrayList <PPRestrictedAccount> accountOperators; 

     //a few functions 
} 
public class PPStudentAccount extends PPAccount { 
    private String parentEmail; 

     //a few functions 
} 

我观察是什么,使用this我已经覆盖了ObjectOutputStream的,并用它当我追加的对象文件。但是,如果我写:

PPBusinessAccount首先,重复任意次数...然后写PPAccount一切都很好。 PPAccount首先,重复....然后写PPBusinessAccount然后写PPAccount,它写得很好,但在阅读时我得到ClassCastException

我尝试读取对象并将它们直接存储在Object类的实例中,以避免类抛出但仍然readObject()抛出ClassCastException

我尽力描述我的情况,告诉你是否没有得到任何东西。这是为什么发生?它是否与第一次编写的标题有关?沿着Base类头的行不能支持子类?什么是转身?

我做这样的演员:

Object o = ois.readObject();  //Surprisingly exception is raised here (line:50 in DataStore) 
PPAccount ppa = (PPAccount)o; 

堆栈跟踪

java.lang.ClassCastException: java.lang.String cannot be cast to java.io.ObjectStreamClass 
    at java.io.ObjectInputStream.readClassDesc(Unknown Source) 
    at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source) 
    at java.io.ObjectInputStream.readClassDesc(Unknown Source) 
    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source) 
    at java.io.ObjectInputStream.readObject0(Unknown Source) 
    at java.io.ObjectInputStream.readObject(Unknown Source) 
    at java.util.ArrayList.readObject(Unknown Source) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) 
    at java.lang.reflect.Method.invoke(Unknown Source) 
    at java.io.ObjectStreamClass.invokeReadObject(Unknown Source) 
    at java.io.ObjectInputStream.readSerialData(Unknown Source) 
    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source) 
    at java.io.ObjectInputStream.readObject0(Unknown Source) 
    at java.io.ObjectInputStream.defaultReadFields(Unknown Source) 
    at java.io.ObjectInputStream.readSerialData(Unknown Source) 
    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source) 
    at java.io.ObjectInputStream.readObject0(Unknown Source) 
    at java.io.ObjectInputStream.readObject(Unknown Source) 
    at in.msitprogram.iiit.paypal.persistance.DataStore.lookupAccount(DataStore.java:50) 
    at in.msitprogram.iiit.paypal.persistance.DataStore.writeAccount(DataStore.java:131) 
    at in.msitprogram.iiit.paypal.console.PPNewAccountScreen.show(PPNewAccountScreen.java:78) 
    at in.msitprogram.iiit.paypal.console.MainMenu.show(MainMenu.java:42) 
    at in.msitprogram.iiit.paypal.PPSystem.main(PPSystem.java:17) 
Exception in thread "main" java.lang.NullPointerException 
    at in.msitprogram.iiit.paypal.persistance.DataStore.lookupAccount(DataStore.java:66) 
    at in.msitprogram.iiit.paypal.persistance.DataStore.writeAccount(DataStore.java:131) 
    at in.msitprogram.iiit.paypal.console.PPNewAccountScreen.show(PPNewAccountScreen.java:78) 
    at in.msitprogram.iiit.paypal.console.MainMenu.show(MainMenu.java:42) 
    at in.msitprogram.iiit.paypal.PPSystem.main(PPSystem.java:17) 

lookUpAccount从流中读取数据,而writeAccount写入流,这里是代码:

public static PPAccount lookupAccount(String email) throws IOException, ClassNotFoundException 
    { 
     PPAccount account = null; //initialize it after reading from file 
     // write code to open the files, read 
     PPAccount foundAccount=null; 
     ObjectInputStream ois=null; 
     FileInputStream fis=null; 
     File ff = new File(PPConstants.AllAccountDetails); 
     if(!ff.exists()) 
     { 
      //System.out.println("Required file not found"); 
      return null; 
     } 
     try 
     { 
      fis=new FileInputStream(PPConstants.AllAccountDetails); 
      ois = new ObjectInputStream(fis); 
      while(fis.available()>0 && foundAccount==null) 
      { 
       //Object o=null; 
       PPAccount ppa=null; 
       try 
       { 
        ppa = (PPAccount)ois.readObject(); 
        if(ppa==null) 
         return null; 
        System.out.println(ppa); 
       } 

       catch(ClassCastException cce) 
       { 
        System.out.println("Class cast exception "+cce.getCause()); 
        cce.printStackTrace(); 
       } 
       if(email.equals(ppa.getEmail())) 
       { 
        foundAccount=ppa; 
        break; 
       } 
       if(ppa instanceof PPBusinessAccount) 
       { 
        PPBusinessAccount ppba = (PPBusinessAccount)ppa; 
        ArrayList<PPRestrictedAccount> alist=ppba.getAccountOperators(); 
        if(alist==null) 
         continue; 
        Iterator<PPRestrictedAccount> it = alist.iterator(); 
        while(it.hasNext()) 
        { 
         PPRestrictedAccount ppr=(PPRestrictedAccount) it.next(); 
         System.out.println(ppr); 
         if(email.equals(ppr.getEmail())) 
         { 
          foundAccount = ppr; 
          break; 
         } 
        }//iterators while loop 
       }//if it is a businessAccount 
      }//outer while 
     }//try 
     finally 
     { 
      if(ois!=null) 
       ois.close(); 
      if(fis!=null) 
       fis.close(); 
     } 
     return foundAccount; 
    } 
    public static void writeAccount(PPAccount account,Boolean append) throws IOException, ClassNotFoundException, DuplicateAccountException 
    { 
     ObjectOutputStream oos=null; 
     FileOutputStream fos=null; 
     try 
     { 
      if(!append) 
      { 
       fos= new FileOutputStream(PPConstants.AllAccountDetails); 
       oos = new ObjectOutputStream(fos); 
       //System.out.println("Not Appending"); 
       oos.writeObject(account); 
      } 
      else 
      { 
       File ff = new File(PPConstants.AllAccountDetails); 
       if(!ff.exists()) 
       { 
        System.out.println("Required file not found"); 
        return; 
       } 
       PPAccount aa=lookupAccount(account.getEmail()); 
       if(aa!=null) 
        throw new DuplicateAccountException("An Account already exits with this email-ID"); 
       oos = new AppendingObjectOutputStream(new FileOutputStream(PPConstants.AllAccountDetails,append)); 
       oos.writeObject(account); 
      } 
     } 
     finally 
     { 
      if(oos!=null) 
       oos.close(); 
      if(fos!=null) 
       fos.close(); 
     } 

    } 
+0

你可以发布异常跟踪吗?它也给出了什么是错误的明确线索... – SiB

+0

你是否正在写入其他任何东西到流?例如,使用writeObject()以外的任何方法? – EJP

+0

@EJP不,我没有写任何其他的'writeObject()'这是写入这些类中的任何一个的唯一方法。 – sasidhar

回答

11

这里的问题是,谁给你的追加ObjectOutputStream以前的海报带领你引入歧途。 ObjectOutputStream/ObjectInputStream会尝试仅存储一次每个对象,然后再返回已存储的对象。也就是说,在流中,你可以用这样的事情结束了,如果你有一堆相同的类的对象:

CLASS_1_DESCRIPTION 
OBJECT_1 
REF_TO_CLASS_1 
OBJECT_2 
REF_TO_CLASS_1 
OBJECT_3 
... 

ObjectInputStream被转换流回到了一堆的对象,它保持它已经反序列化的东西列表。它告诉你的错误是,它试图反序列化对象,读什么应该已经到对象的类的描述的引用,但是当它在其内部表中查找该引用它看到了String。很自然地,它爆炸了。

我认为解决方法是如此简单 - 在你AppendableObjectOutputStream,改变这个方法:

@Override 
    protected void writeStreamHeader() throws IOException { 
    // do not write a header, but reset the handle list 
    reset(); 
    } 

ObjectOutputStreamreset()方法插入标记到流说:“在这一点上扔掉所有国家”。然后,当你读这回与ObjectInputStream,一个什么样的反序列化输入流的想法将匹配什么样的输出流认为国家是当它摆在首位反序列化的东西。

(编辑:从意见回答问题)

,我能想到的这样做的唯一的有害后果:

  • 最终文件将长于它本来如果你愿意将所有内容全部写入一个ObjectOutputStream,特别是如果同一个Profile对象多次出现。即使不这样做,你也会在流中重复类描述符,所以很多重复{open AppendableObjectOutputStream,写一个对象,关闭流}可能会使文件大小膨胀一点。

  • 相关的,你的一切反序列化后,可能会用什么应该是同一对象的多个副本结束。例如,假设您编写了一堆东西,包括一些对象,然后关闭流,将其打开为AppendableObjectOutputStream,然后写出一个PPBusinessAccount,它的operators列表中包含您之前写出的某些PPRestrictedAccount。当您读完所有内容时,您最初阅读的PPRestrictedAccount将不会与您在PPBusinessAccountoperators列表中找到的PPRestrictedAccount相同的对象(即它们不会是==)。他们将分开实例化。为了避免这种情况,你需要用readResolve方法去除它们。然而,写入单个AppendableObjectOutputStream实例的所有内容都将正确连接。根据您的应用程序,这可能根本不需要担心。

在will-this-blow-up-or-safe安全性方面,这与其他任何使用java序列化一样安全;没有任何关于你的课程的具体内容可以使它工作。请注意,输出文件的多个独立开口中写入的任何对象将被反序列化为原始对象的单独副本。 (缺少任何readResolve魔法)

+0

'完美'在我花时间修复这个问题之后,解决方案就是一条线。谢谢。我明天会接受答案和赏金。我有兴趣知道,如果'在这一点上抛弃状态'是安全的或不安全的?该解决方案为我工作,很好。但将其应用于一般情况。它会使解决方案在所有情况下都安全吗? – sasidhar

+0

编辑主帖回答问题。 –

0

试试吧......

readObject()返回objects类型Object,所以你需要明确地扔在其原来的类型...

如:

PPAccount pa = (PPAccount) readObject();

+0

编辑我的问题,请参阅 – sasidhar

+0

@sasidhar尝试'PPAccount ppa =(PPAccount)ois.readObject();' –

+0

这是我的原始代码,澄清异常的地方,我改变它像这样,'readObject )'方法抛出错误。我不明白为什么? – sasidhar

0

没有明确回答你,但有些想法和事情要尝试:

  • 堆栈跟踪包括at java.util.ArrayList.readObject(Unknown Source),这表示在对包含ArrayList的类进行反序列化时发生问题。缩小您的问题下降注释掉private ArrayList<Transaction> transactions;

  • 你有同样的问题,如果你生成一个文件不使用你的appender?如果是这样,创建两种形式的相同的内容:一个与appender,一个没有。比较速度?

  • 我看到另外一个参照类似的问题,没有解决方案。还使用相同的appender:Serialization/deserialization ClassCastException: x cannot be cast to java.io.ObjectStreamClass

+0

在没有* appender的情况下使用*时我没有遇到任何问题。请注意,我也可以读取和写入流,但只能按特定顺序。如果要编写的第一个类是“PPBusinessAccount”,那么一切似乎都可以正常工作。如果ArrayList是罪魁祸首,那么它应该以不同的顺序失败吧?无论如何通过删除数组列表并将其设置为空来进行测试。结果没有什么不同。 – sasidhar

+0

好的,让我们暂时放下ArrayList以尽量减少噪音。你可以生成一个文件(a)“PBAccount,PBAccount,PBAccount,PBBusinessAccount”在每个对象之后使用appender,并且(b)根本不使用appender。在这两个文件上运行'diff'。 –

相关问题