能夠將一個類的定義放在另外一個類的定義內部,這就是內部類。
使用內部類的的緣由主要有三點:java
public class TalkingClock { private int interval; private boolean beep; public TalkingClock(int interval, boolean beep) {...} public void start() {...} public class TimePrinter implements ActionListener { Date now = new Date(); System.out.println("At the tone, this time is" + now); if (beep) Toolkit.getDefaultToolkit().beep(); } }
內部類既能夠訪問自身的數據域,也能夠訪問建立它的外圍類對象的數據域。安全
外圍類的引用在構造器中設置。編譯器修改了全部的內部類的構造器,添加一個外圍類引用的參數。由於TimePrinter類沒有定義構造器,因此編譯器爲這個類生成了一個默認的構造器,其代碼以下:閉包
public TimePrinter(TalkingClock clock) { outer = clock; }
請再注意一下,outer不是Java的關鍵字。咱們只是用它說明內部類中的機制。
當在start方法中建立了TimePrinter對象後,編輯器就會將this引用傳遞給當前的語音始終的構造器。app
ActionListener listener = new TimePinter(this); //parameter automatically added
使用外圍類引用的正規語法以下。表達式:
OuterClass.this
表示外圍類引用。例如,能夠像下面這樣編寫TimePrinter內部類的actionPerformed方法:編輯器
public void actionPerformed(ActionEvent event) { if (TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep(); }
反過來,能夠採用下列語法格式更加明確地編寫內部對象的構造器:ide
outerClass.new InnerClass(construction parameters) 例如 Actionlistner listener = this.new TimePrinter();
在這裏,最新構造的TimePrinter對象的外圍類引用被設置爲建立內部類對象的方法中的this引用。這是一種最多見的狀況。一般,this限定詞是多餘的。不過,能夠經過顯式地命名將外圍類引用設置爲其餘的對象。例如,若是TimePrinter是一個共有內部類,對於任意的語音時鐘均可以構造一個TimePrinter:函數
TalkingClock jabberer = new TalkingClock(1000, true); Talking.TimePrinter listener = jabber.new TimePrinter();
須要注意,在外圍類的做用域以外,能夠這樣引用內部類:
OuterClass.InnerClassthis
要想直接建立內部類的對象,你不能按照你想象的方式,去引用外部類的名字,而必須使用外部類的對象來建立該內部類對象。在擁有外部類對象以前是不可能建立內部類對象的。這是由於內部類對象會暗暗地鏈接到建立它的外部類對象上。翻譯
假設TimePrinter這個類名字只在start方法中建立這個類型的對象時使用了一次,那麼能夠像下面這樣使用:code
public void start() { class TimePrinter implements ActionListner { public void actionPerformed(ActionEvent event) { Date now = new Date(); System.out.println("At the tone, the time is " + now); if (beep) Toolkit.getDefaultToolkit().beep(); } } ActionListener listener = new TimePrinter(); Timer t = new Timer(interval, listner); t.start(); }
局部類不能用public或private訪問說明符進行聲明。它的做用域被限定在聲明這個局部類的塊中。
局部類有一個優點,即對外部世界能夠徹底地隱藏起來。即便TaklingClock類中的其餘代碼也不能訪問。除start方法以外,沒有任何方法知道TimePrinter類的存在。
與其餘內部類比較,局部類還有一個優勢。它們不只可以訪問包含它們的外部類,還能夠訪問局部變量。不過,那些局部變量必須被聲明爲final。如:
public void start(int interval, final bolean beep) { class TimePrinter implements ActionListner { public void actionPerformed(ActionEvent event) { Date now = new Date(); System.out.println("At the tone, the time is " + now()); if (beep) Toolkit.getDefaultToolkit().beep(); } } }
編譯器實現內部類訪問局部變量的方式是這樣的:在內部類中爲每個要訪問的局部變量設置數據域,而後在構造函數中將這些數據域初始化爲要訪問的局部變量值。
將局部內部類的使用再深刻一步。假如只建立這個類的一個對象,就沒必要命名了。這種類被稱爲匿名內部類(annoymous inner class)
public void start(int interval, final boolean beep) { ActionListener listener = new ActionListener() { public void actionPerformed(ActionEvent event) { Date now = new Date(); System.out.println("At the tone, the time is" + now()); if (beep) Toolkit.getDefaultToolkit().beep(); } } Timer t = new Timer(interval, listner); t.start(); }
它的含義是:建立一個實現ActionListner接口的類的新對象,須要實現的方法actionPerformed定義在括號{}內。
一般的語法格式爲:
new SuperType(construction parameters) { inner class methods and data }
其中,SuperType能夠是ActionListner這樣的接口,因而內部類就要實現這個接口。SuperType也能夠是一個類,因而內部類就要擴展它。
若是你的基類須要一個有參數的構造器,應該怎麼辦:
public classs Parcel8 { public Wrapping wrapping(int x) { //Base constructor call return new Wrapping(x) { public int value() { return super.value() * 47; } }; public static void main(String[] args) { Parcel8 p = new Parcel8(); Wrapping w = p.wrapping(10); } }
只須要簡單地傳遞合適的參數給基類的構造器便可,這裏是將x傳進new Wrapping(x)。儘管Wrapping 只是一個具備具體實現的普通類,但它仍是被其導出類當作公共「接口」來使用:
public class Wrapping { private int i; public Wrapping (int x) { i = x;} public int value() { return i; } }
你會注意到, Wrapping擁有一個要求傳遞一個參數的構造器,這使得事情變的更有趣了。
在匿名類中定義定義字段時,還可以對其執行初始化操做:
public class Parcel9 { public Destination destination(final String dest) { return new Destination() { private String label = dest; public String readLabel() { return label; } }; } public static void main(String[] args) { Parcel9 p = new Parcel9(); Destination d = p.destination(「Tesmania」); } }
若是定義一個匿名內部類,而且但願它使用一個在其外部定義的對象,name編譯器會要求其參數引用是final的,就像你在destinaion()的參數中看到的那樣。若是你忘記了,將會獲得一個錯誤信息。
若是隻是簡單地給一個字段賦值,那麼此例中的方法是很好的。可是,若是想作一些相似構造器的行爲。在匿名類中不可能有命名的構造器,但經過實例初始化,就可以達到爲匿名內部類建立一個構造器的效果,就像這樣:
abstract class Base { public Base(int i) { print("Base conctructor, i=" + 1); } } public class AnonymousConstructor { public class Base getBase(int i){ return new Base(i){ print("Inside instance initializer"); public void f() { print("In anonymous f()"); } } } public static void main(String[] args) { Base base = getBase(47); base.f(); } }/* Base constructor, i = 47; Inside instance initializer In anonymous f()
在此例中,不要求變量i必定是final的。覺得i被傳遞給匿名內部類的積累的構造器,它並不會在你大家內部類被直接使用。
下例是帶實例化的「parcel」形式。注意destination()參數必須是final的,由於它們是在匿名內部類使用的:
public class Parcel10 { public Destination destination(final String dest, final float price) { return new Destination(){ private int cost; //Instance initialization for each object { cost = Math.round(price); if (cost > 100) { System.out.println("Over budget"); } } private String label = dest; public String readLabel() { return label; } }; } public static void main(String[] args) { Parcel10 p = new Parcel10(); Destination d = p.destination("Tasmania", 101.395.F); } } // Over budget;
1.使用匿名內部類時,必須繼承或者實現一個接口,可是二者不可兼得,同時也只能繼承一個類或者實現一個接口
普通的內部類對象隱式地保存了一個引用,指向建立它的外圍類對象。然而,當內部類是static的時,就不是這樣了,嵌套類意味着:
1)要建立嵌套類的對象,不須要其外圍類的對象。
2)不能從嵌套類的對象中訪問非靜態的外圍類對象。
嵌套類和普通的內部類還有一個區別。普通內部類的字段與方法,只能放在類的外部層次,因此普通的內部類不能有static數據和static字段,也不能包含嵌套類。可是嵌套類能夠包含這些東西。