簡體   English   中英

最終進入匿名類,就像C ++中的宏一樣?

[英]final in anonymous classes,just like macros in C++?

在Java中使用匿名內部類時,必須將在匿名內部類中使用的封閉類的變量聲明為final 好吧,我明白了為什么必須從

“通過將lastPrice和price最終定為變量,它們不再是真正的變量,而是常量。然后,編譯器可以將常量類的值替換為匿名類中對lastPrice和price的使用(當然是在編譯時),然后您不再會訪問不存在的變量的問題”

這使我無所適從,這是final關鍵字的工作方式,就像C \\ C ++中的Macros一樣。到現在為止,我使用final for變量的方式是每當我嘗試修改它們時(偶然),我都會得到一個錯誤您無法修改它,因為它被聲明為final 。但是對我來說,這種替換的事情還不清楚。

問題:根據上面鏈接中選擇的答案,回答者說

這就是為什么它不起作用的原因:

變量lastPrice和price是main()方法中的局部變量。 您使用匿名類創建的對象可能會持續到main()方法返回之后。

當main()方法返回時,將從棧中清除局部變量(例如lastPrice和price),因此在main()返回之后它們將不再存在。

但是匿名類對象引用了這些變量。 如果匿名類對象在清除變量后嘗試訪問變量,則事情將變得非常糟糕。

**

該存儲將在以后進行替換的地方?誰來照顧它?最終變量只是被值替換?

**

不,這不像C ++中的宏。 不同之處在於宏是在編譯時評估的,而預處理器則用其定義替換宏。

另一方面, final變量可以在運行時計算。 但是,一旦設置,該值將無法在以后更改。 該約束使得可以在內部類中使用該值。

讓我們看一個例子,使它更清楚:

public void func(final int param) {
    InnerClass inner = new InnerClass() {
        public void innerFunc() {
            System.out.println(param);
        }
    }

    inner.innerFunc();
}

請注意,可以在運行時通過向其傳遞不同的值來設置param 但是每次func() ,都會創建一個新的InnerClass對象,並捕獲param當前值,由於它被聲明為final ,因此保證永遠不會更改。

在變量恆定的另一種情況下,編譯器可以在編譯時替換該值。 但是,這對於內部類而言並不特殊,因為無論在何處使用常量,常量都會在編譯時替換。

這個故事的寓意是,匿名內部類可以訪問任何final變量,無論它是編譯時常量還是在運行時計算。

對於匿名類,實際上是在聲明“無名”嵌套類。 對於嵌套類,編譯器將使用構造函數生成一個新的獨立公共類,該構造函數將使用它用作參數的所有變量(對於“命名”嵌套類,這始終是原始/封閉類的實例)。 這樣做是因為運行時環境沒有嵌套類的概念,因此需要從嵌套類到獨立類的(自動)轉換。

以下面的代碼為例:

public class EnclosingClass {
    public void someMethod() {
        String shared = "hello"; 
        new Thread() {
            public void run() {
                // this is not valid, won't compile
                System.out.println(shared); // this instance expects shared to point to the reference where the String object "hello" lives in heap
            }
        }.start();

        // change the reference 'shared' points to, with a new value
        shared = "other hello"; 
        System.out.println(shared);
    }
}

那是行不通的,因為這是編譯器在后台執行的操作:

public void someMethod() {
    String shared = "hello"; 
    new EnclosingClass$1(shared).start();

    // change the reference 'shared' points to, with a new value
    shared = "other hello"; 
    System.out.println(shared);
}

原始的匿名類被編譯器生成的一些獨立類代替(代碼不准確,但應該可以帶給您一個好主意):

public class EnclosingClass$1 extends Thread {
    String shared;
    public EnclosingClass$1(String shared) {
        this.shared = shared;
    }

    public void run() {
        System.out.println(shared);
    }
}

如您所見,獨立類保留對共享對象的引用,請記住,java中的所有內容都是按值傳遞的,因此即使EnclosingClass中的引用變量“ shared”被更改,其指向的實例也不會被修改,以及指向它的所有其他參考變量(例如匿名類中的Enclosing $ 1)將不會意識到這一點。 這是編譯器強迫您將“共享”變量聲明為final的主要原因,這樣,這種類型的行為就不會使其融入已經運行的代碼中。

現在,這是在匿名類中使用實例變量時發生的事情(這是解決問題,將邏輯移至“實例”方法或類的構造函數時應采取的措施):

public class EnclosingClass {
    String shared = "hello";
    public void someMethod() {
        new Thread() {
            public void run() {
                System.out.println(shared); // this is perfectly valid
            }
        }.start();

        // change the reference 'shared' points to, with a new value
        shared = "other hello"; 
        System.out.println(shared);
    }
}

這樣編譯就可以了,因為編譯器將修改代碼,因此新生成的類Enclosing $ 1將保存對實例化該實例的EnclosingClass實例的引用(這只是一個表示,但應該可以使您前進):

public void someMethod() {
    new EnclosingClass$1(this).start();

    // change the reference 'shared' points to, with a new value
    shared = "other hello"; 
    System.out.println(shared);
}

public class EnclosingClass$1 extends Thread {
    EnclosingClass enclosing;
    public EnclosingClass$1(EnclosingClass enclosing) {
        this.enclosing = enclosing;
    }

    public void run() {
        System.out.println(enclosing.shared);
    }
}

像這樣,當EnclosingClass中的參考變量“ shared”被重新分配,並且發生在調用Thread#run()之前,您會看到“ other hello”打印了兩次,因為現在EnclosingClass $ 1#enclosing變量將保留一個參考。到聲明該類的對象的對象,因此對該對象上任何屬性的更改對於EnclosingClass $ 1的實例都是可見的。

有關該主題的更多信息,您可以查看以下出色的博客文章(不是我寫的): http : //kevinboone.net/java_inner.html

來自Brian Goetz的@Butterflow:

聲明最終字段有助於優化器做出更好的優化決策,因為如果編譯器知道該字段的值不會改變,則可以安全地將該值緩存在寄存器中 final字段還通過使編譯器強制字段為只讀來提供額外的安全性。

您可以在此處找到有關關鍵字final的完整文章

暫無
暫無

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

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