內部類顧名思義:將一個類定義在另外一個類裏面或者一個方法裏面,這樣的類稱爲內部類。對於不少Java初學者來講,內部類學起來真的是一頭霧水,根本理解不清楚是個什麼東西,包括我本身(我太菜了!哈哈),因此接下來我要好好地來研究一下。html
咱們來看下內部類的定義格式;java
public class OuterClass { //code class InnerClass{ //code } }
這裏的InnerClass就是一個內部類。不管在咱們的學習中仍是工做中,內部類用到的地方真的不是不少,通常都出如今源碼中,可是咱們仍是要搞懂內部類,由於後面對咱們閱讀源碼很是有幫助。並且隨着後面咱們編程能力的提升,天然而然會領悟到它的魅力所在,它可以讓咱們設計出更加優雅的程序結構。在使用內部類以前咱們須要明白爲何要使用內部類,內部類可以爲咱們帶來什麼樣的好處。編程
在《Think in java》中有這樣一句話:使用內部類最吸引人的緣由是:每一個內部類都能獨立地繼承一個(接口的)實現,因此不管外圍類是否已經繼承了某個(接口的)實現,對於內部類都沒有影響。多線程
也就是說內部類擁有類的基本特徵(能夠繼承父類,實現接口)。在咱們程序設計中有時候會存在一些使用接口很難解決的問題,這個時候咱們能夠利用內部類提供的、能夠繼承多個具體的或者抽象的類的能力來解決這些程序設計問題。能夠這樣說,接口只是解決了部分問題,而內部類使得多重繼承的解決方案變得更加完整。(注:內部類能夠嵌套內部類,可是這極大的破換了代碼的結構,這裏不推薦使用)ide
那咱們來看一下使用內部類如何進行多繼承,接口多繼承就不舉例了,由於接口自己就能夠實現多繼承。函數
1 class Father{ 2 public String handsome(){ 3 return "爸爸很帥氣"; 4 } 5 } 6 7 class Mother{ 8 public String beautiful(){ 9 return "媽媽很漂亮"; 10 } 11 } 12 13 class Son{ 14 //內部類繼承了Father類 15 class MyFather extends Father{ 16 //重寫父類方法 17 public String handsome(){ 18 return "我遺傳了爸爸的帥氣"; 19 } 20 } 21 //內部類繼承了Mother類 22 class MyMother extends Mother{ 23 //重寫父類方法 24 public String beautiful(){ 25 return "我遺傳了媽媽的漂亮"; 26 } 27 } 28 } 29 30 public class Test { 31 public static void main(String[] args) { 32 Son son=new Son(); 33 Son.MyFather myFather=son.new MyFather(); 34 System.out.println(myFather.handsome()); 35 Son.MyMother myMother=son.new MyMother(); 36 System.out.println(myMother.beautiful()); 37 } 38 }
運行結果:post
從上面的舉例代碼能夠看出,兩個內部類分別繼承了Father、Mother類,而且重寫了父類的方法,這是內部類最重要的特性:內部類能夠繼承一個與外部類無關的類,保證了內部類的獨立性,正是基於這一點,多重繼承纔會成爲可能。學習
能夠發如今建立內部類實例的時候,使用了 .new 這個特徵,與以往咱們建立實例不太相同。.new能夠這樣理解:根據外部類來建立內部類的對象實例。this
Java中內部類可分爲四種:成員內部類、局部內部類、匿名內部類、靜態內部類。下面咱們逐一介紹這四種內部類:url
成員內部類是定義在類中的類。咱們能夠把成員內部類當作是外部類的一個成員,因此成員內部類能夠無條件訪問外部類的全部成員屬性和成員方法,包括private成員和靜態成員。可是外部類要訪問內部類的成員屬性和方法則須要經過內部類實例來訪問。當成員內部類擁有和外部類同名的成員變量或者方法時,會優先訪問的是成員內部類的成員,可是咱們可使用 .this(若是有繼承可使用super)來訪問外部類的變量和方法。
在成員內部類中要注意兩點:
1 class OuterClass{ 2 private String outerName="tang_hao_outer"; 3 private int outerAge=22; 4 5 public OuterClass() { 6 } 7 8 //成員方法 9 public void outerMethod() { 10 System.out.println("我是外部類的outerMethod方法"); 11 } 12 13 //外部類靜態方法 14 public static void outerStaticMethod() { 15 System.out.println("我是外部類的outerStaticMethod靜態方法"); 16 } 17 //定義返回內部類實例的方法,推薦使用該方法來換取內部類實例 18 public InnerClass getInnerClassInstance(){ 19 return new InnerClass(); 20 } 21 22 //內部類 23 class InnerClass{ 24 private String innerName="tang_hao_Inner"; 25 private int innerAge=21; 26 27 public InnerClass() { 28 } 29 30 public void show(){ 31 //當名字和外部類同樣時,默認調用內部類的成員屬性 32 System.out.println("內部類變量:"+innerName); 33 System.out.println("內部類變量:"+innerAge); 34 //當名字和外部類同樣時,可使用 。this來調用外部類屬性 35 System.out.println("外部類變量:"+OuterClass.this.outerName); 36 System.out.println("外部類變量:"+OuterClass.this.outerAge); 37 //訪問外部類的方法 38 outerMethod(); 39 outerStaticMethod(); 40 } 41 } 42 } 43 public class Test { 44 public static void main(String[] args) { 45 //普通方法建立實例 46 OuterClass outerClass=new OuterClass(); 47 OuterClass.InnerClass innerClass=outerClass.new InnerClass(); 48 innerClass.show(); 49 System.out.println("-------------------"); 50 //調用外部類的getInnerClassInstance來建立內部類實例 51 OuterClass.InnerClass innerClassInstance = outerClass.getInnerClassInstance(); 52 innerClassInstance.show(); 53 } 54 }
運行結果:
從上面示例中,當內部類和外部類的變量和方法同樣時,咱們用了 .this來調用外部類的屬性(靜態除外,由於靜態隨類加載而加載,優於對象的建立),它能夠理解爲:產生一個指向外部類的引用。還有若是該內部類的構造函數無參數,強烈推薦使用相似getInnerClassInstance()這樣的方法來獲取成員內部類的實例對象。
局部內部類是定義在一個方法或者一個做用域裏面的類,它和成員內部類的區別在於局部內部類的訪問僅限於方法內或者該做用域內。注意:局部內部類就像是方法裏面的一個局部變量同樣,是不能有 public、protected、private 以及 static 修飾符的。
局部內部類通常都用於返回一個類或實現接口的實例。咱們用Comparable接口爲例:
1 class OuterClass{ 2 //建立返回一Comparable接口實例的方法 3 public Comparable getComparable(){ 4 //建立一個實現Comparable接口的內部類:局部內部類 5 class MyComparable implements Comparable{ 6 @Override 7 public int compareTo(Object o) { 8 return 0; 9 } 10 } 11 //返回實現Comparable接口的實例 12 return new MyComparable(); 13 } 14 }
當咱們建立外部類的實例調用getComparable()方法時,就能夠輕鬆獲取實現Comparable接口的實例了。
注意:局部內部類若是想用方法傳入形參,該形參必須使用final聲明(JDK8形參變爲隱式final聲明)。上面的例子若是是getComparable(Object o),那麼這個形參前面就隱式加了final關鍵字。
匿名內部類就是沒有名字的內部類。它與局部內部類很類似,不一樣的是它沒有類名,若是某個局部類你只須要用一次,那麼你就可使用匿名內部類。匿名內部類可使你的代碼更加簡潔,你能夠在定義一個類的同時對其進行實例化。
1 //建立一個接口 2 interface IPerson{ 3 public void eat(); 4 public void sleep(); 5 } 6 7 public class OuterClass { 8 //這裏注意,局部內部類若是須要經過方法傳入參數,該形參必須使用final聲明(JDK8形參變爲隱式final聲明) 9 //我用的JDK8,因此這裏沒有顯式的加final,可是JVM會自動加 10 public static IPerson getInnerClassInstance(String eat,String sleep){ 11 return new IPerson() { 12 @Override 13 public void eat() { 14 System.out.println(eat); 15 } 16 17 @Override 18 public void sleep() { 19 System.out.println(sleep); 20 } 21 };//這個分好要注意 22 } 23 24 public static void main(String[] args) { 25 IPerson person = OuterClass.getInnerClassInstance("吃飯", "睡覺"); 26 person.eat(); 27 person.sleep(); 28 } 29 }
運行結果:吃飯、睡覺
咱們知道在抽象類和接口中是不能被實例化的,可是在匿名內部類中咱們卻看見new了一個IPerson接口,這是怎麼回事。這是由於匿名內部類是直接使用new來生成一個對象的引用,而在new對象時,系統會自動給抽象類或接口添加一個它們的實現類,固然這個引用是隱式的,咱們看不見。咱們本身拆分出來理解一下,注意這裏是本身想出來的,運行時並不會有這些類存在:
1 class Farmer implements IPerson{ 2 3 @Override 4 public void eat() { 5 System.out.println("農民吃飯"); 6 } 7 8 @Override 9 public void sleep() { 10 System.out.println("農民睡覺"); 11 } 12 }
通常咱們建立抽象類或接口的實例是這樣的:IPerson iPerson = new Farmer();這個能夠叫作是非匿名對象非匿名類,而咱們建立的是匿名內部類,因此這個實現類不能有名字,因此只好叫父類的名字,因此就看到了前面直接new了一個接口,實際上是隱式的建立了實現類的對象。(不知道這樣講對不對,鄙人菜鳥一個,若是有什麼不對的或理解錯誤的地方,歡迎指出,虛心接受!)
匿名內部類最經常使用的狀況就是在多線程的實現上,由於要實現多線程必須繼承Thread類或是繼承Runnable接口。
在使用匿名內部類的過程當中,咱們須要注意以下幾點:
靜態內部類是指用static修飾的內部類。在前面夯實Java基礎(七)——Static關鍵字中提到了static關鍵字能夠修飾內部類。咱們知道普通類是不容許聲明爲靜態的,只要內部類才能夠,被static修飾的內部類它不依賴於外部類的實例。這是由於非靜態內部類在編譯完成以後會隱含地保存着一個引用,該引用是指向建立它的外部類。
static修飾內部類注意幾點:
簡單舉例:
1 class OuterClass{ 2 //靜態變量 3 private static int static_num=66; 4 //非靜態變量 5 private int num=99; 6 7 //靜態內部類 8 static class InnerStaticClass{ 9 public void print(){ 10 //靜態內部類只能訪問外部類的靜態變量和靜態方法 11 System.out.println("靜態內部類方法print()=="+static_num); 12 staticShow(); 13 } 14 } 15 //非靜態內部類 16 class InnerClass{ 17 public void display(){ 18 //非靜態內部類中能夠調用外部類的任何成員,不論是靜態的仍是非靜態的 19 System.out.println("外部類靜態變量=="+static_num); 20 System.out.println("外部類普通變量=="+num); 21 show(); 22 System.out.println("非靜態內部類方法display()=="+num); 23 24 } 25 } 26 public void show(){ 27 System.out.println("外部類非靜態show()方法"); 28 } 29 public static void staticShow(){ 30 System.out.println("外部類靜態staticShow()方法"); 31 } 32 } 33 public class Test { 34 public static void main(String[] args) { 35 //static對象實例 36 OuterClass.InnerStaticClass staticClass=new OuterClass.InnerStaticClass(); 37 staticClass.print(); 38 39 //非static對象實例 40 OuterClass outerClass=new OuterClass(); 41 OuterClass.InnerClass innerClass=outerClass.new InnerClass(); 42 innerClass.display(); 43 } 44 }
運行結果:
從上面的例子咱們能夠看到靜態內部類和非靜態內部類的區別。