內部類 ———定義在類的內部的類
為什么需要內部類?
典型的情況是,內部類繼承自某個類或實現某個接口,內部類的代碼操作創建其的外圍類的對象。所以你可以認為內部類提供了某種進入其外圍類的窗口。
java中的內部類和接口加在一起,可以實現多繼承。
可以使某些編碼根簡潔。
隱藏你不想讓別人知道的操作。
使用內部類最吸引人的原因是:
每個內部類都能獨立地繼承自一個(接口的)實現,所以無論外圍類是否已經繼承了某個(接口的)實現,對於內部類都沒有影響。如果沒有內部類提供的可以繼承多個具體的或抽象的類的能力,一些設計與編程問題就很難解決。從這個角度看,內部類使得多重繼承的解決方案變得完整。接口解決了部分問題,而內部類有效地實現了“多重繼承”。
內部類分為: 成員內部類、靜態嵌套類、方法內部類、匿名內部類。
特點:
一、內部類仍然是一個獨立的類,在編譯之后內部類會被編譯成獨立的.class文件,但是前面冠以外部類的類命和$符號。
二、內部類可以直接或利用引用訪問外部類的屬性和方法,包括私有屬性和方法(但靜態內部類不能訪問外部類的非靜態成員變量和方法)。內部類所訪問的外部屬性的值由構造時的外部類對象決定。
三、而外部類要訪問內部類的成員,則只能通過引用的方式進行,可問內部類所有成員
四、訪問機制:
System.out.println(this.x);或System.out.println(x);//內部類訪問內部類的成員變量或成員方法可用此方法。
System.out.println(OuterClass.this.x);//內部類訪問外部類的同名變量時可用此方法,如果沒有同名可用System.out.println(x);
五、內部類可以使用任意的范圍限定:public/private/protected class InnerClass,且嚴格按照這幾種訪問權限來控制內部類能使用的范圍。普通類的范圍限定只可以是public或者不加。
六、內部類的命名不允許與外部類 重名,內部類可以繼承同級的內部類,也可繼承其它類(除內部類和外部類)。
七、內部類可以定義為接口,並且可以定義另外一個類來實現它
八、內部類可以定義為抽象類,可以定義另外一個內部類繼承它
九、內部類使用static修飾,自動升級為頂級類,外部類不可以用static修飾,用OuterClass.InnerClass inner=new OuterClass.InnerClass();創建實例。內部類還可定義為final.
十、內部類可以再定義內部類(基本不用)
十一、方法內的內部類:
方法內的內部類不能加范圍限定(protected public private)
方法內的內部類不能加static修飾符
方法內的內部類只能在方法內構建其實例
方法內的內部類如果訪問方法局部變量,則此局部變量必須使用final修飾
1)靜態內部類(靜態嵌套類)
從技術上講,靜態嵌套類不屬於內部類。因為內部類與外部類共享一種特殊關系,更確切地說是對實例的共享關系。而靜態嵌套類則沒有上述關系。它只是位置在另一個類的內部,因此也被稱為頂級嵌套類。
靜態的含義是該內部類可以像其他靜態成員一樣,沒有外部類對象時,也能夠訪問它。靜態嵌套類不能訪問外部類的成員和方法。
語法
1 package com.tarena.day13; 2 3 import com.tarena.day13.Foo.Koo; 4 /** 5 * 靜態類內部語法演示 6 */ 7 public class StaticInner { 8 public static void main(String[] args) { 9 Koo koo = new Koo(); 10 System.out.println(koo.add());//4 11 } 12 13 } 14 class Foo{ 15 int a = 1; 16 static int b = 3; 17 /** 靜態內部類,作用域類似於靜態變量,屬於類的 */ 18 static class Koo{ 19 public int add(){ 20 //a ,不能訪問a 21 return b+1; 22 } 23 } 24 }
2)成員內部類
* 1 成員內部類必須利用外部類實例創建
* 2 成員內部類可以共享外部類的實例變量
1 import com.tarena.day13.inn.Goo.Moo; 2 3 public class InnerClassDemo { 4 public static void main(String[] args) { 5 //Moo moo = new Moo(); //編譯錯誤,必須創建Goo的實例 6 Goo goo = new Goo(); 7 Moo moo = goo.new Moo();//利用goo實例創建Moo實例 8 Moo moo1 = goo.new Moo(); 9 //moo和moo1共享同一個goo實例的實例變量 10 System.out.println(moo.add());//2 11 System.out.println(moo1.add());//2 12 Goo goo1 = new Goo(); 13 goo1.a = 8; 14 Moo m1 = goo1.new Moo(); 15 Moo m2 = goo1.new Moo(); 16 System.out.println(m1.add());//9 17 System.out.println(m2.add());//9 18 19 } 20 } 21 class Goo{ 22 int a = 1; 23 /**成員內部類*/ 24 class Moo{ 25 public int add(){ 26 return a+1; 27 } 28 } 29 }
3)局部內部類(方法內部類)
(1)、方法內部類只能在定義該內部類的方法內實例化,不可以在此方法外對其實例化。
(2)、方法內部類對象不能使用該內部類所在方法的非final局部變量。
因為方法的局部變量位於棧上,只存在於該方法的生命期內。當一個方法結束,其棧結構被刪除,局部變量成為歷史。但是該方法結束之后,在方法內創建的內部類對象可能仍然存在於堆中!例如,如果對它的引用被傳遞到其他某些代碼,並存儲在一個成員變量內。正因為不能保證局部變量的存活期和方法內部類對象的一樣長,所以內部類對象不能使用它們。用法
1 package com.tarena.day13.inn; 2 3 import java.util.Comparator; 4 5 /** 6 * 局部內部類 7 */ 8 public class LocalInnerClassDemo { 9 public static void main(String[] args) { 10 int a = 5; 11 final int b = 5; 12 //局部內部類,定義在方法內部,作用域類似於局部變量 13 //僅僅在方法內部可見 14 //在局部內部類中可以訪問方法中的局部final變量 15 class Foo{ 16 public int add(){ 17 return b;//正確 18 //return a;//編譯錯誤 19 } 20 } 21 22 Foo foo = new Foo(); 23 //臨時的自定義比較規則 24 class ByLength implements Comparator<String>{ 25 public int compare(String o1,String o2){ 26 return o1.length()-o2.length(); 27 } 28 } 29 } 30 31 }
4)匿名內部類
顧名思義,沒有名字的內部類。表面上看起來它們似乎有名字,實際那不是它們的名字。
匿名內部類就是沒有名字的內部類。什么情況下需要使用匿名內部類?如果滿足下面的一些條件,使用匿名內部類是比較合適的:
·類在定義后馬上用到。
·類非常小(SUN推薦是在4行代碼以下)
·給類命名並不會導致你的代碼更容易被理解
在使用匿名內部類時,要記住以下幾個原則:
·匿名內部類不能有構造方法。
·匿名內部類不能定義任何靜態成員、方法和類。
·匿名內部類不能是public,protected,private,static。
·只能創建匿名內部類的一個實例。
·一個匿名內部類一定是在new的后面,用其隱含實現一個接口或實現一個類。
·因匿名內部類為局部內部類,所以局部內部類的所有限制都對其生效。
A、繼承式的匿名內部類和接口式的匿名內部類。
1 import java.util.Arrays; 2 import java.util.Comparator; 3 4 /**匿名內部類 語法*/ 5 public class AnnInnerClass { 6 7 public static void main(String[] args) { 8 // TODO Auto-generated method stub 9 Yoo yoo = new Yoo();//創建Yoo的實例 10 Yoo y1 = new Yoo(){}; 11 //new Yoo(){}創建匿名類實例 12 //匿名類new Yoo(){}是繼承Yoo類,並且同時創建了對象 13 //new Yoo(){}是Yoo的子類型,其中{}是類體(class Body) 14 //類體中可以定義任何類內的語法,如:屬性,方法,方法重載,方法覆蓋,等 15 //子類型沒有名字,所以叫匿名類! 16 Yoo y2 = new Yoo(){ 17 public String toString(){//方法重寫(覆蓋) 18 return "y2"; //y2是子類的實例 19 } 20 }; 21 System.out.println(y2);//"y2",調用了匿名類對象toString() 22 //匿名內部類可以繼承/實現 於 類,抽象類,接口等 23 //按照繼承的語法,子類型必須實現所有的抽象方法 24 25 //Xoo x = new Xoo(){};//編譯錯誤,沒有實現方法 26 final int b = 5; 27 Xoo xoo = new Xoo(){ //是實現接口,並且創建匿名類實例,不是創建接口對象 28 public int add(int a){//實現接口中的抽象方法 29 return a+b; //要訪問局部變量b,只能訪問final變量 30 } 31 }; 32 System.out.println(xoo.add(5));//10,調用對象的方法 33 //Comparator接口也可以使用匿名類的方式 34 Comparator<String> byLength = new Comparator<String>(){ 35 public int compare(String o1,String o2){ 36 return o1.length()-o2.length(); 37 38 } 39 }; 40 String[] names = {"Andy","Tom","Jerry"}; 41 Arrays.sort(names,byLength); 42 System.out.println(Arrays.toString(names)); 43 //也可以這樣寫,工作中常用 44 Arrays.sort(names,new Comparator<String>(){ 45 public int compare(String o1,String o2){ 46 return o1.length()-o2.length(); 47 } 48 }); 49 } 50 51 }
接口式的匿名內部類是實現了一個接口的匿名類。而且只能實現一個接口。
B。參數式的匿名內部類。
1 class Bar{ 2 void doStuff(Foo f){ 3 } 4 } 5 interface Foo{ 6 void foo(); 7 } 8 class Test{ 9 static void go(){ 10 Bar b = new Bar(); 11 b.doStuff(new Foo(){ 12 public void foo(){ 13 System.out.println("foofy"); 14 } 15 }); 16 } 17 }
構造內部類對象的方法有:
1、內部類在自己所處的外部類的靜態方法內構建對象或在另一個類里構造對象時應用如下形式:
(1)OuterClass out = new OuterClass();
OuterClass.InnerClass in = out.new InnerClass();
(2)OuterClass.InnerClass in=new OuterClass().new InnerClass();
其中OuterClass是外部類,InnerClass是內部類。
2、內部類在它所在的外部類的非靜態方法里或定義為外部類的成員變量時,則可用以下方式來構造對象:
InnerClass in = new InnerClass();
3、如果內部類為靜態類,則可用如下形式來構造函數:
OuterClass.InnerClass in = new OuterClass.InnerClass();
無需再利用外部類的對象來來構造內部類對象,如果靜態內部類需要在靜態方法或其它類中構造對象就必須用上面的方式來初始化。