C#和Java對static關鍵字的理解有很大分歧,主要就是在內部類上。此外,由於Java沒有類似委託這種資料結構,內部類還要擔當封裝方法和響應事件這樣的重要責任。
從C#到Java入門指引之一——基本類型和字串
從C#到Java入門指引之二——類
從C#到Java入門指引之三——內部類
截然不同的內部類
與C#不一樣,Java不允許外部類使用static關鍵字修飾。那麼我們來看看static關鍵字修飾內部類的一個例子(來自《細說Java》)
public class Circle { private static int radius; public static class Center{ // 靜態內部類也可聲明靜態成員 private static Center defaultCenter; static { defaultCenter = new Center(10, 10); // 訪問外部類的私人成員 System.out.println(radius); } public int x, y; public Center(int x, int y){ this.x = x; this.y = y; } }} public class Main{ public static void main(String[] args){ // 靜態內部類可以不依賴外部來執行個體的存在而存在 Circle.Center center = new Circle.Center(15, 20); }}
習慣C#的同學只怕又會覺得納悶,明明這個內部類都static了,為啥還有構造方法?!
這是兩種語言對static的不同解讀造成的:
在C#中,static修飾內部類,表示這是個不能夠執行個體化的靜態類;而在Java中,這表示這個內部類可以不依賴於外部類的執行個體而執行個體化。
所以,Java中加了static的內部類相當於C#中普通的內部類。
那麼,沒加static修飾的內部類,又表示什麼呢?自然是依賴於外部類的執行個體才能執行個體化的內部類嘛!
public class Circle { private int radius; public class Center{ // 普通內部類也可聲明靜態成員 //private static Center defaultCenter; // 但是可以含有靜態常量 private static final int FINAL_STATIC = 10; public int x, y; private int radius; public Center(int x, int y){ this.x = x; this.y = y; radius = 3; // 訪問內部類的成員 this.radius = 3; // this指向內部類 Circle.this.radius = 3; // Circle.this指向外部類 } }} public class Main{ public static void main(String[] args){ // 普通內部類必須依賴外部類的執行個體 //Circle.Center center = new Circle.Center(15, 20); // 必須先建立外部類的執行個體 Circle circle = new Circle(); Circle.Center center = circle.new Center(15, 20); }}
看到建立內部類執行個體的那行代碼,是否覺得很彆扭?
沒錯,而且如果又涉及到繼承的話,比如繼承某個類的內部類,那就更加繁瑣了。所以我的建議是,非static的內部類,能不用就不用。
方法內聲明的局部類
普通內部類可以在語句塊(比如方法)內聲明,那就是局部類。
局部類只在當前語句塊內可訪問。
局部類可以訪問語句塊中的局部變數(但必須用final修飾),以及外部類中的成員。
更為常用的匿名類
如果局部類只是用一次,就可以聲明為匿名類。例子同樣來自《細說Java》:
interface Center{} public class Circle { public Center getCenter(final int pointX, final int pointY){ return new Center(){ public int x, y; { x = pointX; y = pointY; } }; }} public class Main{ public static void main(String[] args){ Circle circle = new Circle(); Center center = circle.getCenter(10, 15); }}
匿名類不能聲明構造方法,所以只能用初始化塊的方式對成員初始化。
匿名類也不是new後面的那個類型:如果new後面是一個類,那麼匿名類的類型是這個類的子類;如果new後面是一個介面,那麼匿名類的類型是這個介面的一個實現。
比如我們常常會給Activity添加一個Handler,以處理各種訊息。每個Activity的Activity各不相同,所以也就沒有重用的必要,用匿名類即可:
private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { //... }};
匿名類用於封裝方法
許多時候,我們需要把方法當成一個變數處理,傳給另一個方法。C/C++可以用函數指標,C#可以用委託。Java沒有比較直觀的方式,但一般可以這麼做:
1.建立介面IFunc,裡面放一個方法的原型,比如void func();
2.建立實現了IFunc的類FuncClass,實現其中的func方法;
3.建立類FuncClass的對象funcObject,傳遞給別的方法。
很麻煩是不,好在有匿名類,可以將建立類和建立對象合成一步。比如,想要把一段代碼放到新線程去跑,就得把代碼塞到很麻煩是不,好在有匿名類,可以將建立類和建立對象合成一步。
比如,想要把一段代碼放到新線程去跑,就需要建立Runnable介面的實現,把代碼塞到run方法,傳給Thread的構造方法:
Thread thread = new Thread(new Runnable() { @Override public void run() { //... }});thread.start();
註:實際使用時,只需要直接重寫Thread的run方法,這麼寫其實是繞了彎路。
實現那個方法人們會把需要傳遞的方法放在一個類裡面,然後用這個類建立一個對象,通過傳遞這個對象來實現傳遞方法。這時候,匿名類就可以大顯身手了。
匿名類與事件響應機制
許多時候,我們需要把方法當成一個變數處理,傳給另一個方法。C/C++可以用函數指標,C#可以用委託,但Java沒有比較直觀的方式。通常,在Java代碼中,人們會把需要傳遞的方法放在一個類裡面,然後用這個類建立一個對象,通過傳遞這個對象來實現傳遞方法。這時候,匿名類就可以大顯身手了。
最簡單的例子,響應按鈕的點擊事件:
mButton.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View view){ //... }}
預設為靜態內部介面
聲明在類內部的介面,即為內部介面。
內部介面預設就是靜態————這個也容易理解,既然是介面,難道還要依賴於外部類的執行個體而存在麼。