定義:在一個類中再定義一個類,則將在類中定義的那個類稱為內部類。具體分為有五種:
1.成員內部類
(1)定義:在一個類中使用內部類,可以在內部類中直接存取其所在類的私有成員變量。
用法:在內部類中可以隨意使用外部類的成員方法及成員變量。
例:
1 public class OuterClass { 2 innerClass in = new innerClass(); // 在外部類實例化內部類對象引用 3 4 public void ouf() { 5 in.inf(); // 在外部類方法中調用內部類方法 6 } 7 8 class innerClass { //內部類的定義 9 innerClass() { // 內部類構造方法 10 } 11 12 public void inf() { // 內部類成員方法 13 } 14 15 int y = 0; // 定義內部類成員變量 16 } 17 18 public innerClass doit() { // 外部類方法,返回值為內部類引用 19 // y=4; //外部類不可以直接訪問內部類成員變量 20 in.y = 4;//通過內部類對象引用調用內部類成員變量 21 return new innerClass(); // 返回內部類引用 22 } 23 24 public static void main(String args[]) { 25 OuterClass out = new OuterClass(); 26 // 內部類的對象實例化操作必須在外部類或外部類中的非靜態方法中實現 27 OuterClass.innerClass in = out.doit(); 28 OuterClass.innerClass in2 = out.new innerClass(); 29 } 30 }
分析:內部類可以訪問外部類的成員,但內部類成員只能在內部類的范圍之內是可以被使用,不能被外部類使用,但可以使用內部類對象引用調用內部類成員變量。
在上面的例子中,如果不使用doit()方法返回內部類對象的引用,可以直接使用內部類實例化內部類對象,但是由於是在主方法中實例化內部類對象,必須在new操作符之前提供一個外部類的引用。如下例子中在主方法中實例化一個內部類對象:
public static void main(String [] args){ OuterClass out = new OuterClass(); OuterClass.innerClass in = out.doit(); OuterClass.innerClass in2 = out.new innerClass(); //實例化內部類對象 }
分析:上例中在實例化內部類對象時,不能再new操作符之前使用外部類名實例化內部類對象,而是應該使用外部類發的對象來創建其內部類對象。
(2)內部類向上轉型為接口
例:
1 package com.lzw; 2 interface OutInterface { // 定義一個接口 3 public void f(); 4 } 5 //主類 6 public class InterfaceInner { 7 public static void main(String args[]) { 8 OuterClass2 out = new OuterClass2(); // 實例化一個OuterClass2對象 9 // 調用doit()方法,返回一個OutInterface接口 10 OutInterface outinter = out.doit(); 11 outinter.f(); // 調用f()方法 12 } 13 } 14 15 class OuterClass2 { 16 // 定義一個內部類實現OutInterface接口 17 private class InnerClass implements OutInterface { 18 //內部類InnerClass 實現了OutIntreface接口 19 InnerClass(String s) { // 內部類構造方法 20 System.out.println(s); 21 } 22 23 public void f() { // 實現接口中的f()方法 24 System.out.println("訪問內部類中的f()方法"); 25 } 26 } 27 //OuterClass2類中的方法,返回一個外部接口類型 28 public OutInterface doit() { // 定義一個方法,返回值類型為OutInterface接口 29 return new InnerClass("訪問內部類構造方法"); 30 } 31 }
分析:從上述例子中可以看出,OuterClass2類中定義了一個修飾權限為private的內部類,這個內部類實現了接口,然后修改doit()方法,使該方法返回一個接口,由於內部類接口修飾權限為private,所以除了OuterClass2類可以訪問內部類之外,其他類都不能訪問,而可以訪問OuterClass2類中的doit()方法,由於該方法返回一個外部接口看、類型,這個接口可以作為外部使用的接口。它包含一個f()方法,在繼承此接口的內部類中實現了該方法,如果某個類繼承了外部類,由於內部類的權限不可以向下轉型為內部類InnerClass,同時也不能訪問f()方法,但可以訪問接口中的方法。
(3).使用this關鍵字獲取內部類與外部類的引用
如果在外部類與內部類中同時存在一個相同名稱的成員變量,可以使用this關鍵字
例:
public class TheSameName{ private int x; private class Inner{ public void doit(int x){ x++; // 調用形參x this.x++; //調用內部類的變量x TheSameName.this.x++; //調用外部類的變量x } } }
2.局部內部類
內部類不僅可以在類中進行定義,也可以在類的局部位置定義,如可以在類的方法或任意的作用域中均可以定義內部類。
例:
1 interface OutInterface2 { // 定義一個接口 2 } 3 class OuterClass3 { 4 public OutInterface2 doit(final String x) { // doit()方法參數為final類型 5 // 在doit()方法中定義一個內部類 6 class InnerClass2 implements OutInterface2 { 7 InnerClass2(String s) { 8 s = x; 9 System.out.println(s); 10 } 11 } 12 return new InnerClass2("doit"); 13 } 14 }
從上述代碼中可以看出,內部類被定義在了doit()內部,但有一點值得注意,內部類Innerclass2時doit()方法的一部分,並非OuterClass3類中的一部分,所以在doit()方法的外部不能訪問該類的內部類,但是該類的內部類可以訪問當前代碼塊的常量以及此外部類的所有成員。例中doit()方法的參數為final類型,如果需要在方法體中使用局部變量,該局部變量需要被設置成final類型,換句話說,在方法中定義的內部類只要能訪問方法中final類型的局部變量,這是因為在方法中定義的局部變量相當於一個常量,它的生命周期超出方法運行周期,由於該局部變量被設為final,所以不能在內部類中改變該局部變量的值。
3.匿名內部類
在上例中,在doit()方法中將return語句和內部定義語句合並在一起
例:
1 class OuterClass4{ 2 public Outinterface2 doit(){ 3 return new Outinterface2{ 4 private inti = 0; 5 public int getValue(){ 6 return i; 7 } 8 }; 9 } 10 }
在上例中,在doit()方法內部首先返回一個Outinterface的引用,然后在return 語句中插入一個定義內部類的代碼,由於這個類沒有名稱,所以這里將該內部類稱為匿名內部類,實質上這種內部類的作用就是創建一個實現於Outerface2接口的匿名類的對象。由於匿名類沒有名稱,所以匿名類使用默認構造方法來生成Outerface2對象,在匿名類內部定義結束后,需要加分號標識,這個分號並不是代表定義內部類結束的標識,而是代表創建Outinterface2引用表達式的標識。
4.靜態內部類
在內部類添加修飾符static,這個內部類就變成了靜態內部類,一個靜態內部類鍾表可以聲明static成員,但是在非靜態內部類中不可以聲明靜態成員。靜態內部類有一個最大的特點,就是不可以使用外部類的非靜態成員。即普通的內部類對象隱式的在外部保存了一個引用,指向創建它的外部類對象,但如果內部類被定義為static,就會有更多的限制,靜態內部類具有以下兩個特點:
(1)如果創建靜態內部類的對象,不需要其他外部類對象;
(2)不能從靜態內部類的對象中訪問非靜態外部類的對象。
例:
1 public class StaticinnerClass{ 2 int x = 100; 3 static class innner{ 4 void doitinner(){ 5 } 6 } 7 }
在內部類的doitInnerClass()方法中調用成員變量x,由於Inner被修飾為static形式,而成員變量x卻是非static類型,所以在doitInner()方法中不能調用x變量。
在靜態內部調用女主方法例:
1 public class StaticInnerClass{ 2 int x = 100; 3 static class inner{ 4 void doitInner(){ 5 } 6 public static void main(String []) args{ 7 System.out.println("a"); 8 } 9 } 10 }
上例中將生成一個名稱為StaticInnerClass的獨立類和一個StaticInnerClass$Inner類,只要使用java.StaticInnerClass$Inner,就可以運行主方法中的內容。
5.內部類繼承
內部類和其他類一樣可以被繼承,但是繼承內部類比繼承普通類復雜,例:
類OutputInnerClass類繼承ClassA類中的內部類ClassB.
1 public class OutputInnerClass extends ClassA.ClassB{ 2 public OutputInnerClass(ClassA a){ 3 a.super(); 4 } 5 } 6 class ClassA{ 7 class ClassB{ 8 } 9 }
在某個類繼承內部類時,必須硬性給予這個類一個帶參數的構造方法,並且該構造方法的參數為需要繼承內部類的外部類的引用,同時在構造方法中使用a.super()語句,這樣才為繼承提供了必要的對象引用。