2012-09-05 27 views
5

Tôi đã cố gắng thực hiện một dự án nhỏ cần có một ObjectOutputStream bổ sung. Tôi đã trải qua một vài giải pháp và tôi đã tìm thấy this Dường như nó đã giải quyết được vấn đề của tôi lúc đầu. Nhưng về sự phát triển hơn nữa của dự án của tôi, tôi bắt đầu nhận được những ngoại lệ bất ngờ. Sau đây là các lớp học của tôi.ClassCastException khi thêm đối tượng OutputStream

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 
} 

gì tôi đã quan sát thấy là, bằng cách sử dụng this tôi đã ghi đè ObjectOutputStream và sử dụng nó trong khi tôi đang phụ thêm các đối tượng vào tập tin. Nhưng điều xảy ra là nếu tôi viết:

PPBusinessAccount trước tiên, lặp lại bất kỳ số lần nào ... sau đó viết PPAccount tất cả đều tốt. PPAccount trước tiên, lặp lại .... sau đó viết PPBusinessAccount rồi viết PPAccount, nó viết tốt nhưng trong khi đọc tôi nhận được một ClassCastException.

Tôi đã thử đọc các đối tượng và lưu trữ chúng trực tiếp trong một thể hiện của lớp Object để tránh lớp diễn viên nhưng vẫn còn readObject() ném ClassCastException.

Tôi đã cố gắng mô tả kịch bản của mình tốt nhất, cho biết bạn có nhận được gì không. Tại sao chuyện này đang xảy ra?? nó đã có một cái gì đó để làm với tiêu đề mà nó được viết cho lần đầu tiên này? Dọc theo dòng tiêu đề lớp cơ sở không thể hỗ trợ lớp con ?? Quay lại là gì?

tôi đang làm các diễn viên như thế này:

Object o = ois.readObject();  //Surprisingly exception is raised here (line:50 in DataStore) 
PPAccount ppa = (PPAccount)o; 

Các vết đống

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) 

Các lookUpAccount đọc từ dòng khi writeAccount ghi vào dòng, đây là mã:

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

    } 
+0

Bạn có thể đăng theo dõi ngoại lệ không? Nó cũng đưa ra một đầu mối rõ ràng về những gì là sai ... – SiB

+0

Bạn có viết gì khác vào luồng không? Ví dụ, với bất kỳ phương thức nào khác ngoài writeObject()? – EJP

+0

@EJP Không, tôi không viết gì khác cho luồng khác mà 'writeObject()' là phương thức duy nhất viết bất kỳ một trong các lớp này. – sasidhar

Trả lời

11

Vấn đề ở đây là các poster trước đó đã cho bạn một appendable ObjectOutputStream dẫn bạn lạc lối. An ObjectOutputStream/ObjectInputStream cố gắng lưu trữ từng đối tượng chỉ một lần và sau đó tham chiếu lại đối tượng đã được lưu trữ. Đó là, trong luồng mà bạn có thể kết thúc với một cái gì đó như thế này nếu bạn có một loạt các đối tượng của cùng một lớp:

CLASS_1_DESCRIPTION 
OBJECT_1 
REF_TO_CLASS_1 
OBJECT_2 
REF_TO_CLASS_1 
OBJECT_3 
... 

Khi một ObjectInputStream được chuyển đổi dòng trở lại vào một loạt các đối tượng, nó duy trì một danh sách những gì nó đã được deserialized. Lỗi nó nói với bạn là nó đang cố gắng deserialize một đối tượng, đọc những gì nên có được một tham chiếu đến mô tả của lớp học của đối tượng, nhưng khi nó nhìn lên tham chiếu trong bảng nội bộ của nó nó thấy một String. Khá tự nhiên, nó nổ tung.

Tôi nghĩ việc sửa chữa cũng đơn giản như thế này - trong AppendableObjectOutputStream của bạn, thay đổi phương pháp này:

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

các reset() phương pháp trong ObjectOutputStream chèn một dấu vào trong dòng nói "vứt bỏ tất cả các tiểu bang vào thời điểm này ". Sau đó, khi bạn đọc lại điều này bằng một ObjectInputStream, ý tưởng của luồng đầu vào về những gì được deserialized sẽ khớp với những gì luồng đầu ra nghĩ rằng nhà nước là khi nó deserialized các công cụ ở nơi đầu tiên.

(EDIT: trả lời câu hỏi từ comments)

Hậu quả bất lợi duy nhất của việc này mà tôi có thể nghĩ đến:

  • Các tập tin cuối cùng sẽ được lâu hơn nó đã có nếu bạn muốn viết tất cả mọi thứ cho một ObjectOutputStream, đặc biệt nếu cùng một đối tượng Profile xuất hiện nhiều lần. Thậm chí nếu không, bạn sẽ lặp lại các mô tả lớp trong luồng, vì vậy rất nhiều lần lặp lại {mở AppendableObjectOutputStream, viết một đối tượng, đóng luồng} có thể làm tăng kích thước tệp một chút.

  • Liên quan đến điều đó, sau khi bạn deserialize tất cả mọi thứ bạn có thể kết thúc với nhiều bản sao của những gì cần phải có được các đối tượng giống hệt nhau. Ví dụ: giả sử bạn viết một loạt các thứ bao gồm một số đối tượng PPRestrictedAccount, sau đó đóng luồng, mở nó dưới dạng AppendableObjectOutputStream và viết ra PPBusinessAccount có trong số operators liệt kê một số số PPRestrictedAccount mà bạn đã viết trước đó. Khi bạn đọc tất cả những điều đó trở lại, ban đầu bạn đọc PPRestrictedAccount sẽ không phải là các đối tượng giống nhau (nghĩa là chúng sẽ không là ==) với số PPRestrictedAccount mà bạn tìm thấy trong danh sách operators của . Chúng sẽ được khởi tạo riêng biệt. Để tránh điều này, bạn cần loại bỏ chúng bằng phương thức readResolve. Tất cả mọi thứ được ghi vào một cá thể AppendableObjectOutputStream đơn lẻ sẽ được kết nối đúng cách. Tùy thuộc vào ứng dụng của bạn, điều này có thể không phải là một cái gì đó để lo lắng về cả.

Về an toàn tuyệt đối, điều này cũng an toàn như bất kỳ việc sử dụng java serialization nào khác; không có bất cứ điều gì cụ thể về các lớp học của bạn mà làm cho nó hoạt động.Chỉ cần lưu ý rằng bất kỳ đối tượng nào được viết trong nhiều lỗ riêng biệt của tệp đầu ra sẽ được deserialized như các bản sao riêng biệt của đối tượng ban đầu. (Không có bất kỳ readResolve ma thuật)

+0

'Hoàn hảo' Sau tất cả thời gian tôi dành cho việc sửa lỗi này, giải pháp là một dòng. Cảm ơn. Tôi sẽ chấp nhận câu trả lời và tiền thưởng vào ngày mai. Tôi muốn biết, nếu 'vứt bỏ nhà nước vào thời điểm này' là an toàn hay không? Các giải pháp làm việc cho tôi, tốt. Nhưng áp dụng điều này cho một kịch bản trường hợp chung. Nó sẽ làm cho giải pháp an toàn trong mọi tình huống? – sasidhar

+0

Đã chỉnh sửa bài đăng chính để trả lời câu hỏi. –

0

Hãy thử như sau ....

readObject()trả vềobjects của loạiObject, vì vậy bạn cần phải rõ ràng quăng vào loại ban đầu của nó ...

Ví dụ:

PPAccount pa = (PPAccount) readObject();

+0

Đã chỉnh sửa câu hỏi của tôi, hãy xem – sasidhar

+0

@sasidhar thử 'PPAccount ppa = (PPAccount) ois.readObject(); ' –

+0

đó là mã ban đầu tôi có, để làm rõ nơi ngoại lệ, tôi thay đổi nó như thế này,' readObject () 'phương thức là ném lỗi. Tôi không thể hiểu tại sao? – sasidhar

0

Không rõ ràng trả lời cho bạn, nhưng một số suy nghĩ và những điều cần thử:

  • Theo dõi ngăn xếp bao gồm at java.util.ArrayList.readObject(Unknown Source), cho biết sự cố xảy ra trong khi deserializing một lớp học có chứa ArrayList. Thu hẹp vấn đề của bạn xuống bằng cách bình luận ra private ArrayList<Transaction> transactions;

  • Bạn có cùng một vấn đề nếu bạn tạo ra một tập tin duy nhất mà không sử dụng appender của bạn? Nếu vậy, hãy tạo hai dạng của cùng một nội dung: một với appender, một không có. Khác biệt?

  • Tôi thấy một tham chiếu khác đến một vấn đề tương tự, không có giải pháp. Cũng sử dụng appender cùng: Serialization/deserialization ClassCastException: x cannot be cast to java.io.ObjectStreamClass

+0

Tôi không gặp phải vấn đề gì khi sử dụng * mà không có * trình bổ sung. Và vui lòng lưu ý rằng tôi cũng có thể đọc và ghi vào luồng, nhưng chỉ theo thứ tự cụ thể. Nếu lớp đầu tiên được viết là của 'PPBusinessAccount' mọi thứ dường như hoạt động tốt. Nếu ArrayList là thủ phạm thì nó sẽ thất bại theo một thứ tự khác đúng không? Anyways kiểm tra bằng cách loại bỏ danh sách mảng và cũng bằng cách làm cho nó null. Kết quả không khác gì. – sasidhar

+0

Ok, vì vậy hãy để ArrayList ra ngoài để giảm thiểu tiếng ồn cho thời điểm này. Bạn có thể tạo ra một tập tin (a) "PBAccount, PBAccount, PBAccount, PBBusinessAccount" bằng cách sử dụng appender cho mỗi đối tượng sau khi đầu tiên, và (b) mà không cần sử dụng appender ở tất cả. Chạy 'diff' trên hai tập tin. –

Các vấn đề liên quan