6

追加可能な 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();
        }

    }
4

3 に答える 3

13

ここでの問題は、追加可能なものを提供した以前の投稿者があなたObjectOutputStreamを迷わせたことです。ObjectOutputStream/は、ObjectInputStream各オブジェクトを 1 回だけ格納し、その後、既に格納されているオブジェクトを参照しようとします。つまり、ストリームでは、同じクラスのオブジェクトがたくさんある場合、次のような結果になる可能性があります。

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オブジェクトが複数回出現する場合に、すべてを 1 つにまとめた場合よりも長くなります。そうでない場合でも、ストリーム内でクラス記述子を繰り返すことになるため、{open AppendableObjectOutputStream, write one object, close stream} を何度も繰り返すと、ファイル サイズが少し肥大化する可能性があります。

  • それに関連して、すべてを逆シリアル化した後、同一のオブジェクトであるべきものの複数のコピーが作成される可能性があります。たとえば、いくつかのPPRestrictedAccountオブジェクトを含む一連のものを書き、ストリームを閉じて として開き、以前に書き出した の一部をリストに含むAppendableObjectOutputStreamを書き出すとします。すべてを読み返すと、最初に読み取った は、のリストにある と同じオブジェクトにはなりません (つまり、 にはなりません) 。それらは個別にインスタンス化されます。これを回避するには、メソッドを使用して重複を排除する必要があります。シングルに書かれたすべてPPBusinessAccountoperatorsPPRestrictedAccountPPRestrictedAccount==PPRestrictedAccountPPBusinessAccountoperatorsreadResolveAppendableObjectOutputStreamただし、インスタンスは正しく接続されます。アプリケーションによっては、これはまったく心配する必要がない場合があります。

will-this-blow-up-up-up-or-not の安全性に関しては、これは Java シリアライゼーションの他の使用法と同じくらい安全です。それを機能させるクラスに固有のものはありません。出力ファイルの複数の別々のオープニングで書き込まれたオブジェクトは、元のオブジェクトの別々のコピーとして逆シリアル化されることに注意してください。(readResolve魔法は使えない)

于 2012-09-15T14:19:32.457 に答える
0

あなたにとって明確な答えはありませんが、いくつかの考えや試してみてください:

  • スタック トレースには が含まれat java.util.ArrayList.readObject(Unknown Source)ています。これは、ArrayList. コメントアウトして問題を絞り込みますprivate ArrayList<Transaction> transactions;

  • アペンダーを使用せずに単一のファイルを生成すると、同じ問題が発生しますか? その場合、同じコンテンツの 2 つのフォームを作成します。1 つはアペンダーあり、もう 1 つはアペンダーなしです。差分?

  • 同様の問題への別の参照が表示されますが、解決策はありません。また、同じアペンダーを使用します: Serialization/deserialization ClassCastException: x cannot be cast to java.io.ObjectStreamClass

于 2012-09-14T06:11:20.233 に答える
0

このようにしてみてください....

readObject() objects型を 返すので、元の型に明示的にキャストするObject必要があります...

例えば:

PPAccount pa = (PPAccount) readObject();

于 2012-09-05T10:23:22.497 に答える