附加对象OutputStream时的ClassCastException

sas*_*har 6 java serialization classcastexception java-io

我一直在尝试做一个需要可附加的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
}
Run Code Online (Sandbox Code Playgroud)

我观察到的是,使用这个我已经覆盖了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;
Run Code Online (Sandbox Code Playgroud)

堆栈跟踪

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)
Run Code Online (Sandbox Code Playgroud)

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

    }
Run Code Online (Sandbox Code Playgroud)

Dan*_*tin 12

这里的问题是前一张给你一个可附加物的海报让ObjectOutputStream你误入歧途.一个ObjectOutputStream/ ObjectInputStream试图存储每个对象只有一次,再后来重提已存储的对象.也就是说,如果你有一堆同一类的对象,你可以在流中得到这样的东西:

CLASS_1_DESCRIPTION
OBJECT_1
REF_TO_CLASS_1
OBJECT_2
REF_TO_CLASS_1
OBJECT_3
...
Run Code Online (Sandbox Code Playgroud)

当a ObjectInputStream将流转换回一堆对象时,它会维护一个已经反序列化的内容列表.它告诉你的错误是它试图反序列化一个对象,读取应该是对象类的描述的引用,但当它在其内部表中查找该引用时,它看到了一个String.很自然,它爆炸了.

我认为修复就像这样简单 - 在你的AppendableObjectOutputStream,改变这个方法:

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

将标记插入流中的reset()方法是ObjectOutputStream"在此时丢弃所有状态".然后,当你用a读回来时ObjectInputStream,输入流对已反序列化的想法将与输出流在首先对其进行反序列化时的状态进行匹配.

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

我能想到的唯一不利后果:

  • 如果您将所有内容全部写入一个文件,则最终文件将比以前更长ObjectOutputStream,特别是如果同一Profile对象多次出现.即使没有,你也会在流中重复类描述符,因此大量重复{open AppendableObjectOutputStream,write one object,close stream}可能会使文件大小膨胀一些.

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

就意志而言 - 这种 - 或非 - 安全性,这与任何其他使用java序列化一样安全; 没有任何关于你的课程的具体信息.请注意,输出文件的多个单独开口中写入的任何对象都将被反序列化为原始对象的单独副本.(没有任何readResolve魔法)