能夠將一個類的定義放在另外一個類的內部,這就是內部類。java
1.能夠實現隱藏閉包
2.內部類瞭解外圍類,並能與之通訊,不少時候能夠寫出更加優雅和清晰的代碼框架
10.1建立內部類this
public class Outer{spa
class Inner{設計
}code
}對象
若是想從外部類的"非靜態方法以外"的任意位置建立某個內部類對象,那麼必須具體指明這個對象的類型:OuterClassName.InnerClassName繼承
10.2連接到外部類接口
內部類自動擁有對外圍類全部成員的訪問權,這是如何作到的呢?當外圍類對象建立了一個內部類對象時,此內部類對象一定會祕密的捕獲一個指向那個外圍類對象的引用,在訪問外部類對象成員時,就是用這個引用來訪問。編譯器會幫助咱們處理這些細節,而咱們使用時,就像是內部類自己帶有的同樣。因此非static內部類在建立時候必需要由外部類對象來建立。
10.3使用.this與.new
若是要使用外部類對象的引用,OuterClassName.this來獲取,編譯器會檢查類型是否正確,無運行時開銷。
建立內部類對象:要先有外部類對象Outer o = new Outer();而後用外部類對象建立內部類對象Outer.Inner i = o.new Inner();不能夠用o.new Outer.Inner();
10.4內部類向上轉型
當把private內部類向上轉型成基類,尤爲是接口的時候,內部類就有了用武之地:這個接口的實現能夠徹底隱藏,徹底不可見,由於返回的是接口引用
參考java源碼AbstractList類:
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
...
}
當咱們外界使用迭代器的時候
Iterator iter = arrayList.iterator();
只能拿到接口的引用,徹底不知道里面是一個private內部類,由於Itr是private的,也不能建立它。
10.5在方法和做用域內的內部類
局部內部類:定義在方法體或者某一做用域裏,超出做用域不能使用。
注意:不能使用意味着在外部不能被訪問到類型,可是該類型在JVM裏是實際存在的,即便這個做用域已經結束。
10.6匿名內部類
沒有名字的內部類,能夠有帶參構造器,能夠添加並初始化域。
若是在內部類的內部要使用外部的一個對象,這個對象的引用須要是final的。構造器中使用的參數不須要是final的。
匿名內部類裏不能本身添加構造器(由於不能被訪問到,並且沒名字),須要作一些初始化的時候,能夠用代碼塊來實現。
匿名內部類與正規的繼承相比,要麼擴展類,要麼實現接口,不能兩者都有,而且只能實現一個接口。
10.6.1再訪工廠方法
能夠把工廠方法改爲匿名內部類。
interface Game{boolean move();}
interface GameFactory{Game getGame();}
class Checkers implements Game{
private Checkers(){}
private int moves = 0;
private static final int MOVES = 3;
public static GameFactory factory = new GameFactory(){
public Game getGame(){return new Checkers();}
};
public boolean move(){
return ++moves != MOVES;
}
}
10.7嵌套類-靜態內部類
1)建立靜態內部類對象不須要外部類對象
2)不能從靜態內部類對象訪問外圍內的非靜態成員
普通內部類不能夠有static字段,靜態能夠有
10.7.1接口內部的類
正常狀況下,接口內部是不能聽任何代碼的,可是嵌套類能夠放。放入接口中的任何類都是public static的。
用途:若是你想建立某些公共代碼,使得它們能夠被這個接口的全部實現使用,那麼接口中嵌套內部類就會很是方便。
10.7.2在多層嵌套類中訪問外部類成員
一個內部類被嵌套多少層並不重要,它能夠透明的訪問全部外圍類的全部成員。
class MMA{
private void f(){}
class A{
private void g(){}
public class B{
void h(){
f();//直接調用 沒有問題
g();
}
}
}
}
10.8爲何須要內部類
通常來講,內部類繼承某個類或實現某個接口,而且能夠操做外圍類對象。
核心緣由:內部類能夠提供多重繼承的能力。
好比: AbstractList裏有Itr和ListItr兩種迭代器,若是咱們只是讓AbstractList實現Iterator接口,那麼只能寫出一個迭代器,經過使用內部類方式能夠添加多個迭代器。
10.8.1閉包與回調
若是有一個類須要實現一個接口來提供某個方法,可是發現這個方法已經被另一個接口使用了,那麼能夠用內部類代替這個類去實現接口。
回調的價值在於它的靈活性---能夠在運行時動態決定須要調用什麼方法。
10.8.2內部類與控制框架
設計一個控制框架,要求到了設定時間就執行相應的事件:
//建立一個抽象事件類,有執行時間和延遲時間,在產生新事件的時候用延遲時間計算出執行時間,action方法由實現類實現。
public abstract class Event{
private long eventTime;
protected final long delayTime;
public Event(long delay){
this.delayTime = delay;
start();
}
public void start(){
eventTime = System.nanoTime() + delayTime;
}
public boolean ready(){
return System.nanoTime >= eventTime;
}
public abstract void action();
}
//建立一個控制類,循環遍歷全部事件,若是事件時間到了就執行action
public class Controller{
private List<Event> eventList = new ArrayList<>();
public void addEvent(Event e){eventList.add(e);}
public void run(){
while(eventlist.size()>0){
for(Event e : eventList){
if(e.ready){
e.action();
eventlist.remove();
}
}
}
}
}
從上面這2個類的設計來看,咱們徹底不知道event具體作什麼,可是咱們整個控制框架的核心功能:"把變化的事物(event具體實現),與不變的事物(時間發生過程)"分開。而咱們只須要建立不一樣的變化的Event,這正是內部類要作的事情:
1)控制框架裏的實現是由單個類建立的,從而使得實現細節被封裝起來。內部類用來表示解決問題所必須的不一樣action。
2)內部類能夠很容易訪問外部類成員,這種實現不會顯得很笨拙。
控制框架:
public class GreenhouseControls extend Controller{
private boolean light = false;
public class LightOn extends Event{
public LightOn(long delay){super(delay);}
public void action(){
//hardware control code to turn on light
light = true;
}
}
public class LightOff extends Event{..}
private boolean water = false;
public class WaterOn extends Event{
public WaterOn(long delay){super(delay);}
public void action(){
//..
water = true;
}
}
//...其餘內部類
}
控制框架中用內部類把實現細節都隱藏起來了。
那麼如何使用這個框架並執行須要的事件呢:
public static void main(String[] args){
GreenhouseControls gc = new GreenhouseControls();
gc.addEvent(gc.new LightOn(100));
gc.addEvent(gc.new LightOff(200));
...
gc.run();
}
控制框架能夠體現出內部類的價值,圖形界面設計裏更是讓人信服。
10.9內部類的繼承
內部類的構造器必須鏈接到外圍類對象的引用,若是一個類繼承了內部類,那麼如何保證外圍類對象存在呢?須要使用特殊語法:
1.在導出類添加一個帶外圍類參數的構造器
2.構造器裏用outer.super()來提供外圍類引用
例如:
class Outer{
class Inner{}
}
public class InheritInner extends Outer.Inner{
InheritInner(Outer o){
o.super();
}
}
10.10內部類能夠被覆蓋嗎
class Outer{
class Inner{}
}
class Outer2 extends Outer{
class Inner{}
}
在Outer裏調用Inner並不會被Outer2裏的Inner覆蓋,即不會產生多態特性。
10.11局部內部類
在方法體裏使用局部內部類和使用匿名內部類有什麼區別呢?
惟一的區別在於:局部內部類能夠有命名的構造器,而且能夠被重載用來初始化,能夠產生多個對象。而匿名內部類只能提供一個該類型的對象。
10.12內部類標識符
匿名內部類: Outer類名 $ 數字
成員內部類: Outer類名 $ 內部類名
局部內部類: Outer類名 $ 數字 內部類名
10.13總結
接口和內部類實現了C++的多重繼承功能,可是更優雅,更簡潔。