之前看項目的代碼中有使用到內部類的情況,但是不理解爲什麼要用內部類這種生僻而難以理解的語法,沒有領悟到內部類的真正意義所在,故而在這裏說下內部類的幾種形式、用途以及使用方式
內部類定義
將一個類的定義放在另一個類內部,就是內部類。跟組合不同的是,組合只是將另一個類的引用指向一個實例後放在類裏面,達到兩個類之間的關聯關係。從兩個類通信角度來說,內部類可以直接訪問外部類的所有屬性方法,而組合中的定義在類內部的其他類的實例,是不能直接訪問這個所謂的“外部類”的所有屬性方法的,除非通過該外部類定義的公有方法去操作(但這樣的話也就跟這個實例沒毛關係了)。
內部類使用
上面我們說過,內部類訪問外部類不需要任何特殊條件,擁有外部類所有的訪問權,相當於是外部類本身一樣隨便訪問。反編譯內部類的class文件可以發現,每個內部類都持有一個外部類的引用,然後以此訪問外部類的成員。如果存在相同的成員,又需要訪問外部類成員變量,則訪問方式如下:外部類類名.this.變量名,下面展示內部類和外部類之間的互相訪問:
public class OuterClass {
int outer = 1;
class InnerClass{
int outer = 2;
public int getOuter() {
return OuterClass.this.outer;
}
}
public static void main(String[] args){
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
System.out.println("OuterClass:" + outer.outer + ";InnerClass:" + inner.getOuter());
}
}
打印結果:OuterClass:1;InnerClass:1
我們都知道java是沒有C++多重繼承這個概念的,在《Java編程思想》中,作者開篇就闡述了內部類誕生的初衷,爲了解決無法多重繼承問題。通過每個內部類都獨立的繼承自一個父類(接口的)實現,當一個外部類裏面存在多個內部類,並且每個內部類都繼承或者實現了不同的外部類的時候,就達到了多重繼承的效果
內部類的分類
- 成員內部類
- 局部內部類
- 匿名內部類
- 靜態內部類
成員內部類
成員內部類也叫實例內部類。每一個外部類對象都需要一個內部類的實例,內部類離不開外部類存在
既然是成員內部類,和成員屬性成員方法地位上自然沒有什麼不同
每個外部類對象都有一個內部類對象,自然持有外部類的引用,如上面例子中定義的innerClass,能夠通過this關鍵字直接訪問外部類的成員屬性
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
局部內部類
局部內部類不能用public或者private或者protected訪問說明符,作用域被限定在了聲明這個局部內部類中了
很好理解,局部的就跟方法變量一樣,限定在了{}之中,自然就不需要設置訪問說明符了,而且你可以想下,也只有類以及類的成員有訪問修飾符,局部變量有訪問修飾符麼
局部類可以對外面完全的隱藏起來,即使是外部類的其他的代碼也不能訪問他
局部內部類雖然被限定在局部代碼塊{} 裏面,但是他也是可以訪問外部類的屬性的,不要被分類迷惑了
匿名內部類
匿名內部類就是局部內部類的進一步隱藏,局部內部類定義了之後在局部區域內仍舊可以創建多個對象
匿名內部類聲明一個類之後就只能創建一個對象了,因爲他並沒有類名字,所以匿名內部類適合那種只使用一次的情境。同時由於匿名內部類不能是抽象類,所以匿名內部類必須實現它的抽象父類或接口裏包含的所有抽象方法。如下所示:
package innerclass;
interface Product
{
public double getPrice();
public String getName();
}
public class AnonymousInnerClass
{
public void test(Product p)
{
System.out.println("購買了一個" + p.getName()
+ ",花掉了" + p.getPrice());
}
public static void main(String[] args)
{
AnonymousInnerClass ta = new AnonymousInnerClass();
// 調用test()方法時,需要傳入一個Product參數,
// 此處傳入其匿名內部類的實例
ta.test(new Product()
{
public double getPrice()
{
return 3000;
}
public String getName()
{
return "筆記本";
}
});
}
}
運行結果:購買了一個筆記本,花掉了3000.0
在Java 8之前,Java要求被局部內部類、匿名內部類訪問的局部變量必須使用final修飾,從Java 8開始這個限制取消了,Java 8更加智能:如果局部變量被匿名內部類訪問,那麼該局部變量相對於自動使用了final修飾。
package innerclass;
interface InnerInterface
{
void test();
}
public class InnerClass
{
public static void main(String[] args)
{
int i = 8; // ①
// 下面代碼i = 2;將會導致編譯錯誤
// 由於i局部變量被匿名內部類訪問了,因此i相當於被final修飾了
// i = 2;
InnerInterface a = new InnerInterface()
{
public void test()
{
// 在Java 8以前下面語句將提示錯誤:age必須使用final修飾
// 從Java 8開始,匿名內部類、局部內部類允許訪問非final的局部變量
System.out.println(i);
}
};
a.test();
}
}
這裏順嘴提一句,由於eclipse的bug,上面這段代碼在eclipse跑的時候會報編譯失敗,即使已經使用了java8也沒什麼卵用,而實際上jdk8確實是支持了內部類訪問局部變量的時候,局部變量不需要final修飾,應該是eclipse的更新跟不上jdk的緣故吧
靜態內部類
使用static修飾的內部類,則是靜態內部類。static可以修改內部類,但是不能修改外部類。
關於靜態內部類,我的理解是當一個類只被某個類使用的時候,這個類就沒必要單獨寫一個java文件來存放它,可以直接放在使用類的內部,並聲明成靜態內部類。
至於靜態內部類的使用,其實也是相當簡單,你就把他當成是外部類的一個靜態成員即可,語法如下,具體實例我就不給了
假如StaticInnerClass是內部靜態類,OuterClass是外部類:
OuterClass.StaticInnerClass inner = new OuterClass.StaticInnerClass();