文章目錄
1.內部類
首先舉一個簡單的例子,如果你想實現一個介面,但是這個介面中的一個方法和你構想的這個類中的一個 方法的名稱,參數相同,你應該怎麼辦?這時候,你可以建一個內部類實現這個介面。由於內部類對外部類的所有內容都是可訪問的,所以這樣做可以完成所有你直 接實現這個介面的功能。
不過你可能要質疑,更改一下方法的不就行了嗎?
的確,以此作為設計內部類的理由,實在沒有說服 力。
真正的原因是這樣的,java中的內部類和介面加在一起,可以的解決常被C++程式員抱怨java中存在的一個問題——沒有多繼承。實際上,C++的多繼承設計起來很複雜,而java通過內部類加上介面,可以很好的實現多繼承的效果。
內部類:一個內部類的定義是定義在另一個內部的類。
原因是:
1.一個內部類的對象能夠訪問建立它的對象的實現,包括私人資料。
2. 對於同一個包中的其他類來說,內部類能夠隱藏起來。
3.匿名內部類可以很方便的定義回調。
4.使用內部類可以非常方便的編寫事件驅動程式
內部類可以讓你更優雅地設計你的程式結構。下面從以下幾個方面來介紹:
第一次見面
public interface Contents { int value();}public interface Destination { String readLabel();}
public class Goods { private class Content implements Contents { private int i = 11; public int value() { return i; } } protected class GDestination implements Destination { private String label; private GDestination(String whereTo) { label = whereTo; } public String readLabel() { return label; } } public Destination dest(String s) { return new GDestination(s); } public Contents cont() { return new Content(); }}class TestGoods { public static void main(String[] args) { Goods p = new Goods(); Contents c = p.cont(); Destination d = p.dest("Beijing"); }}
在這個例子裡類
Content 和 GDestination
被定義在了類 Goods 內部,並且分別有著
protected 和 private
修飾符來控制存取層級。Content 代表著
Goods 的內容,而 GDestination
代表著 Goods 的目的地。它們分別實現了兩個介面Content和Destination。在後面的main方法裡,直接用
Contents c 和
Destination d進行操作,你甚至連這兩個內部類的名字都沒有看見!這樣,內部類的第一個好處就體現出來了——隱藏你不想讓別人知道的操作,也即封裝性。
同時,我們也發現了在外部類作用範圍之外得到內部類對象的第一個方法,那就是利用其外部類的方法建立並返回。上例中的cont()和
dest() 方法就是這麼做的。那麼還有沒有別的方法呢?當然有,其文法格式如下:
outerObject=new outerClass(Constructor Parameters);outerClass.innerClass innerObject=outerObject.new InnerClass(Constructor Parameters);
注意在建立非靜態內部類對象時,一定要先建立起相應的外部類對象。至於原因,也就引出了我們下一個話題——
非靜態內部類對象有著指向其外部類對象的引用
修改上面的例子
public class Goods { private valueRate=2; private class Content implements Contents { private int i = 11 * valueRate; public int value() { return i; } } protected class GDestination implements Destination { private String label; private GDestination(String whereTo) { label = whereTo; } public String readLabel() { return label; } } public Destination dest(String s) { return new GDestination(s); } public Contents cont() { return new Content(); }}
在這裡我們給
Goods 類增加了一個 private
成員變數 valueRate,意義是貨物的價值係數,在內部類
Content 的方法
value() 計算價值時把它乘上。我們發現,value()
可以訪問 valueRate,這也是內部類的第二個好處——一個內部類對象可以訪問建立它的外部類對象的內容,甚至包括私人變數!這是一個非常有用的特性,為我們在設計時提供了更多的思路和捷徑。要想實現這個功能,內部類對象就必須有指向外部類對象的引用。Java
編譯器在建立內部類對象時,隱式的把其外部類對象的引用也傳了進去並一直儲存著。這樣就使得內部類對象始終可以訪問其外部類對象,同時這也是為什麼在外部 類作用範圍之外向要建立內部類對象必須先建立其外部類對象的原因。(具體原因見:http://blog.csdn.net/yu422560654/article/details/6978981)
有人會問,如果內部類裡的一個成員變數與外部類的一個成員變數同名,也即外部類 的同名成員變數被屏蔽了,怎麼辦?沒事,Java裡用如下格式表達外部類的引用:
outerClass.this
有了它,我們就不怕這種屏蔽的情況了。
靜態內部類(嵌套類)
和普通的類一樣,內部類也可以有靜態。不過和非靜態內部類相比,區別就在於靜態內部類沒有了指向外部的引用。這實際上和
C++ 中的嵌套類很相像了,Java
內部類與 C++ 嵌套類最大的不同就在於是否有指向外部的引用這一點上,當然從設計的角度以及以它一些細節來講還有區別。
除此之外,在任何非靜態內部類中,都不能有待用資料,靜態方法或者又一個靜態內部類(內部類的嵌套可以不止一層)。不過靜態內部類中卻可以擁有這一切。這也算是兩者的第二個區別吧。
局部內部類
是的,Java
內部類也可以是局部的,它可以定義在一個方法甚至一個代碼塊之內。
public class Goods1 { public Destination dest(String s) { class GDestination implements Destination { private String label; private GDestination(String whereTo) { label = whereTo; } public String readLabel() { return label; } } return new GDestination(s); } public static void main(String[] args) { Goods1 g= new Goods1(); Destination d = g.dest("Beijing"); }}
上面就是這樣一個例子。在方法dest中我們定義了一個內部類,最後由這個方法返回這個內部類的對象。如果我們在用一個內部類的時候僅需要建立它的一個對象並傳給外部,就可以這樣做。當然,定義在方法中的內部類可以使設計多樣化,用途絕不僅僅在這一點。
下面有一個更怪的例子:
public class Goods2{ private void internalTracking(boolean b) { if(b) { class TrackingSlip { private String id; TrackingSlip(String s) { id = s; } String getSlip() { return id; } } TrackingSlip ts = new TrackingSlip("slip"); String s = ts.getSlip(); } } public void track() { internalTracking(true); } public static void main(String[] args) { Goods2 g= new Goods2(); g.track(); }}
你不能在
if 之外建立這個內部類的對象,因為這已經超出了它的範圍。不過在編譯的時候,內部類
TrackingSlip 和其他類一樣同時被編譯,只不過它由它自己的範圍,超出了這個範圍就無效,除此之外它和其他內部類並沒有區別。
2. 匿名類
匿名類是不能有名稱的類,所以沒辦法引用他們。必須在建立時,作為new語句的一部分來聲明他們。
這就要採用另一種形式 的new語句,如下所示:
new <類或介面> <類的主體>
這種形式的new語句聲明一個 新的匿名類,他對一個給定的類進行擴充,或實現一個給定的介面。他還建立那個類的一個新執行個體,並把他作為語句的結果而返回。要擴充的類和要實現的介面是
new語句的運算元,後跟匿名類的主體。
假如匿名類對另一個類進行擴充,他的主體能夠訪問類的成員、覆蓋他的方法等等,這和其他任何標準的類都 是相同的。假如匿名類實現了一個介面,他的主體必須實現介面的方法。
注意匿名類的聲明是在編譯時間進行的,執行個體化在運行時進行。這意味著
for迴圈中的一個new語句會建立相同匿名類的幾個執行個體,而不是建立幾個不同匿名類的一個執行個體。
從技術上說,匿名類可被視為非靜態內 部類,所以他們具備和方法內部聲明的非靜態內部類相同的許可權和限制。
假如要執行的任務需要一個對象,但卻不值得建立全新的對象(原因可能 是所需的類過於簡單,或是由於他只在一個方法內部使用),匿名類就顯得很有用。匿名類尤其適合在Swing應用程式中快速建立事件處理程式。
interface pr { void print1(); }public class noNameClass { public pr dest() { return new pr() { public void print1() { System.out.println("Hello world!!"); } }; }}public static void main(String args[]) { noNameClass c = new noNameClass(); pr hw = c.dest(); hw.print1();}
有一點需要注意的 是,匿名內部類由於沒有名字,所以它沒有建構函式(但是如果這個匿名內部類繼承了一個只含有帶參數建構函式的父類,建立它的時候必須帶上這些參數,並在實 現的過程中使用
super 關鍵字調用相應的內容)。如果你想要初始化它的成員變數,有下面幾種方法:
1. 如果是在一個方法的匿名內部類,可以利用這個方法傳進你想要的參數,不過記住,這些參數必須被聲明為
final 。
2. 將匿名內部類改造成有名字的局部內部類,這樣它就可以擁有建構函式了。
3. 在這個匿名內部類中使用初始化代碼塊