將一個類定義放在另外一個類、方法、做用域、匿名類等地方,就是內部類;內部類只能由外部類對象建立(經過外部方法或者.new方法),內部類對象建立時必須已經有一個外部類對象,而且與之鏈接(在內部類中會建立一個指向其外部類對象的引用),內部類能夠訪問到外部類對象的全部成員(包括private);java
如:示例代碼中,在Parcel類的內部定義了內部類:Contents和Destination,在外部類方法ship中,能夠直接建立內部類對象;可是在類外部,只能想建立外部類對象q,而後再經過該對象的contents()和to()方法來建立內部類對象;或者能夠經過q2.new來調用內部類的構造方法;注意內部類的引用類型格式是OutClass.InnerClass;git
在內部類中,可使用OutClass.this來調用外部類對象(必然存在該對象,且和內部類對象鏈接);github
當內部類指定是private時,只能在類的內部調用該內部類,外部類對象沒法訪問而且建立該內部類對象;protected內部類則只有外部類的子類能夠訪問;經過建立private內部類能夠經過類來完成一些工做而且隱藏全部類定義的細節;框架
在做用域中使用類時,超過做用域該類就沒法被訪問;如示例代碼展現了幾種內部類的定義方式;單元測試
注意在Parcel七、Parcel8中使用了匿名類,經過基類和接口建立匿名類對象的同時建立類的定義,匿名類的意義爲:建立繼承自一個基類或者接口的匿名類對象,默認添加繼承關係;測試
使用匿名類時,不要忘記類定義後的分號;優化
若是如Parcel9中匿名內部類使用外部類中定義的對象,那麼編譯器會要求其參數引用是final的(如外部類方法destination中),不然將會出錯;this
如AnonymousConstructor中,能夠經過對實例的初始化實現一個匿名構造器功能;若是匿名類中沒有直接使用i,則外部方法的參數不須要指定是final的;又如Parcel10中,因爲if語句並不能在初始化時被執行,全部匿名類中對實例化的初始化實際效果就至關因而構造器;spa
匿名內部類只能實現接口和擴展類的其中一個;對象
在上一節最後使用到了工廠方法,將多個子類的構造器使用通用接口包裝,而後對該接口進行調用便可實現多種子類的構造活動;而因爲子類的構造器是不可見的,而且沒有必要建立做爲工廠的具名類,而只須要一個單一的工廠對象便可,使用匿名內部類改進後的代碼爲:示例代碼;改進後的代碼更加具備實際意義(消除了每一個子類單獨的工廠類);
當不須要內部類和外部類的對象之間有聯繫時,能夠將內部類聲明爲static,這種方式稱爲嵌套類;
嵌套類因爲申明瞭static,並不像內部類同樣,具備一個外部類的對象引用;一旦建立了嵌套類,就說明了嵌套類中的對象並不須要外部類對象的引用,而且不能從嵌套類的對象中訪問非靜態的外部類對象,沒有new方法和this;
正常狀況下,接口中是不能放置任何代碼的,可是嵌套類能夠做爲接口的一部分;因爲接口中全部的成員都將變爲static和public,類也同樣,一旦放入接口中,該類就是一個嵌套類;如:示例代碼;
經過在每一個類中添加main()方法能夠完成單元測試的工做,可是這樣作會將帶上已經編譯後的額外代碼,經過將main方法放置到嵌套類中,能夠優化單元測試,如: 示例代碼;在編譯以後,每一個嵌套類(以及內部類)都會產生一個OutClass$InnerClass.class相似的文件,單元測試的類也同樣,只要運行java TestBed\$Tester便可進行測試工做;只要在發佈時,簡單的將該文件刪除就能夠刪除單元測試的內容;
1. 實現多重繼承
經過使用內部類獨立繼承自一個接口的實現完成多重繼承的工做;同時因爲外部類不能同時繼承自兩個基類,只能經過內部類完成多重繼承;
如:示例代碼中,A、B接口能夠經過X、Y兩種方式實現多重繼承;C、D抽象類則只能經過Z來實現多重繼承;
2. 在同一個外圍類中,可使用多個內部類以不一樣的方式實現同一個接口,這樣能夠在同一個類中完成一個接口的多種實現而不用另外建立一個新類,經過這種方式能夠造成更加清晰的代碼結構;如Sequence中實現ForwardSelector和ReverseSelector兩種Selector接口實現:
interface Selector{
char getCharAt(int i);
}
class Sequence {
private String s;
Sequence(String s){
this.s = s;
}
private Selector fs(){
return new Selector(){
public char getCharAt(int i){
return s.charAt(i);
}
};
}
private Selector rs(){
return new Selector(){
public char getCharAt(int i){
int index = s.length() - i;
return s.charAt(index);
}
};
}
public char forwardSelector(int i){return fs().getCharAt(i);}
public char reverseSelector(int i){return rs().getCharAt(i);}
}
3. 實現回調
當一個外部類繼承自一個接口的實現時,能夠經過內部類完成接口的另外一個實現;並在適當的適合進行選擇;如:示例代碼 中,Callee2繼承自MyIncrement,該類是接口Incrementable的一個實現,Callee2在某些時候須要使用繼承來的方法,可是同時若是又有對該接口的另外實現,則能夠經過新建內部類:Closure來完成,在Closure中返回一個Callee2的鉤子,而且只能調用increment方法;而且經過設置內部類爲private以及getCallbackReference()方法來隱藏實現過程;
4. 控制框架的編寫;
控制框架每每由單個類來建立,用內部類來表示解決問題所必須的各類不一樣的action(),從而使得實現的細節被封裝起來;
1. 內部類的繼承
因爲內部類必需要和一個外部類的對象鏈接才能夠被實例化,因此在繼承一個內部類時,在構造方法中應該引用一個其外部類的對象,而且必須使用:
outclassReference.super()
的方式進行引用;如:
class WithInner{
class Inner{}
}
public class InheritInner extends WithInner.Inner {
InheritInner(WithInner wi){
wi.super();
}
}
2. 內部類的覆蓋
內部類和外部類是徹底獨立的個體,各自在本身的命名空間內,如:示例代碼中,在new BigEgg()時,先調用基類構造方法,打印New Egg()以及調用基類的內部類Yolk構造方法;此時,BigEgg()中的Yolk類沒有起到做用,可是,若是添加BigEgg的構造方法,而且在其中建立Yolk內部類,則是能夠完成覆蓋的(實際上是生成了一個同名方法);真正完成內部類的覆蓋,則須要使內部類也繼承基類中的內部類,並完成改寫操做,以下面的示例;
在示例代碼中還能夠看到內部類的初始化過程:
基類的內部類 -> 基類 -> 基類內部類 -> 子類內部類 -> 子類;
3. 局部內部類
當一個內部類在做用域內建立時,則會局部內部類,局部內部類不能訪問說明符,不是外部類的一部分,而是做用域的一部分,因此能夠訪問做用域內的常量以及外部類的成員;
局部做用域常在方法體內定義,而且被用來重載構造器,或者生成多個內部類對象等;