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的技術部落格,關注公眾號,不定期送書活動各種福利~