<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ## Chapter 12. Serialization(序列化) ### Item 90: Consider serialization proxies instead of serialized instances(考慮以序列化代理代替序列化實例) As mentioned in Items 85 and 86 and discussed throughout this chapter, the decision to implement Serializable increases the likelihood of bugs and security problems as it allows instances to be created using an extralinguistic mechanism in place of ordinary constructors. There is, however, a technique that greatly reduces these risks. This technique is known as the serialization proxy pattern. 正如在 [Item-85](/Chapter-12/Chapter-12-Item-85-Prefer-alternatives-to-Java-serialization.md) 和 [Item-86](/Chapter-12/Chapter-12-Item-86-Implement-Serializable-with-great-caution.md) 中提到的貫穿本章的問題:實現 Serializable 接口的決定增加了出現 bug 和安全問題的可能性,因為它允許使用一種超語言機制來創建實例,而不是使用普通的構造函數。然而,有一種技術可以大大降低這些風險。這種技術稱為序列化代理模式。 The serialization proxy pattern is reasonably straightforward. First, design a private static nested class that concisely represents the logical state of an instance of the enclosing class. This nested class is known as the serialization proxy of the enclosing class. It should have a single constructor, whose parameter type is the enclosing class. This constructor merely copies the data from its argument: it need not do any consistency checking or defensive copying. By design, the default serialized form of the serialization proxy is the perfect serialized form of the enclosing class. Both the enclosing class and its serialization proxy must be declared to implement Serializable. 序列化代理模式相當簡單。首先,設計一個私有靜態嵌套類,它簡潔地表示外圍類實例的邏輯狀態。這個嵌套類稱為外圍類的序列化代理。它應該有一個構造函數,其參數類型是外圍類。這個構造函數只是從它的參數復制數據:它不需要做任何一致性檢查或防御性復制。按照設計,序列化代理的默認序列化形式是外圍類的完美序列化形式。外圍類及其序列代理都必須聲明實現 Serializable 接口。 For example, consider the immutable Period class written in Item 50 and made serializable in Item 88. Here is a serialization proxy for this class. Period is so simple that its serialization proxy has exactly the same fields as the class: 例如,考慮 [Item-50](/Chapter-8/Chapter-8-Item-50-Make-defensive-copies-when-needed.md) 中編寫的不可變 Period 類,并在 [Item-88](/Chapter-12/Chapter-12-Item-88-Write-readObject-methods-defensively.md) 中使其可序列化。這是該類的序列化代理。Period 非常簡單,它的序列化代理具有與類完全相同的字段: ``` // Serialization proxy for Period class private static class SerializationProxy implements Serializable { private final Date start; private final Date end; SerializationProxy(Period p) { this.start = p.start; this.end = p.end; } private static final long serialVersionUID =234098243823485285L; // Any number will do (Item 87) } ``` Next, add the following writeReplace method to the enclosing class. This method can be copied verbatim into any class with a serialization proxy: 接下來,將以下 writeReplace 方法添加到外圍類中。通過序列化代理,這個方法可以被逐字地復制到任何類中: ``` // writeReplace method for the serialization proxy pattern private Object writeReplace() { return new SerializationProxy(this); } ``` The presence of this method on the enclosing class causes the serialization system to emit a SerializationProxy instance instead of an instance of the enclosing class. In other words, the writeReplace method translates an instance of the enclosing class to its serialization proxy prior to serialization. 該方法存在于外圍類,導致序列化系統產生 SerializationProxy 實例,而不是外圍類的實例。換句話說,writeReplace 方法在序列化之前將外圍類的實例轉換為其序列化代理。 With this writeReplace method in place, the serialization system will never generate a serialized instance of the enclosing class, but an attacker might fabricate one in an attempt to violate the class’s invariants. To guarantee that such an attack would fail, merely add this readObject method to the enclosing class: 有了這個 writeReplace 方法,序列化系統將永遠不會生成外圍類的序列化實例,但是攻擊者可能會創建一個實例,試圖違反類的不變性。為了保證這樣的攻擊會失敗,只需將這個 readObject 方法添加到外圍類中: ``` // readObject method for the serialization proxy pattern private void readObject(ObjectInputStream stream) throws InvalidObjectException { throw new InvalidObjectException("Proxy required"); } ``` Finally, provide a readResolve method on the SerializationProxy class that returns a logically equivalent instance of the enclosing class. The presence of this method causes the serialization system to translate the serialization proxy back into an instance of the enclosing class upon deserialization. 最后,在 SerializationProxy 類上提供一個 readResolve 方法,該方法返回外圍類的邏輯等效實例。此方法的存在導致序列化系統在反序列化時將序列化代理轉換回外圍類的實例。 This readResolve method creates an instance of the enclosing class using only its public API and therein lies the beauty of the pattern. It largely eliminates the extralinguistic character of serialization, because the deserialized instance is created using the same constructors, static factories, and methods as any other instance. This frees you from having to separately ensure that deserialized instances obey the class’s invariants. If the class’s static factories or constructors establish these invariants and its instance methods maintain them, you’ve ensured that the invariants will be maintained by serialization as well. 這個 readResolve 方法僅使用其公共 API 創建了一個外圍類的實例,這就是該模式的美妙之處。它在很大程度上消除了序列化的語言外特性,因為反序列化實例是使用與任何其他實例相同的構造函數、靜態工廠和方法創建的。這使你不必單獨確保反序列化的實例遵從類的不變性。如果類的靜態工廠或構造函數建立了這些不變性,而它的實例方法維護它們,那么你就確保了這些不變性也將通過序列化來維護。 Here is the readResolve method for Period.SerializationProxy above: 以下是上述 `Period.SerializationProxy` 的 readResolve 方法: ``` // readResolve method for Period.SerializationProxy private Object readResolve() { return new Period(start, end); // Uses public constructor } ``` Like the defensive copying approach (page 357), the serialization proxy approach stops the bogus byte-stream attack (page 354) and the internal field theft attack (page 356) dead in their tracks. Unlike the two previous approaches, this one allows the fields of Period to be final, which is required in order for the Period class to be truly immutable (Item 17). And unlike the two previous approaches, this one doesn’t involve a great deal of thought. You don’t have to figure out which fields might be compromised by devious serialization attacks, nor do you have to explicitly perform validity checking as part of deserialization. 與防御性復制方法(第 357 頁)類似,序列化代理方法阻止偽字節流攻擊(第 354 頁)和內部字段盜竊攻擊(第 356 頁)。與前兩種方法不同,這種方法允許 Period 的字段為 final,這是 Period 類真正不可變所必需的([Item-17](/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md))。與前兩種方法不同,這一種方法不需要太多的思考。你不必指出哪些字段可能會受到狡猾的序列化攻擊的危害,也不必顯式地執行有效性檢查作為反序列化的一部分。 There is another way in which the serialization proxy pattern is more powerful than defensive copying in readObject. The serialization proxy pattern allows the deserialized instance to have a different class from the originally serialized instance. You might not think that this would be useful in practice, but it is. 序列化代理模式還有另一種方式比 readObject 中的防御性復制更強大。序列化代理模式允許反序列化實例具有與初始序列化實例不同的類。你可能不認為這在實踐中有用,但它確實有用。 Consider the case of EnumSet (Item 36). This class has no public constructors, only static factories. From the client’s perspective, they return EnumSet instances, but in the current OpenJDK implementation, they return one of two subclasses, depending on the size of the underlying enum type. If the underlying enum type has sixty-four or fewer elements, the static factories return a RegularEnumSet; otherwise, they return a JumboEnumSet. 考慮 EnumSet 的情況([Item-36](/Chapter-6/Chapter-6-Item-36-Use-EnumSet-instead-of-bit-fields.md))。該類沒有公共構造函數,只有靜態工廠。從客戶端的角度來看,它們返回 EnumSet 實例,但是在當前 OpenJDK 實現中,它們返回兩個子類中的一個,具體取決于底層枚舉類型的大小。如果底層枚舉類型有 64 個或更少的元素,則靜態工廠返回一個 RegularEnumSet;否則,它們返回一個 JumboEnumSet。 Now consider what happens if you serialize an enum set whose enum type has sixty elements, then add five more elements to the enum type, and then deserialize the enum set. It was a RegularEnumSet instance when it was serialized, but it had better be a JumboEnumSet instance once it is deserialized. In fact that’s exactly what happens, because EnumSet uses the serialization proxy pattern. In case you’re curious, here is EnumSet’s serialization proxy. It really is this simple: 現在考慮,如果序列化一個枚舉集合,它的枚舉類型有 60 個元素,然后給這個枚舉類型再增加 5 個元素,之后反序列化這個枚舉集合。當它被序列化的時候,返回 RegularEnumSet 實例,但最好是 JumboEnumSet 實例。事實上正是這樣,因為 EnumSet 使用序列化代理模式。如果你好奇,這里是 EnumSet 的序列化代理。其實很簡單: ``` // EnumSet's serialization proxy private static class SerializationProxy <E extends Enum<E>> implements Serializable { // The element type of this enum set. private final Class<E> elementType; // The elements contained in this enum set. private final Enum<?>[] elements; SerializationProxy(EnumSet<E> set) { elementType = set.elementType; elements = set.toArray(new Enum<?>[0]); } private Object readResolve() { EnumSet<E> result = EnumSet.noneOf(elementType); for (Enum<?> e : elements) result.add((E)e); return result; } private static final long serialVersionUID =362491234563181265L; } ``` The serialization proxy pattern has two limitations. It is not compatible with classes that are extendable by their users (Item 19). Also, it is not compatible with some classes whose object graphs contain circularities: if you attempt to invoke a method on such an object from within its serialization proxy’s readResolve method, you’ll get a ClassCastException because you don’t have the object yet, only its serialization proxy. 序列化代理模式有兩個限制。它與客戶端可擴展的類不兼容([Item-19](/Chapter-4/Chapter-4-Item-19-Design-and-document-for-inheritance-or-else-prohibit-it.md))。而且,它也不能與對象圖中包含循環的某些類兼容:如果你試圖從對象的序列化代理的 readResolve 方法中調用對象上的方法,你將得到一個 ClassCastException,因為你還沒有對象,只有對象的序列化代理。 Finally, the added power and safety of the serialization proxy pattern are not free. On my machine, it is 14 percent more expensive to serialize and deserialize Period instances with serialization proxies than it is with defensive copying. 最后,序列化代理模式所增強的功能和安全性并不是沒有代價的。在我的機器上,通過序列化代理來序列化和反序列化 Period 實例的開銷,比用保護性拷貝進行的開銷增加了14%。 In summary, consider the serialization proxy pattern whenever you find yourself having to write a readObject or writeObject method on a class that is not extendable by its clients. This pattern is perhaps the easiest way to robustly serialize objects with nontrivial invariants. 總之,當你發現必須在客戶端不可擴展的類上編寫 readObject 或 writeObject 方法時,請考慮序列化代理模式。要想穩健地將帶有重要約束條件的對象序列化時,這種模式可能是最容易的方法。 --- **[Back to contents of the chapter(返回章節目錄)](/Chapter-12/Chapter-12-Introduction.md)** - **Previous Item(上一條目):[Item 89: For instance control prefer enum types to readResolve(對于實例控制,枚舉類型優于 readResolve)](/Chapter-12/Chapter-12-Item-89-For-instance-control-prefer-enum-types-to-readResolve.md)**
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看