附加对象OutputStream时的ClassCastException [英] ClassCastException when Appending Object OutputStream

查看:109
本文介绍了附加对象OutputStream时的ClassCastException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have been trying to do a little project that needs an appendable ObjectOutputStream. I have gone through a couple of solutions and i found this It seemed to solve my problem at first. But on further development of my project i started getting unexpected exceptions. The following are my classes.

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并在我附加对象时使用它到文件。但是如果我写的话会发生什么:

What i have observed is, using the this i have overridden the ObjectOutputStream and used it while i am appending the objects to the file. But what happens is if i write:

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

PPBusinessAccount first, repeat any number of times... then write PPAccount all is well. PPAccount first, repeat.... then write PPBusinessAccount then write PPAccount, it writes well but while reading i get a ClassCastException.

我试过读取Objects并将它们直接存储在 Object 类的实例中以避免类转换,但仍然 readObject() throws ClassCastException

I am tried reading the Objects and storing them directly in an instance of Object class to avoid the class cast but still readObject() throws ClassCastException.

我尽力描述我的情景,告诉你是否得到任何东西。为什么会这样?它是否与它第一次写的标题有关?沿着Base类头的行不能支持子类?轮到你了什么?

I tried best to describe my scenario, tell if you don't get anything. Why is this happening?? has it got something to do with the header that it is writing for this first time?? Along the lines of Base class header cannot support child class?? What's the turn around?

我正在做这样的演员:

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 写入流,这是代码:

The lookUpAccount reads from the stream while writeAccount writes to the stream, here is the code:

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 尝试仅存储每个对象一次,然后再返回已存储的对象。也就是说,如果你有一堆同一类的对象,你可以在流中得到类似的东西:

The problem here is that the previous poster who gave you an appendable ObjectOutputStream led you astray. An ObjectOutputStream/ObjectInputStream tries to store each object only once, and then later refer back to the object already stored. That is, in the stream you can end up with something like this if you have a bunch of objects of the same class:

CLASS_1_DESCRIPTION
OBJECT_1
REF_TO_CLASS_1
OBJECT_2
REF_TO_CLASS_1
OBJECT_3
...

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

When an ObjectInputStream is converting the stream back into a bunch of objects, it maintains a list of what things it's already deserialized. The error it's telling you is that it was trying to deserialize an object, read what should have been a reference to the description of the object's class, but when it looked up that reference in its internal table it saw a String. Quite naturally, it blew up.

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

I think the fix is as simple as this - in your AppendableObjectOutputStream, change this method:

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

reset() ObjectOutputStream 中的方法在流中插入一个标记,说此时丢弃所有状态。然后,当您使用 ObjectInputStream 读回来时,输入流对反序列化内容的想法将与输出流在反序列化时的状态相匹配。第一名。

the reset() method in ObjectOutputStream inserts a marker into the stream saying "throw away all the state at this point". Then, when you read this back in with a ObjectInputStream, the input stream's idea of what's been deserialized will match what the output stream thought the state was when it deserialized the stuff in the first place.

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

( Answer question from comments)

这是唯一有害的后果,我可以想到:

The only detrimental consequences of this that I can think of:


  • 如果你把所有东西都写成一个<$>那么最后的文件会比你原来的文件更长c $ c> ObjectOutputStream ,特别是如果多次出现相同的 Profile 对象。即使没有,你也会在流中重复类描述符,因此大量重复{open AppendableObjectOutputStream ,写一个对象,关闭流}可能会使文件大小膨胀一点。

  • The final file will be longer than it would have been if you'd written everything all to one ObjectOutputStream, especially if the same Profile object appears multiple times. Even if not, you'll repeat class descriptors in the stream, so lots of repetitions of {open AppendableObjectOutputStream, write one object, close stream} could bloat the file size a bit.

与此相关,在对所有内容进行反序列化后,您最终可能会获得应该是相同对象的多个副本。例如,假设您编写了大量内容,包括一些 PPRestrictedAccount 对象,然后关闭流,将其打开为 AppendableObjectOutputStream ,并写出一个 PPBusinessAccount ,其运算符列出了一些 PPRestrictedAccount 你先前写过的。当您阅读所有内容后,您最初阅读的 PPRestrictedAccount 将不会是相同的对象(也就是说,它们不会是 = = )到你在 PPBusinessAccount 的<$ c中找到的 PPRestrictedAccount $ c>运营商列表。它们将被单独实例化。为避免这种情况,您需要使用 重复删除它们。 readResolve 方法。但是,写入单个 AppendableObjectOutputStream 实例的所有内容都将正确连接。根据您的应用程序,这可能根本不用担心。

Related to that, after you deserialize everything you may end up with multiple copies of what should have been the identical object. For example, suppose you write a bunch of things including some PPRestrictedAccount objects, then close the stream, open it as an AppendableObjectOutputStream, and write out a PPBusinessAccount that has in its operators list some of the PPRestrictedAccounts you wrote out earlier. When you read all that back in, the PPRestrictedAccounts you read initially won't be the same objects (that is, they won't be ==) to the PPRestrictedAccounts that you find in the PPBusinessAccount's operators list. They'll be instantiated separately. To avoid this, you'd need to de-duplicate them with a readResolve method. Everything that was written to a single AppendableObjectOutputStream instance will be connected correctly however. Depending on your application, this might not be something to worry about at all.

就意志而言 - 这 - 爆破或不安全,这与任何其他使用java序列化一样安全;您的课程没有任何具体的内容可以使其发挥作用。请注意,输出文件的多个单独开口中写入的任何对象都将被反序列化为原始对象的单独副本。 (没有任何 readResolve 魔法)

In terms of will-this-blow-up-or-not safety, this is as safe as any other use of java serialization; there isn't anything specific about your classes that makes it work. Just be aware that any object written in multiple separate openings of the output file will be deserialized as separate copies of the original object. (Absent any readResolve magic)

这篇关于附加对象OutputStream时的ClassCastException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆