簡體   English   中英

Java中的可變可見性

[英]Variable visibility in Java

在下面的代碼中,為什么我不能從另一個線程中看到變量“i”?

public class Main {
    public static void main(String[] args) {
        int i = 0;
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(i);
            }
        }).start();
    }
}

為什么我可以在下面的代碼中看到它?

public class Main {
    int i = 0;      
    public static void main(String[] args) {
        new Main().method();
    }

    private void method() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                i = 1;
            }
        }).start();
    }
}

來自docs

與實例方法和變量一樣,內部類與其封閉類的實例相關聯,並且可以直接訪問該對象的方法和字段。

在此輸入圖像描述

現在先拿第二個例子,當在父類中聲明i時,內部類可以訪問它,因為內部類可以訪問整個父對象。 i正確地從內部類引用為Main.this.i編譯器秘密地在內部類中this.i的副本。 this永遠不會在對象內部發生變化, this.i將指向內部和外部類的正確對象。 編譯器很高興。

在第一個例子中, i不是父類的一部分,它在一個方法中聲明,因此它的引用現在在棧上 ,而不是在堆上。 (在這種情況下, i不會生活在上圖所示的那個大外圓上)。 現在編譯器必須秘密地在內部類中復制i 但是害怕方法的i可能會在堆棧上改變。 編譯器不能在這里使用外部類連接到達i ,並且每次更改時都無法從堆棧中復制i引用。 因此,它是決定,你必須使該方法的i參考不變,或者換句話說, final

Java播放的模糊秘密技巧,以便您可以從內部類訪問外部字段,詳細解釋如下: http//techtracer.com/2008/04/14/mystery-of-accessibility-in-local-inner-classes/

並在這里討論了更多“為什么”: http//people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg04044.html

tl; dr:實例字段可由內部類直接訪問,本地字段必須最終可由內部類訪問。

Java僅允許您訪問在匿名類中聲明為final的局部變量的值。 不允許寫作。 這樣做的理由是因為函數范圍可能(實際上在這種情況下)退出,而變量可以從內部類中訪問,該值實際上是在匿名類實例中緩存的。 為了避免混淆並使JIT工作,這些變量只允許是最終的。 請注意,只能修改原始值或引用,但仍可以修改引用對象中包含的任何內容。

在第二種情況下,它是線程可訪問的實例變量。 請注意, i = 1現在代表Main.this.i ; Main.this. 部分是隱含的。

在第一個程序中,您可以“查看”變量i ,但不能訪問它,因為它未被聲明為final 只能訪問在創建匿名類實例之前聲明的成員變量和final局部變量:

public class Main {
    public static void main(String[] args) {
        final int i = 123; // Make the variable final
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(i); // Now it works, but you cannot assign it
            }
        }).start();
    }
}

在ideone上演示

使變量static也可以:

public class Main {
    private static int i = 321;
    public static void main (String[] args) throws java.lang.Exception
    {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(i); // Now it works, but you cannot assign it
            }
        }).start();
    }
}

演示。

暫無
暫無

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

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