ClassCastException при добавлении объекта OutputStream

Я пытался сделать небольшой проект, который нуждается в добавляемом 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();
        }

    }

3 ответа

Решение

Проблема здесь в том, что предыдущий постер, который дал вам доступное 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, написать один объект, закрыть поток} может немного увеличить размер файла.

  • В связи с этим после десериализации всего вы можете получить несколько копий того, что должно было быть идентичным объектом. Например, предположим, что вы пишете кучу вещей, включая некоторые PPRestrictedAccount объекты, затем закройте поток, откройте его как AppendableObjectOutputStream и выписать PPBusinessAccount что имеет в своем operators перечислите некоторые из PPRestrictedAccount Вы написали ранее. Когда вы читаете все это обратно, PPRestrictedAccount Если вы читаете изначально, это не будут одни и те же объекты (то есть они не будут ==) к PPRestrictedAccount с, что вы найдете в PPBusinessAccount "s operators список. Они будут созданы отдельно. Чтобы избежать этого, вам нужно будет дублировать их с помощью readResolve метод. Все, что было написано в один AppendableObjectOutputStream Экземпляр будет подключен правильно, однако. В зависимости от вашего приложения, это может вообще не беспокоить.

С точки зрения безопасности "будь это взорван или нет", это так же безопасно, как и любое другое использование сериализации Java; в ваших классах нет ничего конкретного, что могло бы заставить его работать. Просто имейте в виду, что любой объект, записанный в нескольких отдельных открытиях выходного файла, будет десериализован как отдельные копии исходного объекта. (Отсутствует readResolve магия)

Попробуйте это так....

readObject() возвращает objects типа Objectтак что вам нужно явно привести их в исходные типы...

Например:

PPAccount pa = (PPAccount) readObject();

Нет четкого ответа для вас, но некоторые мысли и вещи, чтобы попробовать:

  • Трассировка стека включает at java.util.ArrayList.readObject(Unknown Source), который указывает, что проблема возникает при десериализации класса, содержащего ArrayList, Сузьте проблему, комментируя private ArrayList<Transaction> transactions;

  • У вас есть такая же проблема, если вы генерируете один файл без использования вашего appender? Если это так, создайте две формы одного и того же контента: один с appender, другой без. Diffs?

  • Я вижу еще одну ссылку на подобную проблему, но нет решения. Также использует тот же appender: Сериализация / десериализация ClassCastException: x не может быть приведен к java.io.ObjectStreamClass

Другие вопросы по тегам