引言
內部類,嵌套在另一個類的里面,所以也稱為 嵌套類;
內部類分為以下四種:
- 靜態內部類
- 成員內部類
- 局部內部類
- 匿名內部類
一、靜態內部類
靜態內部類: 一般也稱”靜態嵌套類“,在類中用static
聲明的內部類。
因為是static,所以不依賴于外圍類對象實例而獨立存在,靜態內部類的可以訪問外圍類中的所有靜態成員,包括private
的靜態成員。
同時靜態內部類可以說是所有內部類中獨立性最高的內部類,其創建對象、繼承(實現接口)、擴展子類等使用方式與外圍類并沒有多大的區別。
public class OuterClass {//外圍類public int aa; //實例成員private static float f = 1.5f;//private的靜態成員static void println() {System.out.println("這是靜態方法");}protected static class StaticInnerClass{//protected的靜態內部類float a;public StaticInnerClass() {a = f;// 外圍類的private靜態變量println();//外圍類的靜態方法}}
}class OtherClass{public static void main(String[] args) {//創建靜態內部類的對象OuterClass.StaticInnerClass staticInnerClass = new OuterClass.StaticInnerClass(); }
}
二、成員內部類
成員內部類: 定義在類的內部,而且與成員方法、成員變量同級,即也是外圍類的成員之一,因此 成員內部類 與 外圍類 是緊密關聯的。
注意:
這種緊密關聯指的是,成員內部類的對象的創建必須依賴于外圍類的對象(即沒有外圍類對象,就不可能創建成員內部類)。因此,成員內部類有以下3個特點:
- 成員內部類可以訪問外圍類的所有成員,包括私有成員;
- 成員內部類是不可以聲明靜態成員(包括靜態變量、靜態方法、靜態成員類、嵌套接口),但有個例外---可以聲明
static final
的變量, 這是因為編譯器對final
類型的特殊處理,是直接將值寫入字節碼; - 成員內部類對象都隱式地保存了一個引用,指向創建它的外部類對象;或者說,成員內部類的入口是由外圍類的對象保持著(靜態內部類的入口,則直接由外圍類保持著)
成員內部類中的 this,new關鍵字:
- 獲取外部類對象:
OuterClass.this
- 明確指定使用外部類的成員(當內部類與外部類的名字沖突時):
OuterClass.this.成員名
- 創建內部類對象的new:
外圍類對象.new
//先創建外圍類對象
OuterClass outer=new OuterClass();
//創建成員內部類對象
OuterClass.InnerClass inner=outer.new InnerClass();
1. 成員內部類的對象創建
??我們知道,成員內部類就像外圍類的實例成員一樣,一定要存在對象才能訪問,即成員內部類必須綁定一個外圍類的對象。上面已經介紹了成員內部類的創建格式了,我們直接看一個例子
public class OuterClass {//外圍類public int aa; //實例成員private static float f = 1.5f;//private的靜態成員public void initInnerClass() {System.out.println("內部類的初始化方法");}public void createInnerClass() {////外圍類的成員方法中創建成員內部類對象InnerClass innerClass = new InnerClass();}class InnerClass{//成員內部類private double aa; //與圍類的變量aa的名字重復public InnerClass(){this.aa = OuterClass.this.aa + f;//明確指定兩個aa的所屬initInnerClass();}}
}//其他類
class OtherClass{public static void main(String[] args) {//其他類中創建成員內部類OuterClass oc = new OuterClass();//外部類對象//創建內部類對象OuterClass.InnerClass innerClass = oc.new InnerClass();}
}
??注意上面的例子中,在外圍類的成員方法中創建成員內部類 與 在其他類中或靜態方法中創建成員內部的方式是不一樣的。
補充幾點:
- 成員內部類可以繼續包含成員內部類,而且不管一個內部類被嵌套了多少層,它都能透明地訪問它的所有外部類所有成員;
- 成員內部可以繼續嵌套多層的成員內部類,但無法嵌套靜態內部類;靜態內部類則都可以繼續嵌套這兩種內部類。
下面的例子是基于上面的例子進行改造:
class InnerClass{//成員內部類private double aa; //與圍類的變量aa的名字重復public InnerClass(){this.aa = OuterClass.this.aa + f;//明確指定兩個aa的所屬initInnerClass();}public class InnerInnerCalss2{//成員內部類中的成員內部類protected double aa = OuterClass.this.aa;//最外層的外圍類的成員變量}//InnerInnerCalss2}//InnerClass
2. 繼承成員內部類
在內部類的訪問權限允許的情況下,成員內部類也是可以被繼承的。由于成員內部類的對象依賴于外圍類的對象,或者說,成員內部類的構造器入口由外圍類的對象把持著。因此,繼承了成員內部類的子類必須要與一個外圍類對象關聯起來。同時,子類的構造器是必須要調用父類的構造器方法,所以也只能通過父類的外圍類對象來調用父類構造器。
下面的例子也是基于上面的例子的,只貼出多出的部分代碼。
class ChildClass extends OuterClass.InnerClass{//成員內部類的子類的構造器的格式public ChildClass(OuterClass outerClass) {outerClass.super();//通過外圍類的對象調用父類的構造方法}
}
三、局部內部類
局部內部類: 就是在方法、構造器、初始化塊中聲明的類,在結構上類似于一個局部變量。因此局部內部類是不能使用訪問修飾符。
局部內部類的兩個訪問限制:
- 對于局部變量,局部內部類只能訪問
final
的局部變量。不過,后期JDK(忘了是JDK幾了)局部變量可不用final
修飾,也可以被局部內部類訪問,但你必須時刻記住此局部變量已經是final
了,不能再改變。 - 對于類的全局成員,局部內部類定義在實例環境中(構造器、對象成員方法、實例初始化塊),則可以訪問外圍類的所有成員;但如果內部類定義在靜態環境中(靜態初始化塊、靜態方法),則只能訪問外圍類的靜態成員。
public class OuterClass {private int a = 21;static {//靜態域中的局部內部類class LocalClass1{// int z = a; //錯誤,在靜態的作用域中無法訪問對象成員}}{//實例初始化塊中的局部內部類class localClass2{ }}public OuterClass(){int x = 2;final int y = 3;// x = 3;//若放開此行注釋,編譯無法通過,因為局部變量x已經是final類型//構造器中的局部內部類class localClass3{int z = y; //可以訪問final的局部變量int b = a;//可以訪問類的所有成員//訪問沒有用final修飾的局部變量int c = x;}}public void createRunnable() {final int x = 4;//方法中的局部內部類class LocalClass4 implements Runnable {//@Overridepublic void run() {System.out.println("局部final變量:"+x);System.out.println("對象成員變量:"+a);}}}
}
四、匿名內部類
匿名內部類: 與局部內部類很相似,只不過匿名內部類是一個沒有給定名字的內部類,在創建這個匿名內部類后,便會立即用來創建并返回此內部類的一個對象引用。
作用:匿名內部類用于隱式繼承某個類(重寫里面的方法或實現抽象方法)或者實現某個接口。
匿名內部類的訪問限制: 與局部內部類一樣,請參考局部內部類;
匿名內部類的優缺點:
優點: 編碼方便快捷;
缺點:
- 只能繼承一個類或實現一個接口,不能再繼承其他類或其他接口。
- 只能用于創建一次對象實例;
下面的例子是我們創建線程時經常用到的匿名內部類的方式來快速地創建一個對象的例子:
class MyOuterClass {private int x = 5;void createThread() {final int a = 10;int b = 189;// 匿名內部類繼承Thread類,并重寫Run方法Thread thread = new Thread("thread-1") {int c = x; //訪問成員變量int d = a; //final的局部變量int e = b; //訪問沒有用final修飾的局部變量@Overridepublic void run() {System.out.println("這是線程thread-1");}};// 匿名內部類實現Runnable接口Runnable r = new Runnable() {@Overridepublic void run() {System.out.println("線程運行中");}};}
}
總結
類 型 | 訪問修飾符 | 聲明靜態成員 | 綁定外圍類 |
---|---|---|---|
靜態內部類 | 四種訪問修飾符 | 可以聲明 | 不綁定 |
成員內部類 | 四種訪問修飾符 | 除 final static 的變量外,其余靜態成員都不行 | 綁定 |
局部內部類 | 不可以聲明 | 不可以聲明 | 取決于此內部類的聲明環境 |
匿名內部類 | 不可以聲明 | 不可以聲明 | 取決于此內部類的聲明環境 |