Java允許在一個(gè)類里面定義另一個(gè)類欢策,類里面的類就是內(nèi)部類。內(nèi)部類看似簡單关串,其實(shí)里面大有乾坤拧廊,下面我們就來好好聊一聊內(nèi)部類。代碼示例在最下面晋修。
初識(shí)內(nèi)部類
內(nèi)部類作用及其一些共性和特點(diǎn):
使用內(nèi)部類最吸引人的原因是:每個(gè)內(nèi)部類都能獨(dú)立地實(shí)現(xiàn)某一接口吧碾,所以無論外部類是否已經(jīng)實(shí)現(xiàn)了某個(gè)接口,對(duì)于內(nèi)部類都沒有影響墓卦。接口和內(nèi)部類配合使用倦春,使得多繼承的解決方案變得更加完整。
① 我們都知道類一般都不聲明成private和protected落剪,但是內(nèi)部類可以睁本,所以通過內(nèi)部類可以很好地隱藏我們的信息。
② 內(nèi)部類它可以直接訪問外部類的成員變量和方法(甚至是私有的)忠怖,利用這個(gè)特性呢堰,配合接口,我們可以更好地實(shí)現(xiàn)多繼承的效果凡泣。
③ 內(nèi)部類聲明成靜態(tài)的枉疼,就不能隨便訪問外部類的成員數(shù)據(jù)了,此時(shí)內(nèi)部類只能訪問外部類的靜態(tài)成員數(shù)據(jù)鞋拟。
④ 在編譯成功之后骂维,它就與外部類是不同的類,是一個(gè)獨(dú)立的類贺纲,當(dāng)然他們之間還是有聯(lián)系的航闺。在編譯之后內(nèi)部類會(huì)被編譯成獨(dú)立的.class文件,前面冠以外部類的類名和$符號(hào)哮笆。
在這里我要詳細(xì)解釋下上面的第二點(diǎn)和第三點(diǎn):
靜態(tài)內(nèi)部類雖然定義在外部類的里面来颤, 但是它只是在形式上(寫法上)和外部類有關(guān)系,其實(shí)在邏輯上和外部類并沒有直接的關(guān)系稠肘,它并不依賴外部類福铅。雖然它也能訪問外部類的靜態(tài)數(shù)據(jù),這是因?yàn)樵诰幾g的時(shí)候项阴,就已經(jīng)做到數(shù)據(jù)共享了滑黔。
而一般的內(nèi)部類,不僅在形式上和外部類有關(guān)系(寫在外部類的里面)环揽, 在邏輯上也和外部類有聯(lián)系略荡。這種邏輯關(guān)系主要表現(xiàn)在:內(nèi)部類對(duì)象的創(chuàng)建依賴于外部類對(duì)象,內(nèi)部類對(duì)象持有指向外部類對(duì)象的引用歉胶。
至于為什么會(huì)持有外部類對(duì)象的引用汛兜,以后會(huì)再專門拿出篇幅進(jìn)行說明,這里就先不做介紹了通今。
根據(jù)不同的區(qū)分方法粥谬,總的來說可以分為成員內(nèi)部類(普通內(nèi)部類)、靜態(tài)內(nèi)部類辫塌、局部內(nèi)部類漏策、匿名內(nèi)部類。
一臼氨、成員內(nèi)部類
概念:
成員內(nèi)部類是跟外部類的成員變量和方法同級(jí)的內(nèi)部類掺喻。成員內(nèi)部類的修飾詞跟外部類的成員變量及方法的權(quán)限修飾詞的作用是一樣的。
用法特征:
① 成員內(nèi)部類與外部類的實(shí)例相聯(lián)系储矩,可以訪問外部類的所有成員數(shù)據(jù)(包括外部類中的 private成員)感耙。正因?yàn)槌蓡T內(nèi)部類與外部類的實(shí)例聯(lián)系,因此不能在其內(nèi)部定義靜態(tài)成員變量椰苟。
② 非靜態(tài)內(nèi)部類的創(chuàng)建需要依賴于外部類抑月。
二、靜態(tài)內(nèi)部類
概念:
跟成員內(nèi)部類不同的是:他是由static來修的類舆蝴,叫做靜態(tài)內(nèi)部類谦絮。
用法特征:
靜態(tài)內(nèi)部類與非靜態(tài)內(nèi)部類之間存在一個(gè)最大的區(qū)別,我們知道非靜態(tài)內(nèi)部類在編譯完成之后會(huì)隱含地保存著一個(gè)引用洁仗,該引用是指向創(chuàng)建它的外部類层皱,但是靜態(tài)內(nèi)部類卻沒有。沒有這個(gè)引用就意味著:
① 靜態(tài)內(nèi)部類不能直接訪問外部類的非靜態(tài)成員赠潦,靜態(tài)內(nèi)部類可以直接訪問外部類的靜態(tài)成員數(shù)據(jù)叫胖。
③ 靜態(tài)內(nèi)部類可以直接創(chuàng)建實(shí)例,不需要依賴于外部類她奥。
三瓮增、局部內(nèi)部類
概念:
即在方法中定義的內(nèi)部類怎棱,與局部變量類似,其范圍為定義它的代碼塊绷跑。
用法特征:
① 局部內(nèi)部類可以訪問外部類的所有成員數(shù)據(jù)和方法內(nèi)的數(shù)據(jù)拳恋。
② 局部內(nèi)部類訪問局部變量和形參時(shí),局部變量和形參必須修飾為final砸捏。
③ 局部內(nèi)部類和普通內(nèi)部類是相似的谬运,因?yàn)樗麄兌疾荒芏x靜態(tài)成員數(shù)據(jù)(靜態(tài)常量除外)。如果局部內(nèi)部類在靜態(tài)方法內(nèi)被定義垦藏,那么這個(gè)局部內(nèi)部類就只能訪問方法的靜態(tài)成員梆暖。
④ 不能在局部內(nèi)部類中聲明接口,因?yàn)榻涌诒旧砭褪庆o態(tài)的掂骏。
⑤ 局部內(nèi)部類只能在定義該內(nèi)部類的方法內(nèi)實(shí)例化轰驳,不可以在此方法外對(duì)其實(shí)例化。
在這里解釋下上面的第二點(diǎn):
局部內(nèi)部類訪問局部變量和形參時(shí)芭挽,局部變量和形參必須修飾為final滑废。這是因?yàn)榉椒ǖ木植孔兞课挥跅I希淮嬖谟谠摲椒ǖ纳趦?nèi)袜爪。當(dāng)一個(gè)方法結(jié)束蠕趁,其棧結(jié)構(gòu)被刪除,局部變量成為歷史辛馆。但是該方法結(jié)束之后俺陋,在方法內(nèi)創(chuàng)建的內(nèi)部類對(duì)象可能仍然存在于堆中!正因?yàn)椴荒鼙WC局部變量的存活期和方法內(nèi)部類對(duì)象的一樣長昙篙,所以內(nèi)部類對(duì)象不能使用它們腊状。
在Java SE8中,不再需要這樣了苔可,只要局部變量和形參不被二次賦值即可缴挖。
四、匿名內(nèi)部類
概念:
沒有名字的內(nèi)部類焚辅。形式參考代碼示例映屋。
語法:
new 實(shí)現(xiàn)接口()|父類構(gòu)造器(實(shí)參列表)
{
//匿名內(nèi)部類的實(shí)體部分
}
用法特征:
① 匿名內(nèi)部類特別適合于只使用一次的類。
② 使用匿名內(nèi)部類時(shí)同蜻,我們必須是繼承一個(gè)類或者實(shí)現(xiàn)一個(gè)接口棚点,但是兩者不可兼得,同時(shí)也只能繼承一個(gè)類或者實(shí)現(xiàn)一個(gè)接口湾蔓。
③ 匿名內(nèi)部類內(nèi)部不能有構(gòu)造方法瘫析。
④ 匿名內(nèi)部類中不能存在任何的靜態(tài)成員變量和靜態(tài)方法。
⑤ 匿名類和局部內(nèi)部類一樣,可以訪問外部類的成員(必須是final或沒有二次賦值的成員)贬循。
java8以前咸包,Java要求被局部內(nèi)部類,匿名內(nèi)部類訪問的局部變量必須使用final修飾杖虾,java8以后诉儒,這個(gè)限制取消了!
應(yīng)用場(chǎng)景及注意事項(xiàng):
當(dāng)我們只需要類的一個(gè)實(shí)例亏掀,且類在定義以后會(huì)馬上被用到,代碼量少泛释,寫上匿名內(nèi)部類會(huì)優(yōu)化程序結(jié)構(gòu)滤愕,并符合以上的那些用法特征的時(shí)候,可以考慮用匿名內(nèi)部類怜校。
對(duì)于匿名內(nèi)部類的使用它是存在一個(gè)缺陷的间影,就是它僅能被使用一次,創(chuàng)建匿名內(nèi)部類時(shí)它會(huì)立即創(chuàng)建一個(gè)該類的實(shí)例茄茁,該類的定義會(huì)立即消失魂贬,所以匿名內(nèi)部類是不能夠被重復(fù)使用。
匿名內(nèi)部類是唯一一種沒有構(gòu)造器的類裙顽。正因?yàn)槠錄]有構(gòu)造器付燥,所以匿名內(nèi)部類的使用范圍非常有限,大部分匿名內(nèi)部類用于接口回調(diào)愈犹。一般來說键科,匿名內(nèi)部類用于繼承其他類或是實(shí)現(xiàn)接口,并不需要增加額外的方法漩怎,只是對(duì)繼承方法的實(shí)現(xiàn)或是重寫勋颖。
有一點(diǎn)需要注意的是,匿名內(nèi)部類由于沒有名字勋锤,所以它沒有構(gòu)造函數(shù)(但是如果這個(gè)匿名內(nèi)部類繼承了一個(gè)只含有帶參數(shù)構(gòu)造函數(shù)的父類饭玲,創(chuàng)建它的時(shí)候必須帶上這些參數(shù),并在實(shí)現(xiàn)的過程中使用super關(guān)鍵字調(diào)用相應(yīng)的內(nèi)容)叁执。如果你想要初始化它的成員變量茄厘,有下面幾種方法:
① 如果是在一個(gè)方法的匿名內(nèi)部類,可以利用這個(gè)方法傳進(jìn)你想要的參數(shù)徒恋。
② 將匿名內(nèi)部類改造成有名字的局部內(nèi)部類蚕断,這樣它就可以擁有構(gòu)造函數(shù)了。
③ 在這個(gè)匿名內(nèi)部類中使用初始化代碼塊入挣。
五亿乳、代碼示例
//OuterClass類
public class OuterClass {
private static String outerStaticStr;
private int outerInt;
// 普通方法
public void outerDisplay(){
System.out.println("OuterClass outerDisplay Method");
}
// 靜態(tài)方法
public static void outerStaticDisplay(){
System.out.println("OuterClass outerStaticDisplay Method");
}
/*成員內(nèi)部類*/
public class InnerClass{
public String innerStr; //成員內(nèi)部類不能聲明靜態(tài)變量
static final int innerInt = 100; // 靜態(tài)常量
public void innerDisplay(){
outerStaticStr = "Tom"; // 使用外部類的成員變量
System.out.println("成員內(nèi)部類 " + outerStaticStr);
outerDisplay();// 使用外部類的方法
outerStaticDisplay();
}
}
/*靜態(tài)內(nèi)部類*/
public static class StaticInnerClass {
private String innerStr;
public static String innerStaticStr; // 與成員內(nèi)部類不同,靜態(tài)內(nèi)部類可以聲明靜態(tài)變量
public void innerDisplay() {
//靜態(tài)內(nèi)部類可以直接訪問外部類的靜態(tài)成員數(shù)據(jù)。
outerStaticStr = "Jerry";
System.out.println("靜態(tài)內(nèi)部類 " + outerStaticStr);
outerStaticDisplay();
}
}
/*局部內(nèi)部類*/
public void outPut(String string) {
int a = 20;
outerInt = 999;
class LocalInnerClass { // 此時(shí)局部內(nèi)部類與局部變量同一等級(jí)葛假,局部不能加private等權(quán)限訪問修飾詞修飾障陶。
static final int b = 20; //靜態(tài)常量
public void localOutPut() {
//string = "局部內(nèi)部類"; //不能再賦值
//a = 30; //不能再賦值
System.out.println(string);
System.out.println(outerInt);
}
}
// 只能在方法內(nèi)實(shí)例化
LocalInnerClass localInner = new LocalInnerClass();
localInner.localOutPut();
}
/*匿名內(nèi)部類*/
public void test(Person per){
System.out.println(per.getName() + "今天走了" + per.walk() + "米。");
}
public InnerClass getInnerClass(){
return new InnerClass();
}
public StaticInnerClass getStaticInnerClass(){
return new StaticInnerClass();
}
}
public class Main {
public static void main(String[] args) {
OuterClass outer = new OuterClass();
//OuterClass.InnerClass inner = outer.getInnerClass();
OuterClass.InnerClass inner = new OuterClass().new InnerClass();// 非靜態(tài)內(nèi)部類的創(chuàng)建需要依賴于外部類聊训。
inner.innerDisplay();
OuterClass.StaticInnerClass staticInner = outer.getStaticInnerClass();
staticInner.innerDisplay();
outer.outPut("局部內(nèi)部類");
/*匿名內(nèi)部類*/
outer.test(new Person() {
public int walk() { // 實(shí)現(xiàn)抽象方法
return 500;
}
public String getName() { // 重寫方法
return "小明";
}
});
/*
* test()方法接受一個(gè)Person類型的參數(shù)抱究,同時(shí)我們知道一個(gè)抽象類是沒有辦法直接new的。
* 我們必須要先有實(shí)現(xiàn)類才能new出來它的實(shí)現(xiàn)類實(shí)例带斑。
* 所以在方法中直接使用匿名內(nèi)部類來創(chuàng)建一個(gè)Person實(shí)例鼓寺。
* 由于匿名內(nèi)部類不能是抽象類,所以它必須要實(shí)現(xiàn)它的抽象父類或者接口里面所有的抽象方法勋磕。
* */
}
}
public abstract class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract int walk();
}
寫完嘍妈候!ㄟ(▔,▔)ㄏㄟ(▔,▔)ㄏㄟ(▔,▔)ㄏ
知識(shí)重在總結(jié)和梳理,只有不斷地去學(xué)習(xí)并運(yùn)用挂滓,才能化為自己的東西苦银。當(dāng)你能為別人講明白的時(shí)候,說明自己已經(jīng)掌握了赶站。
歡迎轉(zhuǎn)載幔虏,轉(zhuǎn)載請(qǐng)注明出處!
如果有錯(cuò)誤的地方贝椿,或者有您的見解想括,還請(qǐng)不嗇賜教!
喜歡的話烙博,麻煩點(diǎn)個(gè)贊主胧!
本文部分參考了:http://blog.csdn.net/chenssy/article/details/13170015