如果一個類定義在另一個類的內部,這個類就是內部類。
爲什麼要用內部類,什麼場景下需要用內部類
-
根據面向對象設計原則,需要定義一個新的類,並對相關操作和屬性進行封裝。
-
新類僅同另一個類有密切依賴關係或者邏輯關係,其它類基本不會用到它;或者這個新的類僅是爲了另一個類提供服務。
-
當發現一個類需要繼承一個以上的類時,此時需要檢查一下此類是否符合類的單一功能原則,是否應該定義新的類,是否需要定義一個內部類來繼承另外的類,同時也能實現功能需求。
-
需要對一個抽象類或者接口做具體實現,但是這個實現只有一個地方會調用他,此時沒必要專門定義一個單獨的類,可以用局部內部類或者匿名內部類。
內部類的優點
-
內部類繼承的類和外部類繼承的類不同,這樣對於外部類來說,變相的實現了多繼承功能。
-
方便將存在一定邏輯關係的類組織在一起,又可以對外界隱藏。
-
相對於直接將內部類的功能揉在外部類中——即不定義一個新的內部類——來說,定義一個內部類也能更充分利用JIT的優化功能,也更符合類的單一功能設計原則。
-
方便編寫事件驅動程序
內部類的語法規則
內部類分爲四種:成員內部、局部內部類、匿名內部類、靜態內部類。
常規內部類 / 成員內部類
可以將成員內部類當做外部類的一個成員,與外部類的實例域和成員方法是同級的,是與外部類的實例域和成員方法的地位相同的存在。因此:
同實例域一樣,成員內部類也可以聲明爲private的,也只有內部類可以聲明爲private。成員內部類的訪問控制也同外部類的實例域相同。
同成員方法類似,成員內部類中不允許定義靜態成員,即不能有靜態變量和靜態方法。
同外部類的成員方法一樣,內部類可以直接訪問其所在外部類內的其它成員,包括域和方法。可以顯示地通過outer.fieldName | outer.method()
,也可以不用outer而直接訪問。
當在另外的類中構造內部類的實例時,首先要有一個外部類的實例對象outer。
public class Test{
public static void main(String [] args){
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
}
}
public class Outer{
public class Inner{
}
}
當在外部類中調用成員內部類的方法時,需要遵循一般類的訪問控制規則,如果內部類中的成員爲private時,外部類是沒有訪問權限的。
常規內部類的對象inner會持有外圍類對象outer的引用。因爲編譯器在編譯階段會自動改造內部類的構造方法——不論內部類的構造方法是否有顯式的定義,都會對構造方法添加一個參數outer,並將此參數聲明爲改造後的構造方法的第一個參數。
而常規內部類的創建上也可以體現這一點:
public class Test{
public static void main(){
Outer outer = new Outer();
Inner inner = outer.new Inner();
outer.outerM1();
inner.innerM2();
}
}
public class Outer{
private String outerStr;
public void outerM1(){
new Inner().innerM2();
}
// 成員內部類,地位等同於所在外部類的實例域和成員方法,訪問控制符的對成員內部類的作用也與他們相同
class Inner{ //如果聲明爲private,那麼除其所在外部類外其它類無法訪問,一般情況下會定義爲private。
// 成員內部類中定義的實例域和方法,其訪問權限同普通類內的實例域和成員方法一樣。
private String innerStr;
public Inner(){
}
//可以直接訪問外部類中的實例域、直接調用外部類中的方法。
public void innerM1(){
outerM2();
}
public void innerM2(){
outerStr="outerStrByInnerM2";
}
}
}
局部內部類
在方法或者代碼塊中定義的類,如同方法內部的局部變量一樣。
其可見性也只在所在代碼塊內。
public class Outer{
public static void main(String[] args){
new Outer().outerM1();
}
public void outerM1(){
// 局部內部類
class LocalInner
{
public void innerM1(){
System.out.println(" innerM1");
}
}
// 實例化並調用方法
new LocalInner().innerM1();
}
}
匿名內部類
匿名內部類也是定義在代碼塊中的類,但是沒有顯式地聲明類的名稱。一般是對接口的實現或者類的繼承,在定義的同時會直接實例化出一個對象並調用方法。線程的創建和使用是最常見使用匿名內部類的場景。
public class Test{
public static void main(String[] args){
new Test().m1();
}
public void m1(){
new Thread(
new Runnable(){
public void run(){
try{
Thread.sleep(1000);
System.out.println("thread end");
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
).start();
}
}
靜態內部類
在內部類不需要訪問外圍類對象的時候,應該使用靜態內部類。也可以稱靜態內部類爲嵌套類(nested class)。
靜態內部類只能訪問外部類的靜態成員。
單例模式可以用靜態內部類實現。
public class Singleton{
// 利用JVM自身的類加載和初始化機制,既能確保單例,又能確保實例只在需要的時候被初始化
// 加載,鏈接(校驗、準備、解析),初始化(只在用戶程序主動調用時發生)
private static class InstanceHolder{
public static final Singleton INSTANCE = new Singleton();
}
// 構造函數私有化
private Singleton(){}
// 對外提供一個獲取單例的方法
public static Singleton getInstance(){
return InstanceHolder.INSTANCE ;
}
}