<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>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                ## Chapter 8. Methods(方法) ### Item 50: Make defensive copies when needed(在需要時制作防御性副本) One thing that makes Java a pleasure to use is that it is a safe language. This means that in the absence of native methods it is immune to buffer overruns, array overruns, wild pointers, and other memory corruption errors that plague unsafe languages such as C and C++. In a safe language, it is possible to write classes and to know with certainty that their invariants will hold, no matter what happens in any other part of the system. This is not possible in languages that treat all of memory as one giant array. Java 是一種安全的語言,這是它的一大優點。這意味著在沒有本地方法的情況下,它不受緩沖區溢出、數組溢出、非法指針和其他內存損壞錯誤的影響,這些錯誤困擾著 C 和 c++ 等不安全語言。在一種安全的語言中,可以編寫一個類并確定它們的不變量將保持不變,而不管在系統的任何其他部分發生了什么。在將所有內存視為一個巨大數組的語言中,這是不可能的。 Even in a safe language, you aren’t insulated from other classes without some effort on your part. **You must program defensively, with the assumption that clients of your class will do their best to destroy its invariants.** This is increasingly true as people try harder to break the security of systems, but more commonly, your class will have to cope with unexpected behavior resulting from the honest mistakes of well-intentioned programmers. Either way, it is worth taking the time to write classes that are robust in the face of ill-behaved clients. 即使使用一種安全的語言,如果你不付出一些努力,也無法與其他類隔離。**你必須進行防御性的設計,并假定你的類的客戶端會盡最大努力破壞它的不變量。** 隨著人們越來越多地嘗試破壞系統的安全性,這個觀點越來越正確,但更常見的情況是,你的類將不得不處理程序員的無意錯誤所導致的意外行為。無論哪種方式,都值得花時間編寫一個健壯的類來面對行為不軌的客戶端。 While it is impossible for another class to modify an object’s internal state without some assistance from the object, it is surprisingly easy to provide such assistance without meaning to do so. For example, consider the following class, which purports to represent an immutable time period: 雖然如果沒有對象的幫助,另一個類是不可能修改對象的內部狀態的,但是要提供這樣的幫助卻出奇地容易。例如,考慮下面的類,它表示一個不可變的時間段: ``` // Broken "immutable" time period class public final class Period { private final Date start; private final Date end; /** * @param start the beginning of the period * @param end the end of the period; must not precede start * @throws IllegalArgumentException if start is after end * @throws NullPointerException if start or end is null */ public Period(Date start, Date end) { if (start.compareTo(end) > 0) throw new IllegalArgumentException(start + " after " + end); this.start = start; this.end = end; } public Date start() { return start; } public Date end() { return end; } ... // Remainder omitted } ``` At first glance, this class may appear to be immutable and to enforce the invariant that the start of a period does not follow its end. It is, however, easy to violate this invariant by exploiting the fact that Date is mutable: 乍一看,這個類似乎是不可變的,并且要求一個時間段的開始時間不能在結束時間之后。然而,利用 Date 是可變的這一事實很容易繞過這個約束: ``` // Attack the internals of a Period instance Date start = new Date(); Date end = new Date(); Period p = new Period(start, end); end.setYear(78); // Modifies internals of p! ``` As of Java 8, the obvious way to fix this problem is to use Instant (or Local-DateTime or ZonedDateTime) in place of a Date because Instant (and the other java.time classes) are immutable (Item 17). **Date is obsolete and should no longer be used in new code.** That said, the problem still exists: there are times when you’ll have to use mutable value types in your APIs and internal representations, and the techniques discussed in this item are appropriate for those times. 從 Java 8 開始,解決這個問題的典型方法就是使用 Instant(或 Local-DateTime 或 ZonedDateTime)來代替 Date,因為 Instant(和其他時間類)類是不可變的([Item-17](/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md))。**Date 已過時,不應在新代碼中使用。** 盡管如此,問題仍然存在:有時你必須在 API 和內部表示中使用可變值類型,本項目中討論的技術適用于這些情形。 To protect the internals of a Period instance from this sort of attack, **it is essential to make a defensive copy of each mutable parameter to the constructor** and to use the copies as components of the Period instance in place of the originals: 為了保護 Period 實例的內部不受此類攻擊,**必須將每個可變參數的防御性副本復制給構造函數**,并將副本用作 Period 實例的組件,而不是原始組件: ``` // Repaired constructor - makes defensive copies of parameters public Period(Date start, Date end) { this.start = new Date(start.getTime()); this.end = new Date(end.getTime()); if (this.start.compareTo(this.end) > 0) throw new IllegalArgumentException(this.start + " after " + this.end); } ``` With the new constructor in place, the previous attack will have no effect on the Period instance. Note that **defensive copies are made before checking the validity of the parameters (Item 49), and the validity check is performed on the copies rather than on the originals.** While this may seem unnatural, it is necessary. It protects the class against changes to the parameters from another thread during the window of vulnerability between the time the parameters are checked and the time they are copied. In the computer security community, this is known as a time-of-check/time-of-use or TOCTOU attack [Viega01]. 有了新的構造函數,之前的攻擊將不會對 Period 實例產生影響。注意,**防御性副本是在檢查參數的有效性之前制作的([Item-49](/Chapter-8/Chapter-8-Item-49-Check-parameters-for-validity.md)),有效性檢查是在副本上而不是在正本上執行的。** 雖然這看起來不自然,但卻是必要的。在檢查參數和復制參數之間的空窗期,它保護類不受來自其他線程的參數更改的影響。在計算機安全社區里,這被稱為 time-of-check/time-of-use 或 TOCTOU 攻擊 [Viega01]。 Note also that we did not use Date’s clone method to make the defensive copies. Because Date is nonfinal, the clone method is not guaranteed to return an object whose class is java.util.Date: it could return an instance of an untrusted subclass that is specifically designed for malicious mischief. Such a subclass could, for example, record a reference to each instance in a private static list at the time of its creation and allow the attacker to access this list. This would give the attacker free rein over all instances. To prevent this sort of attack, **do not use the clone method to make a defensive copy of a parameter whose type is subclassable by untrusted parties.** 還要注意,我們沒有使用 Date 的 clone 方法來創建防御性副本。因為 Date 不是 final 的,所以不能保證 clone 方法返回一個 java.util.Date 的實例對象:它可以返回一個不受信任子類的實例,這個子類是專門為惡意破壞而設計的。例如,這樣的子類可以在創建時在私有靜態列表中記錄對每個實例的引用,并允許攻擊者訪問這個列表。這將使攻擊者可以自由控制所有實例。為防止此類攻擊,**對可被不受信任方子類化的參數類型,不要使用 clone 方法進行防御性復制。** While the replacement constructor successfully defends against the previous attack, it is still possible to mutate a Period instance, because its accessors offer access to its mutable internals: 雖然替換構造函數成功地防御了之前的攻擊,但是仍然可以修改 Period 實例,因為它的訪問器提供了對其可變內部結構的訪問: ``` // Second attack on the internals of a Period instance Date start = new Date(); Date end = new Date(); Period p = new Period(start, end); p.end().setYear(78); // Modifies internals of p! ``` To defend against the second attack, merely modify the accessors to return defensive copies of mutable internal fields: 要防御第二次攻擊,只需修改訪問器,返回可變內部字段的防御副本: ``` // Repaired accessors - make defensive copies of internal fields public Date start() { return new Date(start.getTime()); } public Date end() { return new Date(end.getTime()); } ``` With the new constructor and the new accessors in place, Period is truly immutable. No matter how malicious or incompetent a programmer, there is simply no way to violate the invariant that the start of a period does not follow its end (without resorting to extralinguistic means such as native methods and reflection). This is true because there is no way for any class other than Period itself to gain access to either of the mutable fields in a Period instance. These fields are truly encapsulated within the object. 有了新的構造函數和新的訪問器,Period 實際上是不可變的。無論程序員多么惡毒或無能,都不可能違背一個時間段的開始時間不能在結束時間之后這一不變條件(除非使用諸如本地方法和反射之類的外部語言手段)。這是真的,因為除了 Period 本身之外,任何類都無法訪問 Period 實例中的任何可變字段。這些字段真正封裝在對象中。 In the accessors, unlike the constructor, it would be permissible to use the clone method to make the defensive copies. This is so because we know that the class of Period’s internal Date objects is java.util.Date, and not some untrusted subclass. That said, you are generally better off using a constructor or static factory to copy an instance, for reasons outlined in Item 13. 在訪問器中,與構造函數不同,可以使用 clone 方法進行防御性復制。這是因為我們知道 Period 的內部 Date 對象的類是 `java.util.Date`,而不是某個不可信的子類。也就是說,基于 [Item-13](/Chapter-3/Chapter-3-Item-13-Override-clone-judiciously.md) 中列出的原因,一般情況下,最好使用構造函數或靜態工廠來復制實例。 Defensive copying of parameters is not just for immutable classes. Any time you write a method or constructor that stores a reference to a client-provided object in an internal data structure, think about whether the client-provided object is potentially mutable. If it is, think about whether your class could tolerate a change in the object after it was entered into the data structure. If the answer is no, you must defensively copy the object and enter the copy into the data structure in place of the original. For example, if you are considering using a client-provided object reference as an element in an internal Set instance or as a key in an internal Map instance, you should be aware that the invariants of the set or map would be corrupted if the object were modified after it is inserted. 參數的防御性復制不僅適用于不可變類。在編寫方法或構造函數時,如果要在內部數據結構中存儲對客戶端提供的對象的引用,請考慮客戶端提供的對象是否可能是可變的。如果是,請考慮該對象進入數據結構之后,你的類是否能夠容忍該對象發生更改。如果答案是否定的,則必須防御性地復制對象,并將副本輸入到數據結構中,而不是原始正本。舉個例子,如果你正在考慮使用由客戶提供的對象引用作為內部 Set 實例的元素,或者作為內部 Map 實例的鍵, 就應該意識到如果這個對象在插入之后發生改變,Set 或者 Map 的約束條件就會遭到破壞。 The same is true for defensive copying of internal components prior to returning them to clients. Whether or not your class is immutable, you should think twice before returning a reference to an internal component that is mutable. Chances are, you should return a defensive copy. Remember that nonzero-length arrays are always mutable. Therefore, you should always make a defensive copy of an internal array before returning it to a client. Alternatively, you could return an immutable view of the array. Both of these techniques are shown in Item 15. 在將內部組件返回給客戶端之前應對其進行防御性復制也是如此。無論你的類是否是不可變的,在返回對可變內部組件的引用之前,你都應該三思。很有可能,你應該返回一個防御性副本。記住,非零長度數組總是可變的。因此,在將內部數組返回給客戶端之前,應該始終創建一個防御性的副本。或者,你可以返回數組的不可變視圖。這兩種技術都已經在 [Item-15](/Chapter-4/Chapter-4-Item-15-Minimize-the-accessibility-of-classes-and-members.md) 中演示過。 Arguably, the real lesson in all of this is that you should, where possible, use immutable objects as components of your objects so that you that don’t have to worry about defensive copying (Item 17). In the case of our Period example, use Instant (or LocalDateTime or ZonedDateTime), unless you’re using a release prior to Java 8. If you are using an earlier release, one option is to store the primitive long returned by Date.getTime() in place of a Date reference. 可以說,所有這些教訓體現了,在可能的情況下,應該使用不可變對象作為對象的組件,這樣就不必操心防御性復制([Item-17](/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md))。在我們的 Period 示例中,使用 Instant(或 LocalDateTime 或 ZonedDateTime),除非你使用的是 Java 8 之前的版本。如果使用較早的版本,一個選項是存儲 `Date.getTime()` 返回的 long 基本數據類型,而不是 Date 引用。 There may be a performance penalty associated with defensive copying and it isn’t always justified. If a class trusts its caller not to modify an internal component, perhaps because the class and its client are both part of the same package, then it may be appropriate to dispense with defensive copying. Under these circumstances, the class documentation should make it clear that the caller must not modify the affected parameters or return values. 防御性復制可能會帶來性能損失,而且并不總是合理的。如果一個類信任它的調用者不會去修改內部組件,可能是因為類和它的客戶端都是同一個包的一部分,那么就應該避免防御性復制。在這種情況下,類文檔應該表明調用者不能修改受影響的參數或返回值。 Even across package boundaries, it is not always appropriate to make a defensive copy of a mutable parameter before integrating it into an object. There are some methods and constructors whose invocation indicates an explicit handoff of the object referenced by a parameter. When invoking such a method, the client promises that it will no longer modify the object directly. A method or constructor that expects to take ownership of a client-provided mutable object must make this clear in its documentation. 即使跨越包邊界,在將可變參數集成到對象之前對其進行防御性復制也并不總是合適的。有一些方法和構造函數,它們的調用要求參數引用的對象要進行顯式切換。當調用這樣一個方法時,客戶端承諾不再直接修改對象。希望擁有客戶端提供的可變對象所有權的方法或構造函數必須在其文檔中明確說明這一點。 Classes containing methods or constructors whose invocation indicates a transfer of control cannot defend themselves against malicious clients. Such classes are acceptable only when there is mutual trust between a class and its client or when damage to the class’s invariants would harm no one but the client. An example of the latter situation is the wrapper class pattern (Item 18). Depending on the nature of the wrapper class, the client could destroy the class’s invariants by directly accessing an object after it has been wrapped, but this typically would harm only the client. 包含方法或構造函數的類,如果其方法或構造函數的調用需要移交對象的控制權,就不能保護自己免受惡意客戶端的攻擊。只有當一個類和它的客戶端之間相互信任,或者對類的不變量的破壞只會對客戶端造成傷害時,這樣的類才是可接受的。后一種情況的一個例子是包裝類模式([Item-18](/Chapter-4/Chapter-4-Item-18-Favor-composition-over-inheritance.md))。根據包裝類的性質,客戶端可以在包裝對象之后直接訪問對象,從而破壞類的不變量,但這通常只會損害客戶端。 In summary, if a class has mutable components that it gets from or returns to its clients, the class must defensively copy these components. If the cost of the copy would be prohibitive and the class trusts its clients not to modify the components inappropriately, then the defensive copy may be replaced by documentation outlining the client’s responsibility not to modify the affected components. 總而言之,如果一個類具有從客戶端獲取或返回給客戶端的可變組件,則該類必須防御性地復制這些組件。如果復制的成本過高,并且類信任它的客戶端不會不適當地修改組件,那么可以不進行防御性的復制,取而代之的是在文檔中指明客戶端的職責是不得修改受到影響的組件。 --- **[Back to contents of the chapter(返回章節目錄)](/Chapter-8/Chapter-8-Introduction.md)** - **Previous Item(上一條目):[Item 49: Check parameters for validity(檢查參數的有效性)](/Chapter-8/Chapter-8-Item-49-Check-parameters-for-validity.md)** - **Next Item(下一條目):[Item 51: Design method signatures carefully(仔細設計方法簽名)](/Chapter-8/Chapter-8-Item-51-Design-method-signatures-carefully.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>

                              哎呀哎呀视频在线观看