能夠將一個類的定義放在另外一個類的定義內部,這就是內部類。html
內部類是一個很是有用的特性但又比較難理解使用的特性(鄙人對內部類也只是略知一二)。java
第一次見面
內部類咱們從外面看是很是容易理解的,無非就是在一個類的內部在定義一個類。編程
1 public class OuterClass { 2 private String name ; 3 private int age; 4 5 public String getName() { 6 return name; 7 } 8 9 public void setName(String name) { 10 this.name = name; 11 } 12 13 public int getAge() { 14 return age; 15 } 16 17 public void setAge(int age) { 18 this.age = age; 19 } 20 21 class InnerClass{ 22 public InnerClass(){ 23 name = "chenssy"; 24 age = 23; 25 } 26 } 27 }
在這裏InnerClass就是內部類,對於初學者來講內部類實在是使用的很少,鄙人菜鳥一個一樣沒有怎麼使用過,可是隨着編程能力的提升,咱們會領悟到它的魅力所在,它可使用可以更加優雅的設計咱們的程序結構。在使用內部類之間咱們須要明白爲何要使用內部類,內部類可以爲咱們帶來什麼樣的好處。函數
1、爲何要使用內部類
爲何要使用內部類?在《Think in java》中有這樣一句話:使用內部類最吸引人的緣由是:每一個內部類都能獨立地繼承一個(接口的)實現,因此不管外圍類是否已經繼承了某個(接口的)實現,對於內部類都沒有影響。this
在咱們程序設計中有時候會存在一些使用接口很難解決的問題,這個時候咱們能夠利用內部類提供的、能夠繼承多個具體的或者抽象的類的能力來解決這些程序設計問題。能夠這樣說,接口只是解決了部分問題,而內部類使得多重繼承的解決方案變得更加完整。編碼
1 public interface Father { 2 3 } 4 5 public interface Mother { 6 7 } 8 9 public class Son implements Father, Mother { 10 11 } 12 13 public class Daughter implements Father{ 14 15 class Mother_ implements Mother{ 16 17 } 18 }
其實對於這個實例咱們確實是看不出來使用內部類存在何種優勢,可是若是Father、Mother不是接口,而是抽象類或者具體類呢?這個時候咱們就只能使用內部類才能實現多重繼承了。url
其實使用內部類最大的優勢就在於它可以很是好的解決多重繼承的問題,可是若是咱們不須要解決多重繼承問題,那麼咱們天然可使用其餘的編碼方式,可是使用內部類還可以爲咱們帶來以下特性(摘自《Think in java》):spa
一、內部類能夠用多個實例,每一個實例都有本身的狀態信息,而且與其餘外圍對象的信息相互獨立。.net
二、在單個外圍類中,可讓多個內部類以不一樣的方式實現同一個接口,或者繼承同一個類。設計
三、建立內部類對象的時刻並不依賴於外圍類對象的建立。
四、內部類並無使人迷惑的「is-a」關係,他就是一個獨立的實體。
五、內部類提供了更好的封裝,除了該外圍類,其餘類都不能訪問。
2、內部類基礎
在這個部分主要介紹內部類如何使用外部類的屬性和方法,以及使用.this與.new。
當咱們在建立一個內部類的時候,它無形中就與外圍類有了一種聯繫,依賴於這種聯繫,它能夠無限制地訪問外圍類的元素。
1 public class OuterClass { 2 private String name ; 3 private int age; 4 5 /**省略getter和setter方法**/ 6 7 public class InnerClass{ 8 public InnerClass(){ 9 name = "chenssy"; 10 age = 23; 11 } 12 13 public void display(){ 14 System.out.println("name:" + getName() +" ;age:" + getAge()); 15 } 16 } 17 18 public static void main(String[] args) { 19 OuterClass outerClass = new OuterClass(); 20 OuterClass.InnerClass innerClass = outerClass.new InnerClass(); 21 innerClass.display(); 22 } 23 } 24 -------------- 25 Output: 26 name:chenssy ;age:23
在這個應用程序中,咱們能夠看到內部了InnerClass能夠對外圍類OuterClass的屬性進行無縫的訪問,儘管它是private修飾的。這是由於當咱們在建立某個外圍類的內部類對象時,此時內部類對象一定會捕獲一個指向那個外圍類對象的引用,只要咱們在訪問外圍類的成員時,就會用這個引用來選擇外圍類的成員。
其實在這個應用程序中咱們還看到了如何來引用內部類:引用內部類咱們須要指明這個對象的類型:OuterClasName.InnerClassName。同時若是咱們須要建立某個內部類對象,必需要利用外部類的對象經過.new來建立內部類: OuterClass.InnerClass innerClass = outerClass.new InnerClass();。
同時若是咱們須要生成對外部類對象的引用,可使用OuterClassName.this,這樣就可以產生一個正確引用外部類的引用了。固然這點實在編譯期就知曉了,沒有任何運行時的成本。
1 public class OuterClass { 2 public void display(){ 3 System.out.println("OuterClass..."); 4 } 5 6 public class InnerClass{ 7 public OuterClass getOuterClass(){ 8 return OuterClass.this; 9 } 10 } 11 12 public static void main(String[] args) { 13 OuterClass outerClass = new OuterClass(); 14 OuterClass.InnerClass innerClass = outerClass.new InnerClass(); 15 innerClass.getOuterClass().display(); 16 } 17 } 18 ------------- 19 Output: 20 OuterClass...
到這裏了咱們須要明確一點,內部類是個編譯時的概念,一旦編譯成功後,它就與外圍類屬於兩個徹底不一樣的類(固然他們之間仍是有聯繫的)。對於一個名爲OuterClass的外圍類和一個名爲InnerClass的內部類,在編譯成功後,會出現這樣兩個class文件:OuterClass.class和OuterClass$InnerClass.class。
在Java中內部類主要分爲成員內部類、局部內部類、匿名內部類、靜態內部類。
3、成員內部類
成員內部類也是最普通的內部類,它是外圍類的一個成員,因此他是能夠無限制的訪問外圍類的全部 成員屬性和方法,儘管是private的,可是外圍類要訪問內部類的成員屬性和方法則須要經過內部類實例來訪問。
在成員內部類中要注意兩點,第一:成員內部類中不能存在任何static的變量和方法;第二:成員內部類是依附於外圍類的,因此只有先建立了外圍類纔可以建立內部類。
1 public class OuterClass { 2 private String str; 3 4 public void outerDisplay(){ 5 System.out.println("outerClass..."); 6 } 7 8 public class InnerClass{ 9 public void innerDisplay(){ 10 //使用外圍內的屬性 11 str = "chenssy..."; 12 System.out.println(str); 13 //使用外圍內的方法 14 outerDisplay(); 15 } 16 } 17 18 /*推薦使用getxxx()來獲取成員內部類,尤爲是該內部類的構造函數無參數時 */ 19 public InnerClass getInnerClass(){ 20 return new InnerClass(); 21 } 22 23 public static void main(String[] args) { 24 OuterClass outer = new OuterClass(); 25 OuterClass.InnerClass inner = outer.getInnerClass(); 26 inner.innerDisplay(); 27 } 28 } 29 -------------------- 30 chenssy... 31 outerClass...
推薦使用getxxx()來獲取成員內部類,尤爲是該內部類的構造函數無參數時 。
4、局部內部類
有這樣一種內部類,它是嵌套在方法和做用於內的,對於這個類的使用主要是應用與解決比較複雜的問題,想建立一個類來輔助咱們的解決方案,到那時又不但願這個類是公共可用的,因此就產生了局部內部類,局部內部類和成員內部類同樣被編譯,只是它的做用域發生了改變,它只能在該方法和屬性中被使用,出了該方法和屬性就會失效。
對於局部內部類實在是想不出什麼好例子,因此就引用《Think in java》中的經典例子了。
定義在方法裏:
1 public class Parcel5 { 2 public Destionation destionation(String str){ 3 class PDestionation implements Destionation{ 4 private String label; 5 private PDestionation(String whereTo){ 6 label = whereTo; 7 } 8 public String readLabel(){ 9 return label; 10 } 11 } 12 return new PDestionation(str); 13 } 14 15 public static void main(String[] args) { 16 Parcel5 parcel5 = new Parcel5(); 17 Destionation d = parcel5.destionation("chenssy"); 18 } 19 }
定義在做用域內:
1 public class Parcel6 { 2 private void internalTracking(boolean b){ 3 if(b){ 4 class TrackingSlip{ 5 private String id; 6 TrackingSlip(String s) { 7 id = s; 8 } 9 String getSlip(){ 10 return id; 11 } 12 } 13 TrackingSlip ts = new TrackingSlip("chenssy"); 14 String string = ts.getSlip(); 15 } 16 } 17 18 public void track(){ 19 internalTracking(true); 20 } 21 22 public static void main(String[] args) { 23 Parcel6 parcel6 = new Parcel6(); 24 parcel6.track(); 25 } 26 }
5、匿名內部類
在作Swing編程中,咱們常用這種方式來綁定事件
1 button2.addActionListener( 2 new ActionListener(){ 3 public void actionPerformed(ActionEvent e) { 4 System.out.println("你按了按鈕二"); 5 } 6 });
咱們咋一看可能以爲很是奇怪,由於這個內部類是沒有名字的,在看以下這個例子:
1 public class OuterClass { 2 public InnerClass getInnerClass(final int num,String str2){ 3 return new InnerClass(){ 4 int number = num + 3; 5 public int getNumber(){ 6 return number; 7 } 8 }; /* 注意:分號不能省 */ 9 } 10 11 public static void main(String[] args) { 12 OuterClass out = new OuterClass(); 13 InnerClass inner = out.getInnerClass(2, "chenssy"); 14 System.out.println(inner.getNumber()); 15 } 16 } 17 18 interface InnerClass { 19 int getNumber(); 20 } 21 22 ---------------- 23 Output:
這裏咱們就須要看清幾個地方
一、 匿名內部類是沒有訪問修飾符的。
二、 new 匿名內部類,這個類首先是要存在的。若是咱們將那個InnerClass接口註釋掉,就會出現編譯出錯。
三、 注意getInnerClass()方法的形參,第一個形參是用final修飾的,而第二個卻沒有。同時咱們也發現第二個形參在匿名內部類中沒有使用過,因此當所在方法的形參須要被匿名內部類使用,那麼這個形參就必須爲final。
四、 匿名內部類是沒有構造方法的。由於它連名字都沒有何來構造方法。
PS:因爲篇幅有限,對匿名內部類就介紹到這裏,有關更多關於匿名內部類的知識,我就會在下篇博客作詳細的介紹,包括爲什麼形參要定義成final,怎麼對匿名內部類進行初始化等等,敬請期待……
6、靜態內部類
在java提升篇-----關鍵字static中提到Static能夠修飾成員變量、方法、代碼塊,其餘它還能夠修飾內部類,使用static修飾的內部類咱們稱之爲靜態內部類,不過咱們更喜歡稱之爲嵌套內部類。靜態內部類與非靜態內部類之間存在一個最大的區別,咱們知道非靜態內部類在編譯完成以後會隱含地保存着一個引用,該引用是指向建立它的外圍內,可是靜態內部類卻沒有。沒有這個引用就意味着:
一、 它的建立是不須要依賴於外圍類的。
二、 它不能使用任何外圍類的非static成員變量和方法。
1 public class OuterClass { 2 private String sex; 3 public static String name = "chenssy"; 4 5 /** 6 *靜態內部類 7 */ 8 static class InnerClass1{ 9 /* 在靜態內部類中能夠存在靜態成員 */ 10 public static String _name1 = "chenssy_static"; 11 12 public void display(){ 13 /* 14 * 靜態內部類只能訪問外圍類的靜態成員變量和方法 15 * 不能訪問外圍類的非靜態成員變量和方法 16 */ 17 System.out.println("OutClass name :" + name); 18 } 19 } 20 21 /** 22 * 非靜態內部類 23 */ 24 class InnerClass2{ 25 /* 非靜態內部類中不能存在靜態成員 */ 26 public String _name2 = "chenssy_inner"; 27 /* 非靜態內部類中能夠調用外圍類的任何成員,不論是靜態的仍是非靜態的 */ 28 public void display(){ 29 System.out.println("OuterClass name:" + name); 30 } 31 } 32 33 /** 34 * @desc 外圍類方法 35 * @author chenssy 36 * @data 2013-10-25 37 * @return void 38 */ 39 public void display(){ 40 /* 外圍類訪問靜態內部類:內部類. */ 41 System.