1. 程式人生 > >Java基礎系列(三十):區域性內部類

Java基礎系列(三十):區域性內部類

What

區域性內部類就是定義在某個方法內部的內部類,它的作用域僅限於這個方法。

Why

當我們在外圍類中定義的內部類僅僅在某個方法中使用了一次,這種情況下,我們就可以選擇去使用區域性內部類。

How

以上節課的例子繼續講解,由於TestListener這個內部類僅僅在start方法中使用了一次,所以我們在這裡可以使用區域性內部類。

public class InnerClassTest {
    private Integer times;
    private boolean beep;

    public InnerClassTest(Integer times, boolean
beep) { this.interval = interval; this.beep = beep; }; public void start(){ class TestListener implements ActionListner { public void actionPerformed(ActionEvent event) { System.out.println("TestListener is running"); if (beep) { Tookit.getDefaultToolkit().beep(); } } } ActionListener listener = new
TestListener(); Timer t = new Timer(times, listner); t.start(); } }

這裡需要注意,區域性類不可以使用public或者private訪問修飾符進行宣告,因為它作用域僅僅被限定在宣告這個區域性類的塊中。

區域性類有一個優勢,它可以對外部世界完全的隱藏,即使他的外部類中的其他模組也不可以訪問它,除了start方法以外,沒有任何方法知道這個內部類的存在。

外部方法訪問變數(進階)

與其他的內部類相比,區域性類還有一個其他內部類所不具備的有優點。它不僅可以訪問包含它們的外部類,還可以訪問區域性變數,但是這些區域性變數必須宣告為final,它們一旦被賦值,就不能被改變。

下面我們接著來改變上面的那個栗子:

public void start(int times, boolean beep){
    class TestListener implements ActionListner {
        public void actionPerformed(ActionEvent event) {
            System.out.println("TestListener is running");
            if (flag) {
                Tookit.getDefaultToolkit().beep();
            }
        }
    }
    ActionListener listener = new TestListener();
    Timer t = new Timer(times, listner);
    t.start();
}  

我們可以看到,外圍類不在需要去儲存例項變數beep了,它只是引用start方法中的引數。
接下來我們來深入瞭解這個方法的控制流程:
1. 呼叫start方法
2. 呼叫內部類的構造器,初始化物件變數listener
3. 將listener引用傳遞給Timer構造器,定時器開始計時,start方法結束。此時,start方法中beep變數被回收。
4. 然後actionPerformed方法執行if(beep)

看到這裡,我相信大部分人會有疑問,為什麼beep變數被回收,但是actionPerformed方法仍然可以呼叫到這個方法?

實際上,內部類在beep域被釋放之前將beep域用start方法中的區域性變數進行備份,我們接下來來看一下反編譯後的內部類,來證實我們的猜測:

class InnerClassTest$TestListener {
    public InnerClass$TestListener(InnerClassTest, boolean);
    public void actionPerformed(java.awt.event.ActionEvent);

    final boolean val$beep;
    final InnerClassTest this$0;

}

請注意構造器的boolean引數和val$beep例項變數。當建立一個物件的時候,beep就會傳遞給構造器,並存儲在val$beep域中。編譯器必須檢測對區域性變數的訪問,為每一個變數建立相應的資料域,並將區域性變數拷貝到構造器中,以便將這些資料域初始化為區域性變數的副本。

匿名內部類

匿名內部類其實就是對區域性內部類的一個深化的應用,如果我們只是需要建立這個類的一個物件,那麼我們完全不必去給這個類命名,這種類就被稱為匿名內部類。

接下來,我們接著對上面的例子進行改編:

public void start(int times, boolean beep){  
    ActionListener listener = new ActionListener() {
        public void actionPerformed(ActionEvent event) {
            System.out.println("TestListener is running");
            if (flag) {
                Tookit.getDefaultToolkit().beep();
            }
        }
    }
    Timer t = new Timer(times, listner);
    t.start();
}  

這段語句的含義是:建立一個實現ActionListener介面的類的新物件,需要實現的方法定義在括號內。

通用的語法格式是:

new SuperType(constrution params) {
    inner class methods and data
}

其中SuperType既可以是介面,那麼內部類就要去實現這個介面,它同樣可以是一個類,那麼內部類就要去擴充套件它。

由於構造器的名字必須與類名相同,但是匿名類並沒有類名,所以,匿名類不能有構造器。取而代之的是,將構造器引數傳遞給父類構造器。尤其是在內部類實現介面的時候,不能有任何構造引數。

如果構造引數的閉小括號後跟的是單引號,那麼就是在構造一個類的新物件,如果說構造引數的閉小括號後面跟一個開大括號,正在定義的就是匿名內部類。

靜態內部類(僅供瞭解)

有時候,使用內部類只是為了把一個類隱藏在另外一個類的內部,並不需要內部類引用外部類物件。所以可以把內部類宣告為static,以便取消產生的引用。

只有內部類可以宣告為static,靜態內部類的物件除了沒有對生成它的外圍類物件的引用特權外,與其他所有內部類完全一樣。

與常規內部類不同的地方是,靜態內部類可以有靜態域和方法,宣告在介面中的內部類自動生成static和public類。


公眾號

掃碼或微信搜尋 Vi的技術部落格,關注公眾號,不定期送書活動各種福利~

image