1. 程式人生 > 實用技巧 >淺談Java中的四種內部類

淺談Java中的四種內部類

  在 Java 中,可以將一個類定義在另一個類裡面或者一個方法裡面,這樣的類稱為內部類。

  廣泛意義上的內部類一般來說包括這四種:成員內部類、區域性內部類、靜態內部類和匿名內部類。

1.成員內部類

  從名字就可以看出,這種內部類是作為類的成員而存在的,其定義位於類的內部。

 1 //外部類Outer
 2 public class Outer {
 3     //外部類成員屬性
 4     private int a = 3;
 5     //建立一個內部類的例項
 6     private Inner in;
 7     public Outer(){
 8         in = new
Inner(); 9 } 10 public int getInnerA(){ 11 // 引用內部類的變數,需通過例項 12 return in.a; 13 } 14 //成員內部類Inner 15 public class Inner { 16 //內部類成員屬性 17 public int a = 2; 18 //內部類成員方法 19 public void doSomething() { 20 // 呼叫外部類的方法或屬性:外部類名.this.成員方法/屬性。
21 System.out.println(Outer.this.a);//3 22 System.out.println(a);//2 23 } 24 } 25 26 } 27 //與外部類Outer平級的類Extender繼承內部類Inner 28 class Extender extends Outer.Inner{ 29 //構造方法中需要傳入父類的例項物件 30 public Extender(Outer outer){ 31 //外部類例項名.super(內部類引數列表) 32 outer.super
(); 33 } 34 } 35 public class Test { 36 public static void main(String[] args) { 37 Outer outer=new Outer(); 38 System.out.println(outer.getInnerA());//2 39 40 //外部類名.內部類名 例項名 = 外部類例項名.new 內部類構造方法(引數) 41 //Outer.Inner inner = outer.new Inner(); 42 //外部類名.內部類名 例項名 = new 外部類構造方法(引數).new 內部類構造方法(引數) 43 Outer.Inner inner = new Outer().new Inner(); 44 inner.doSomething(); 45 46 Extender extender=new Extender(outer); 47 extender.doSomething(); 48 49 } 50 }

注意事項:  

(1)外部類無法直接訪問成員內部類的方法和屬性,需要通過內部類的一個例項來訪問

成員內部類可以無條件訪問外部類的所有成員屬性和成員方法(包括private成員和靜態成員),

訪問外部類的成員方法和屬性時使用:外部類名.this.成員方法/屬性。

(2)建立成員內部類的例項使用:

直接方法:

  ①外部類名.內部類名 例項名 = new 外部類構造方法(引數).new 內部類構造方法(引數)

  ②外部類名.內部類名 例項名 = 外部類例項名.new 內部類構造方法(引數)

   前提是已經建立了一個外部類的物件,可以理解為隱式地儲存了一個引用,指向建立它的外部類物件

間接方法:

  在外部類的方法中使用內部類,在主函式中只是呼叫外部類的方法。

(3)成員內部類中不能存在static關鍵字,即,不能宣告靜態屬性、靜態方法、靜態程式碼塊等。

注意:非靜態內部類也可以定義靜態成員但需要同時有final關鍵詞修飾,靜態方法鑑於無法用final修飾,仍必須是在靜態內部類或者非內部類中定義。

(4)與外部類平級的類繼承內部類時,其構造方法中需要傳入父類的例項物件。且在構造方法的第一句呼叫“外部類例項名.super(內部類引數)”。

(5)內部類在編譯之後生成一個單獨的class檔案,裡面包含該類的定義,所以內部類中定義的方法和變數可以跟父類的方法和變數相同。

   例如上面定義的四個類的class檔案分別是:Outer.class、Outer$Inner.class、Extender.class和Test.class四個檔案。

2.區域性內部類

  定義在程式碼塊、方法體內、作用域(使用花括號“{}”括起來的一段程式碼)內的類叫區域性內部類。

 1 public class Outer {
 2     public void methodOuter(){
 3         final int num=20;
 4         class Inner{//區域性內部類
 5             public void methodInner(){
 6                 System.out.println(num);//20
 7             }
 8         }
 9         Inner inner=new Inner();
10         inner.methodInner();
11     }
12 }
13 public class Test {
14     public static void main(String[] args) {
15         Outer obj=new Outer();
16         obj.methodOuter();
17     }
18 }

注意事項:

  (1)區域性內部類只能在程式碼程式碼塊、方法體內和作用域中使用(如建立物件和使用類物件等)

  (2)區域性內部類訪問作用域內的區域性變數,該區域性變數需要使用final修飾。

    注意:該區域性變數也可以不用final修飾(從Java8+開始),是有效final的也行(就是隻賦給區域性變數一次值並且保證不變即可)

  (3)可以使用abstract修飾,宣告為抽象類。

補充:定義一個類時,許可權修飾符規則:

   ①外部類:public、(default)

   ②成員內部類:public、protected、(default)、private

   ③區域性內部類:(default)

3.靜態內部類

  使用static修飾的成員內部類叫靜態內部類。

  與成員內部類的對比如下:

說明

成員內部類

靜態內部類

靜態成員

靜態成員需同時有final關鍵詞修飾

可以

靜態方法

不可定義

可以

訪問外部類非static屬性/方法

外部類名.this.成員方法/屬性

不可以

外部類訪問內部類

需要通過內部類的一個例項來訪問

需要通過內部類的一個例項來訪問

建立例項

外部類名.內部類名 例項名 = 外部類例項名.new 內部類構造方法(引數)

外部類名.內部類名 例項名 = new 外部類名.內部類名(引數)

編譯後的class檔案

單獨的class檔案(內部類中的方法和變數可以跟父類的方法和變數同名),外部類$內部類.class

單獨的class檔案(內部類中的方法和變數可以跟父類的方法和變數同名),外部類$內部類.class

其他

與外部類平級的類繼承內部類時,其構造方法中需要傳入父類的例項物件。且在構造方法的第一句呼叫“外部類例項名.super(內部類引數)”

4.匿名內部類

  匿名內部類定義和例項化形式如下:
1 new 父類構造方法(引數){ 
2          //注:該方法名必須在父類中已經存在 
3      修飾符 返回引數型別 方法名(引數列表){ 
4           //...
5      } 
6 }

注意事項:

  (1)只能使用一次,建立例項之後,類定義會立即消失。

  (2)必須繼承一個類(抽象的、非抽象的都可以)或者實現一個介面。如果父類(或者父介面)是抽象類,則匿名內部類必須實現其所有抽象方法。

  (3)不能是抽象類,因為匿名內部類在定義之後,會立即建立一個例項。

  (4)不能定義構造方法,匿名內部類沒有類名,無法定義構造方法,但是,匿名內部類擁有與父類相同的所有構造方法。

  (5)可以定義程式碼塊,用於例項的初始化,但是不能定義靜態程式碼塊。

  (6)可以定義新的方法和屬性(不能使用static修飾),但是無法顯式的通過“例項名.方法名(引數)”的形式呼叫,因為使用new建立的是“上轉型物件”(即父類宣告指向子類物件)。

  (7)①匿名內部類,在建立物件的時候,只能使用唯一一次。

      如果希望多次建立物件,而且類的內容一樣的話,那麼就要使用單獨定義的實現類了。

   ②匿名物件,在呼叫方法的時候,只能呼叫唯一一次。

      如果希望同一個物件,呼叫多次方法,那麼必須給物件起個名字。

   ③匿名內部類是省略了實現類/子類名稱,但是匿名物件是省略了物件名稱匿名內部類和匿名物件不是一回事)。

說明

成員內部類

匿名內部類

靜態成員

靜態成員需同時有final關鍵詞修飾

不可定義

靜態方法

不可定義

不可定義

訪問外部類非static屬性/方法

外部類名.this.成員方法/屬性

外部類名.this.成員方法/屬性

外部類訪問內部類

需要通過內部類的一個例項來訪問

需要通過內部類的一個例項來訪問

建立例項

外部類名.內部類名 例項名 = 外部類例項名.new 內部類構造方法(引數)

如上:父類 例項名 = new 父類(){}

編譯後的class檔案

單獨的class檔案(內部類中的方法和變數可以跟父類的方法和變數同名),外部類$內部類.class

單獨的class檔案,使用類$數字.class

其他

與外部類平級的類繼承內部類時,其構造方法中需要傳入父類的例項物件。且在構造方法的第一句呼叫“外部類例項名.super(內部類引數)”