首頁 > Java > java教程 > 主體

談談Java的匿名內部類別

高洛峰
發布: 2016-12-15 12:48:42
原創
1263 人瀏覽過

在很多時候,我們需要在類別的內部初始化一個靜態的Map或List,然後保存一下常數值提供給類別內部方法使用。 
我們通常的做法是: 
先初始化一個Map的靜態變數。
接著在靜態區塊加入常數值: 

Java程式碼 

private final static Map<String, String> CONSTANT =   
    new HashMap<String, String>();  
static {  
    CONSTANT.put("1", "one");  
    CONSTANT.put("2", "two");  
}
登入後複製

其實還可以這麼寫: 

Java程式碼 

private final static Map<String, String> CONSTANT =   
     new HashMap<String, String>() {  
    {  
        put("1", "one");  
        put("2", "two");  
    }  
};
登入後複製

程式碼 

new Thread() {  
    public void run() {  
        System.out.println("Thread running!");  
    };  
}.start();
登入後複製

其實上面這段程式碼的意思是,宣告一個Thread的子類別並重寫Thread的run()方法,然後建立一個該子類別的實例然後呼叫其start()方法。由於聲明的該Thread的子類別沒有名字,所以叫匿名類別。又由於沒有名字的類別只能存在於一個類別或一個方法內部,所以又稱為匿名內部類別。

匿名內部類別的語法也可以這麼寫: 



Java程式碼 

Thread thread = new Thread() {  
    public void run() {  
        System.out.println("Thread running!");  
    };  
};   
thread.start();
登入後複製

唯一的區別就是不是直接建立子類別並呼叫其方法,而是聲明一個該子類別的父類別引用thread,然後透過該父類別引用呼叫子類別方法。 

建立完匿名類別的實例後,沒有立即執行start(),建立實例和執行實例的方法分開。

兩者的差異相當於: 


Java程式碼 

//1  
new User().setName("Boyce Zhang");  
  
//2  
User user = new User();  
user.setName("Boyce Zhang");
登入後複製

匿名內部類別的另一個語法場景: 

Java程式碼 

new Thread() {  
    public void run() {  
        System.out.println("Thread running!");  
    };  
    {  
        start();  
    }  
};
登入後複製

實際上這種寫法呼叫其類別方法。 

局部程式碼區塊內的語句是在建立該類別的實例後由類別載入器隱含立即執行的。 

相當於: 



Java程式碼 

public class MyThread extends Thread {  
    {  
        start();  
    }  
    public void run() {  
        System.out.println("Thread running!");  
    };  
}
登入後複製

所以三種方式在執行的時刻上略微的差別之外,效果並沒有太大的區別。

這樣一來,前面初始化Map的方式就不難理解了: 



Java程式碼 

private final static Map<String, String> CONSTANT = new HashMap<String, String>() {  
    {  
        put("1", "one");  
        put("2", "two");  
    }  
};
登入後複製

原理就是: 

宣告並實例化一個HashMap的子類別(子類別沒有重寫父類別HashMap的任何方法),並且在子類別的類別局部程式碼區塊呼叫父類別HashMap的put()方法。 

最後宣告一個Map介面引用CONSTANT指向實例化的HashMap子類別的實例。 

根據前面的例子我們知道,類別局部程式碼區塊中的put()方法呼叫將在HashMap的匿名子類別被實例化後由類別載入器隱含的執行。 

其實,對於Java的任何類別或接口,都可以聲明一個匿名類別繼承或實現它。如: 


Java程式碼 

//重写父类方法,局部代码块调用自己重写过的父类方法。  
List<String> list = new ArrayList<String>() {  
    public boolean add(String e) {  
        System.out.println("Cannot add anything!");  
    }  
      
    //代码块的顺序在前后都无所谓,可以出现在类范围的任何位置。  
    {  
        add("Boyce Zhang");  
    }  
};  
  
//局部代码块调用父类方法。  
dao.add(new User(){  
    {  
        setName("Boyce Zhang");  
        setAge(26);  
    }  
});  
  
//重写父类方法  
ThreadLocal<User> threadLocal = new ThreadLocal<User>() {  
    protected String initialValue() {  
        return new User("Boyce Zhang", 26);  
    }  
};
登入後複製

在匿名類別的內部我們不但可以實作或重寫其父類別的方法。 

而且也可以在其類別的局部程式碼區塊中執行自己的方法或其父類別的方法。 

這並不是匿名內部類別的特殊語法,而是Java的語法,對於任何類別都適用。 


這種寫法常常就是用在實例化一個類別後立即執行某些方法做一些類別實例的資料初始化什麼的。
其作用和先實例化一個類,在使用其引用調用需要立即調用的方法是一樣的,如: 


Java代碼 

Map<String, String> map = new HashMap<String, String>();  
map.put("1", "one");  
map.put("2", "two");
登入後複製
這種語法的優點就是簡單,實例化一個類後立即做一些事情,比較方便。 

效果有一點兒像Javascript裡的即時函數。但是有本質的區別。 

因為Javascript沒有類別的概念,或是說Javascript中function就是類別,類別就是function,所以即時函數是載入完後執行整個function。而Java的局部程式碼區塊是可以選擇執行類別的任何方法。


當然這種寫法也有其缺點: 
每一個內部類別的實例都會隱性的持有一個指向外部類別的引用(靜態內部類別除外),這樣一方面是多餘的引用浪費,另一方面當串行化這個子類別實例時外部類別也會被不知不覺的串列化,如果外部類別沒有實作serialize介面時,就會報錯。


更多談談Java的匿名內部類相關文章請關注PHP中文網!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板