簡體   English   中英

我是否需要防御性的復制,以* non final *構建一個不可變的類,盡管它是不可變的字段?

[英]Do I need defensive copying constructing an immutable class with *non final* albeit immutable fields?

如果要構造一個不可變的類,則不應將引用公開給非最終字段-甚至對於Strings之類的不可變對象?

public final class Test { // Test class is meant to be immutable

    private String s; // CAN'T MAKE THIS FINAL

    void onCreate(String s) { // a callback called ONCE after construction
        this.s = new String(s); // do I need to do this ? (protect me from me)
    }

    public String getS() {
        return new String(s); //do I need to do this ?(protect me from the world)
    }
}

從理論上講,通過不安全的發布,可以看到帶有未初始化( nulls Test類的實例,也可以通過正確初始化的s看到該實例。 這可以通過使s volatile

但是,如果您發生了這樣的回調,我想您想再看一下您的設計。

如果您要使類可Serializable那么您將遇到更多問題。

我認為沒有必要。 甚至在文檔中說:

字符串是常量; 它們的值創建后無法更改。 由於String對象是不可變的,因此可以共享它們。

因此,一旦創建了String對象,其值就永遠不會改變。 如果要“更改”變量的值,則會創建一個新的String對象。 例如在toUpperCase方法中,原始字符串不變,但是創建了一個新副本。

編輯:

考慮字符串時,將文字放入共享池中,這意味着:

String h = "HELLO";
String h1 = "HELLO";

s1s2引用相同的對象。

您可以嘗試以下代碼返回true

String h = "HELLO";
String h1 = "HELLO";
boolean r = (h==h1);
System.out.println(r);

但是,您可以使用反射更改String的值:

java.lang.reflect.Field valueField = String.class.getDeclaredField("value");
valueField.setAccessible(true);
valueField.set("Original", "Modified".toCharArray()); 

從技術上講,如果您確實想要Java中的不可變類,則必須確保在創建類后不能更改該類的實例。 因此,它的所有字段都可以是最終字段,例如,如果它們通過吸氣劑“暴露”在世界上,那么這些字段本身必須是不可變的(就像字符串一樣),或者必須不返回給外部世界(保留私有並創建防御性副本) (在getter中使用它們),因此原始字段值保持不變。 同樣,不可變性也不能輕易被此類繼承而破壞。

您可以在《有效的Java》(約書亞·布洛赫(Joshua Bloch)的書)中閱讀有關它的更多信息,或從Internet上獲得一些注釋,例如從此處

關於您最近對帖子的更新,以下建議可確保初始化僅進行一次:

private String s; // CAN'T MAKE THIS FINAL
private boolean stringWasSet = false;

public void onCreate(String s) { // a callback called ONCE after construction
    if (!stringWasSet) {
        this.s = s; // No need for defensive copy here, if the variable itself is immutable, like String
        stringWasSet = true;
    }
}

public String getS() {
    return s; // No need for defensive copy here, if the variable itself is immutable, like String
}

此類是否不可變(對於不可變的任何定義)都沒有關系。 特別是,引用s是否曾經更改為指向其他字符串都沒有關系。 字符串對象是不可變的,因此您無需復制它。 如果沒有防御性復制,則getS調用者將獲得對Test方法和getS的其他調用者所使用的同一字符串對象的getS 沒關系,因為它們對該字符串執行的任何操作1都不會影響其他引用對象。 這將浪費時間和內存。

1我忽略了反思。 像這樣惡意使用反射的代碼幾乎可以破壞所有內容,並且不會偶然或難以發現。 擔心這種情況甚至是不切實際的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM