Java語(yǔ)言理論告訴我們內(nèi)部類(lèi)對(duì)象持有外部類(lèi)對(duì)象的一個(gè)引用胁出,這說(shuō)明內(nèi)部類(lèi)與外部類(lèi)還是獨(dú)立的兩個(gè)類(lèi),只不過(guò)內(nèi)部類(lèi)對(duì)象通過(guò)持有外部類(lèi)的對(duì)象的引用來(lái)維持這個(gè)關(guān)系忆家。
通常任何一個(gè)類(lèi)都不可能訪問(wèn)另一個(gè)類(lèi)的私有成員犹菇,那么內(nèi)部類(lèi)是如何做到訪問(wèn)外部類(lèi)的私有成員的呢?
其實(shí)“持有外部類(lèi)對(duì)象的一個(gè)引用”這句話已經(jīng)給了我們提示芽卿,Java代碼中并不需要自己去聲明這么一個(gè)引用揭芍,因此是編譯器背著我們創(chuàng)建了這個(gè)引用。既然編譯器有這個(gè)習(xí)性卸例,它很可能也給外部類(lèi)的私有成員創(chuàng)建了getter方法使得內(nèi)部類(lèi)可以訪問(wèn)称杨。
下面我們用javap命令反編譯class文件來(lái)探尋這個(gè)猜想是否正確。
javap 用法:
javap class文件路徑
獲得類(lèi)摘要信息币厕,直接輸出到標(biāo)準(zhǔn)輸出列另。
第一步,驗(yàn)證內(nèi)部類(lèi)對(duì)象持有外部類(lèi)對(duì)象的引用旦装。
先定義一個(gè)外部類(lèi)和一個(gè)內(nèi)部類(lèi):
class Outer {
class Inner {
}
}
使用 javac
編譯出class文件,會(huì)生成多個(gè)class文件:Outer.class
Outer$Inner.class
摊滔。
javap Outer$Inner.class
輸出:
class Outer$Inner {
final Outer this$0; // 外部類(lèi)對(duì)象的引用
Outer$Inner(Outer); // 還生成了一個(gè)構(gòu)造函數(shù)阴绢,傳入了外部類(lèi)的引用。
}
一個(gè)有趣的問(wèn)題來(lái)了:如果Inner顯式定義了構(gòu)造函數(shù)會(huì)怎么樣艰躺?
class Outer {
class Inner {
private final String name;
public Inner(String name) {
this.name = name;
}
}
}
javap Outer$Inner.class
輸出:
class Outer$Inner {
final Outer this$0;
public Outer$Inner(Outer, java.lang.String); // 改造了顯式定義的構(gòu)造函數(shù)
第二步呻袭,驗(yàn)證外部類(lèi)生成了私有成員變量的訪問(wèn)器
先添加一個(gè)私有成員變量:
class Outer {
private final int code;
public Outer(int code) {
this.code = code;
}
class Inner {
private final String name;
public Inner(String name) {
this.name = name;
}
}
}
javap輸出:
class Outer {
public Outer(int);
}
可見(jiàn),內(nèi)部類(lèi)不訪問(wèn)外部類(lèi)私有成員變量時(shí)腺兴,并沒(méi)有隱藏的方法聲明左电。
然后,添加內(nèi)部類(lèi)對(duì)外部類(lèi)私有成員變量的訪問(wèn):
class Outer {
private final int code;
public Outer(int code) {
this.code = code;
}
class Inner {
private final String name;
public Inner(String name) {
this.name = name;
}
public void hello() {
System.out.println("Inner: hello: " + code);
}
}
}
javap Outer.class
輸出:
class Outer {
public Outer(int);
static int access$000(Outer); // 多了一個(gè)靜態(tài)方法,返回私有成員篓足。
}
再添加一個(gè)私有方法:
class Outer {
private final int code;
public Outer(int code) {
this.code = code;
}
private void sayhi(String message) {
System.out.println("Outer: hi, " + message);
}
class Inner {
private final String name;
public Inner(String name) {
this.name = name;
}
public void hello() {
System.out.println("Inner: hello: " + code);
sayhi("this is from inner");
}
}
}
javap Outer.class
輸出:
class Outer {
public Outer(int);
static int access$000(Outer);
static void access$100(Outer, java.lang.String);
}
結(jié)論:
- 內(nèi)部類(lèi)創(chuàng)建了一個(gè)外部類(lèi)對(duì)象的引用段誊,并通過(guò)改造構(gòu)造函數(shù)將其傳入內(nèi)部類(lèi)。
- 內(nèi)部類(lèi)如果不訪問(wèn)外部類(lèi)的私有成員栈拖,并不會(huì)生成訪問(wèn)方法连舍,而是需要的時(shí)候才生成。
- 外部類(lèi)生成的訪問(wèn)方法涩哟,是static類(lèi)型的索赏,并傳入外部類(lèi)對(duì)象引用,返回值與參數(shù)根據(jù)需要訪問(wèn)的變量和函數(shù)相對(duì)應(yīng)贴彼。