当前位置: 首页 > 面试题库 >

追加对象OutputStream时发生ClassCastException

佘修为
2023-03-14
问题内容

我一直在尝试做一个需要可附加ObjectOutputStream的小项目。我已经经历了几个解决方案,但我发现这似乎一开始就解决了我的问题。但是在我的项目的进一步开发中,我开始遇到意外的异常。以下是我的课程。

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
}

我观察到的是,使用此方法我重写了ObjectOutputStream并在将对象附加到文件时使用了它。但是会发生什么,如果我写:

PPBusinessAccount首先,重复任意次…然后写完PPAccount就好了。
PPAccount首先,重复…。然后写PPBusinessAccount然后写PPAccount,它写得很好,但是在阅读时我得到了一个ClassCastException

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

我尽力描述自己的情况,告诉你什么都没有。为什么会这样?它与它第一次编写的标头有关吗?沿着基类的头文件不能支持子类?怎么回事?

我在做这样的演员表:

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();
        }

    }

问题答案:

这里的问题是,以前给您提供附件的海报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();
  }

中的reset()方法在ObjectOutputStream流中插入一个标记,表示“此时放弃所有状态”。然后,当您使用读回来时ObjectInputStream,输入流对反序列化的想法将与输出流首先对东西进行反序列化时所认为的状态相匹配。

(编辑:从评论中回答问题)

我能想到的唯一有害后果是:

  • 如果将所有内容全部写入一个文件,则最终文件将比原文件更长ObjectOutputStream,尤其是同一Profile对象出现多次时。即使没有,您也将在流中重复类描述符,因此{open AppendableObjectOutputStream,编写一个对象,close stream}的许多重复操作可能会使文件大小变大。

  • 与此相关的是,在对所有内容进行反序列化之后,您可能最终会获得原本应该是同一对象的多个副本。例如,假设您编写了一堆东西,包括一些PPRestrictedAccount对象,然后关闭流,将其作为打开AppendableObjectOutputStream,并写出一个a PPBusinessAccount,该operators列表中包含PPRestrictedAccount您先前写的一些s。当你阅读所有回的PPRestrictedAccount就是你读最初不会是相同的对象(也就是说,他们不会==)的PPRestrictedAccount是你们等在找到PPBusinessAccountoperators名单。它们将分别实例化。为避免这种情况,您需要使用readResolve方法对它们进行重复数据删除。一切都写到一个AppendableObjectOutputStream实例将正确连接。根据您的应用程序,这可能根本不用担心。

就遗嘱的安全性而言,这与java序列化的任何其他用法一样安全。没有任何关于您的类的具体信息可以使其正常工作。请注意,在输出文件的多个单独的开口中编写的任何对象都将反序列化为原始对象的单独副本。(没有任何readResolve魔法)



 类似资料:
  • 问题内容: 我正在研究一个第三方开发人员用来为我们的核心应用程序编写扩展的Python库。 我想知道引发异常时是否可以修改回溯,因此最后一个堆栈帧是对开发人员代码中库函数的调用,而不是对引发异常的库中的行的调用。堆栈底部还有一些框架,其中包含对第一次加载我理想上也希望删除的代码时使用的函数的引用。 在此先感谢您的任何建议! 问题答案: 不更改回溯怎么办?您要求的两件事都可以通过不同的方式轻松完成。

  • 问题内容: 假设您有一些AppendObjectOutputStream类(这是一个ObjectOutputStream!),它重写了writeStreamHeader(),如下所示: 现在,假设您打算将多个对象保存到一个文件中。程序每次运行时,一个对象。即使在第一次运行时,您是否也会使用AppendObjectOutputStream()? 问题答案: 您必须首先使用常规ObjectOutput

  • 假设您有一些AppendObjectOutputStream类(这是一个ObjectOutputStream!)将重写writeStreamHeader(),如下所示: 现在,假设您计划将多个对象保存到一个文件中;每次程序运行一个对象。即使在第一次运行时,您也会使用AppendObjectOutputStream()吗?

  • 我的中有一个datetime文件,另一个字段也保存了选定的时区。我需要将该时区的信息附加到我的datetime对象,如下所示:我该如何做?

  • 我将下面的jsonb数据存储在如下的表列中: 我需要向json数组中的所有对象添加一个元素“License”:“Property”,如下所示: 我在这里添加了sql小提琴链接:https://dbfiddle.uk/?rdbms=postgres_11 提前谢谢。

  • 问题内容: 我的老师给我一个问题: “用Java创建对象时会发生什么”。 据我所知,创建对象时会发生内存分配,变量初始化和构造函数方法调用。 但是我的老师说我几乎是对的。后面的两件事是正确的,除了内存堆。相反,他说发生了内存分配。我认为对象存储在堆中,所以我的老师错了。你这样认为吗? 问题答案: 与往常一样,找到针对此类问题的解决方案的最佳位置是Java语言规范。 具体来说,从创建新实例的部分可以