淺談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(內部類引數)” |
無 |