搜尋

首頁  >  問答  >  主體

java - 為什麼Serializable中定義的Class 不能序列化?

Serialized 類別中的

Fields 本身必須是可序列化的或瞬態的,即使該類別從未明確序列化或反序列化。這是因為在負載下,大多數J2EE 應用程式框架都會將物件刷新到磁碟,而具有非瞬態、不可序列化資料成員的所謂可序列化物件可能會導致程式崩潰,並為攻擊者打開大門。

此規則在非可序列化欄位以及非私有欄位(因為可以在外部為它們分配非序列化值)以及在類別內為它們分配非序列化類型時引發集合欄位問題。

不合規程式碼範例

雷雷
欧阳克欧阳克2728 天前1281

全部回覆(2)我來回復

  • 扔个三星炸死你

    扔个三星炸死你2017-06-30 09:58:37

    一個物件序列化時,依照Java預設的序列化規則,物件內的所有成員都要序列化,也就是說,這些Class都必須實作Serializable。

    所以,你有兩種改法,一是Address實作Serializable接口,二是對Person中的address成員加上transient標記,這樣該成員就不會被序列化進去。

    回覆
    0
  • typecho

    typecho2017-06-30 09:58:37

    如果 address 成員需要進行序列化的話,則Address類別也需要實作Serializable介面。
    如果 address 成員不需要進行序列化的話,可以加上transient關鍵字,則address成員不做序列化操作,值為null。如下:

    public class Person implements Serializable {
      private static final long serialVersionUID = 1905122041950251207L;
    
      private String name;
      private transient Address address;  // Noncompliant; Address isn't serializable
    }

    當然還有其他方式:
    例如實作Externalizable接口,重寫readExternal(ObjectInput in)和writeExternal(ObjectOutput out)方法。
    還有一個替代實作Externalizable介面方法,還是實作Serializable接口,加入writeObject(ObjectOutputStream obs)和readObject(ObjectInputStream ois)方法。


    再說說為什麼Address一定要實作Serializable,或是加上transient關鍵字Person才能進行序列化?
    先看看不做處理,使用 ObjectOutputStream 來持久化對象,拋出的異常

    Exception in thread "main" java.io.NotSerializableException

    ObjectOutputStream源碼:

    /**
         * Underlying writeObject/writeUnshared implementation.
         */
        private void writeObject0(Object obj, boolean unshared)
            throws IOException
        {
                //......
                // remaining cases
                if (obj instanceof String) {
                    writeString((String) obj, unshared);
                } else if (cl.isArray()) {
                    writeArray(obj, desc, unshared);
                } else if (obj instanceof Enum) {
                    writeEnum((Enum<?>) obj, desc, unshared);
                } else if (obj instanceof Serializable) {
                    writeOrdinaryObject(obj, desc, unshared);
                } else {
                    if (extendedDebugInfo) {
                        throw new NotSerializableException(
                            cl.getName() + "\n" + debugInfoStack.toString());
                    } else {
                        throw new NotSerializableException(cl.getName());
                    }
                }
            } finally {
                depth--;
                bout.setBlockDataMode(oldMode);
            }
        }

    從此可知, 如果被寫入物件型別是String、Array、Enum、Serializable,就可以進行序列化,否則會拋出NotSerializableException。且在序列化物件時,不僅會序列化目前物件本身,還會對該物件引用的其它物件也進行序列化。

    回覆
    0
  • 取消回覆