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
Объект появляется несколько раз. Даже если нет, вы будете повторять дескрипторы классов в потоке, поэтому много повторений {openAppendableObjectOutputStream
, написать один объект, закрыть поток} может немного увеличить размер файла.В связи с этим после десериализации всего вы можете получить несколько копий того, что должно было быть идентичным объектом. Например, предположим, что вы пишете кучу вещей, включая некоторые
PPRestrictedAccount
объекты, затем закройте поток, откройте его какAppendableObjectOutputStream
и выписатьPPBusinessAccount
что имеет в своемoperators
перечислите некоторые изPPRestrictedAccount
Вы написали ранее. Когда вы читаете все это обратно,PPRestrictedAccount
Если вы читаете изначально, это не будут одни и те же объекты (то есть они не будут==
) кPPRestrictedAccount
с, что вы найдете вPPBusinessAccount
"soperators
список. Они будут созданы отдельно. Чтобы избежать этого, вам нужно будет дублировать их с помощью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