Java梳理之理解內(nèi)部類

今天整理一下內(nèi)部類焦影,其中包含了內(nèi)部類的特殊形式诲祸,對比普通類有什么區(qū)別和作用,內(nèi)部類和外圍類之間的聯(lián)系掀序,內(nèi)部類的擴展和被擴展帆焕,內(nèi)部類編譯后文件的標識符。文章中有任何錯誤或遺漏請告知博主~

前置概念

嵌套類型:將一個類innerClass或者接口innerInterface的定義放在另一個類outterClass或者接口outterInterface定義的內(nèi)部不恭,那么類innerClass就是內(nèi)部類叶雹、outterClass則是外圍類,類似的接口innerInterface為嵌套接口换吧,接口outterInterface為外圍接口折晦。

從這里可以看出嵌套類型之間的區(qū)別取決于內(nèi)部類型是一個類還是一個接口,以及它的外圍類型是一個類還是一個接口沾瓦。內(nèi)部類型可以是靜態(tài)的满着,也可以不是:前者允許簡單的類型結(jié)構(gòu),后者定義了它和包圍類對象之間的特別關(guān)系贯莺。
例如:

class OutterClass{
    class InnerClass{
        private String name;
    }
    interface InnerInterface{
        String getName();
    }
    static class InnerClass2{
        String name;
    }
}
interface OutterInterface{
    class InnerClass{
        private String name;
        String getName(){
            return name;
        }
    }
    interface InnerInterface{
        void getName();
    }
}

在這篇文章中风喇,從最常見的類中嵌套類開始理解整個嵌套類型~

內(nèi)部類

知道了內(nèi)部類的概念之后,可以根據(jù)類的各種形式得到一些特殊的內(nèi)部類缕探,其中包括魂莫,靜態(tài)內(nèi)部類,局部內(nèi)部類爹耗,匿名內(nèi)部類豁鲤。在對整個內(nèi)部類應(yīng)該有個大體的認識后,在這里拋出疑問鲸沮,為什么需要內(nèi)部類琳骡,僅僅是因為定義在另一個類的內(nèi)部么?
其實問題在《Thinking in Java》中有早有說明:每個內(nèi)部類都能獨立地繼承自一個(接口的)實現(xiàn)讼溺,所以無論外圍類是否已經(jīng)繼承了某個(接口的)實現(xiàn)楣号,對于內(nèi)部類來說都沒有影響。這句話說明了它可以提供一個功能,即炫狱,使用內(nèi)部類后藻懒,外圍類可以得到類似于多繼承的能力。但不僅于此视译,使用內(nèi)部類還可以獲得其他一些特性:

1.內(nèi)部類可以有多個實例嬉荆,每個實例都有自己的狀態(tài)信息,并且與外圍類的對象信息相互獨立酷含。
2.在單個外圍類中鄙早,可以用多個內(nèi)部類以不同的形式實現(xiàn)同一個接口或繼承同一個類。
3.內(nèi)部類屬于外圍類的一個輕量級可選組件椅亚。
4.內(nèi)部類提供了更好的封裝效果限番,只能依靠外圍類來訪問。
注:《Thinking in Java》中寫的幾條特性里面只選了前面兩條呀舔,并去掉其中第三條換了第四條弥虐,如果有不同理解可以一起討論。

大體上媚赖,內(nèi)部類的作用已經(jīng)被明確了霜瘪,但是我們并不清楚定義內(nèi)部類是如何和外圍類進行聯(lián)系的,下面可以看一下內(nèi)部類和外圍類的聯(lián)系機制惧磺。

內(nèi)部類與外圍類的聯(lián)系

盡管我們知道內(nèi)部類的作用和帶來的特性颖对,但是并不清楚它是如何做到這些的,例如它能提供類似于多繼承的機制豺妓,但是內(nèi)部類和外圍類又有什么關(guān)系惜互?如果無法將內(nèi)部類和外圍類聯(lián)系在一起,那么內(nèi)部類僅僅只是寫在類定義里面的一個普通類琳拭,無法帶來它特殊的作用训堆。如下代碼:

/**
***內(nèi)部類與外圍類的聯(lián)系
**/
public class InnerClassDemo {
    public static void main(String[] arg0){
        OutterClass outter = new OutterClass();
        OutterClass.InnerClass inner = outter.new InnerClass();
        System.out.println("--inner.getName = "+inner.getName());
    }

}
class OutterClass{
    private String name = "OutterClassName";
    class InnerClass{
        public String getName(){
            return name;
        }
    }
}   
輸出:
--inner.getName = OutterClassName

可以看到,內(nèi)部類本身并沒有定義name字段白嘁,它的方法是得到name字段的值坑鱼,輸出的name字段的值是外圍類的name字段的值,那么外圍類和內(nèi)部類之間必然是有關(guān)聯(lián)的絮缅,這種關(guān)聯(lián)就是內(nèi)部類特性和作用的關(guān)鍵鲁沥。在上面的例子中,內(nèi)部類自然擁有了外圍類成員的訪問權(quán)限耕魄,這是如何做到的呢画恰?其實在代碼中可以看到,內(nèi)部類InnerClass實例inner的創(chuàng)建是通過其外部類實例outter.new出來的吸奴,那么內(nèi)部類對象是有機會獲取外圍類對象的引用的允扇。其實事實上也確實如此缠局,在訪問外圍類成員的字段時,就是通過捕獲的外圍類對象引用(非static的內(nèi)部類)這個引用是限定-this的引用(這個引用強調(diào)了一個概念:內(nèi)部類對象是和包圍類對象緊密綁定在一起的考润,這也是同一個外圍類對象實現(xiàn)的一部分)狭园,通過這個引用可以訪問它的包圍類的所有成員,包括私有的糊治。包圍類對象也可以訪問它內(nèi)部類對象的私有成員唱矛,但只能先聲明字段才能訪問。

知道了內(nèi)部類和外部類的聯(lián)系后井辜,就可以靈活使用內(nèi)部類語法規(guī)則來完成我們的設(shè)計绎谦,下面來看內(nèi)部類的使用。

內(nèi)部類的使用

創(chuàng)建

因為內(nèi)部類提供了更好的封裝性抑胎,我們只能通過它的外圍類來訪問它燥滑,那么怎么在外圍類外部創(chuàng)建內(nèi)部類對象呢渐北?在沒有使用內(nèi)部類的時候阿逃,我們創(chuàng)建一個類型的實例時,通常選擇使用new關(guān)鍵字赃蛛,但是在內(nèi)部類這里恃锉,會發(fā)現(xiàn)new關(guān)鍵字只有在外圍類內(nèi)部才起作用,而在外圍類之外是無法new出來的呕臂,其實這也是和內(nèi)部類與外部類的聯(lián)系有關(guān)破托,內(nèi)部類既然要自然擁有訪問外圍類實例的權(quán)限,自然要與外圍類實例聯(lián)系在一起歧蒋,所以需要如上例所示通過外圍類實例使用new創(chuàng)建:OutterClass.InnerClass inner = outter.new InnerClass();
當然這種特別的new的方式不是唯一解土砂,我們也可以選擇另一種創(chuàng)建方式:

/**
***  .this創(chuàng)建
**/
public class innerClassDemo {
    public static void main(String[] arg0){
        OutterClass outter = new OutterClass();
        OutterClass.InnerClass inner = outter.getInnerClass();
        System.out.println("--inner.getName = "+inner.getName());
    }
}
class OutterClass{
    private String name = "OutterClassName";
    public InnerClass getInnerClass(){
        return new InnerClass();
    }
    class InnerClass{
        public OutterClass getOutterClass(){
            return OutterClass.this;
        }
        public String getName(){
            return name;
        }
    }
}
輸出:
--inner.getName = OutterClassName

在這個例子中,我們直接通過方法返回一個內(nèi)部類谜洽,在這個內(nèi)部類中使用.this來綁定它的外圍類對象萝映,輸出的結(jié)果也是正確的。

總結(jié)一下阐虚,有兩種方式在外部創(chuàng)建一個類的內(nèi)部類序臂,一種是.this,另一種是.new

繼承

在之前实束,我們的內(nèi)部類一直是一個基類奥秆。很多時候,我們使用的內(nèi)部類咸灿,需要繼承一個已經(jīng)存在的類构订,這個類中存在了一些基本的實現(xiàn)。既然是繼承那么內(nèi)部類的繼承也是有選擇性的避矢,它可以繼承一個普通類悼瘾,也可以繼承一個其他類的內(nèi)部類签赃。在繼承普通類時,就像一般的類分尸,但是如果它要繼承另外一個內(nèi)部類锦聊,如下:

/**
***內(nèi)部類繼承內(nèi)部類
**/
public class innerClassDemo {
    public static void main(String[] arg0){
        OutterClass outter = new OutterClass();
        OutterClass.InnerClass inner = outter.getInnerClass();
        System.out.println("--inner.getName = "+inner.getName());
    }
}
class OutterClass extends OutterClass2{
    private String name = "OutterClassName";
    public InnerClass getInnerClass(){
        return new InnerClass();
    }
    class InnerClass extends InnerClass2{
        public OutterClass getOutterClass(){
            return OutterClass.this;
        }
        public String getName(){
            return name;
        }
    }
}
class OutterClass2{
    private String name = "OutterClassName2";
    public InnerClass2 getInnerClass(){
        return new InnerClass2();
    }
    class InnerClass2{
        public OutterClass2 getOutterClass(){
            return OutterClass2.this;
        }
        public String getName(){
            return name;
        }
    }
}
輸出:
--inner.getName = OutterClassName

在這里可以看到InnerClass繼承了InnerClass2,程序也是正確的運行的箩绍,當我把InnerClass類的外圍類OutterClass的繼承關(guān)系去掉孔庭,就會提示錯誤,這說明材蛛,如果一個內(nèi)部類要繼承另外一個內(nèi)部類圆到,那么需要它的外圍類也繼承它要繼承的內(nèi)部類的外圍類,即InnerClass要繼承InnerClass2卑吭,則OutterClass要繼承OutterClass2芽淡。

把內(nèi)部類繼承內(nèi)部類說完之后,再看一下豆赏,外部類繼承包圍類的一個內(nèi)部類挣菲,例如:

/**
***外部類繼承內(nèi)部類
**/
public class innerClassDemo {
    public static void main(String[] arg0){
        Unrelate unrelate = new Unrelate(new OutterClass());
        System.out.println("unrelateName = "+unrelate.getName());
    }
}
class OutterClass{
    private String name = "OutterClassName";
    public InnerClass getInnerClass(){
        return new InnerClass();
    }
    class InnerClass{
        public OutterClass getOutterClass(){
            return OutterClass.this;
        }
        public String getName(){
            return name;
        }
    }
}
class Unrelate extends OutterClass.InnerClass{
    Unrelate(OutterClass outter){
        outter.super();
    }
}
輸出:
unrelateName = OutterClassName

在這里,如果直接class Unrelate extends InnerClass會報錯掷邦,因為它的超類InnerClass是一個內(nèi)部類白胀,需要關(guān)聯(lián)一個外圍類,所以正確的寫法是class Unrelate extends OutterClass.InnerClass抚岗,盡管Unrelate類不是一個內(nèi)部類或杠,也不是一個外圍類,但是還是需要給它傳入一個外圍類對象綁定宣蔚。而Unrelate對象的使用和其他普通類并沒有什么不同向抢。

作用字段,繼承和隱藏

在這里整體的分析一下內(nèi)部類的作用情況胚委。
首先挟鸠,內(nèi)部類獲取了它外圍類的引用,所以外圍類的所有字段和方法都是可以使用的篷扩,術(shù)語叫作 在作用字段內(nèi)兄猩。但是,內(nèi)部類自己也存在的字段和方法鉴未,如果內(nèi)部類的字段和方法和外圍類的字段方法名一樣枢冤,會不會造成沖突?代碼如下:

/**
***內(nèi)部類作用字段
**/
public class innerClassDemo {
    public static void main(String[] arg0){
        OutterClass outter = new OutterClass();
        OutterClass.InnerClass inner = outter.new InnerClass();
        System.out.println("--getName = "+inner.getName());
    }
}
class OutterClass{
    private String name = "OutterClassName";
    public InnerClass getInnerClass(){
        return new InnerClass();
    }
    public String getName(){
        System.out.println("--OutterClass-getName-");
        return name;
    }
    class InnerClass{
        private String name = "InnerClassName";
        public OutterClass getOutterClass(){
            return OutterClass.this;
        }
        public String getName(){
            System.out.println("--InnerClass-getName-");
            return name;
        }
    }
}
輸出:
--InnerClass-getName-
--getName = InnerClassName

這里可以看到铜秆,輸出的結(jié)果和之前在InnerClass中沒有name字段時不一樣淹真,inner使用了它自己的字段name,而屬于外圍類的字段name和方法getName()被隱藏了连茧,其實隱藏外圍類的情況共有兩種:
1.內(nèi)部類有自己的字段和方法核蘸。
2.內(nèi)部類的父類有字段和方法巍糯。

在這兩種情況下,任意的簡單名用法客扎,都會直接引用內(nèi)部類成員祟峦。因為會出現(xiàn)隱藏的問題,所以在內(nèi)部類內(nèi)使用外圍類的字段和方法的時候徙鱼,建議使用.this來限定宅楞,例如:OutterClass.this.name

把一般的內(nèi)部類說完之后袱吆,我們看一下幾種特別的內(nèi)部類:

靜態(tài)內(nèi)部類

定義:把一個靜態(tài)類定義在另一個類定義中厌衙,就是靜態(tài)內(nèi)部類(嵌套類),和普通的內(nèi)部類相比绞绒,定義時使用了static關(guān)鍵字婶希。
還記得之前,普通的內(nèi)部類對象會在內(nèi)部捕獲并保存它外圍類對象的引用蓬衡,但是喻杈,靜態(tài)內(nèi)部類不是這樣的。靜態(tài)內(nèi)部類本身就是外圍類的一個成員撤蟆,而不是一個獨立的對象存在的奕塑。因為沒有保存它外圍類對象堂污,所以靜態(tài)內(nèi)部類的創(chuàng)建不依賴于外圍類的引用家肯,也沒有自然獲取的外圍類各種字段方法的權(quán)限。 例如:

/**
***靜態(tài)內(nèi)部類
**/
public class innerClassDemo {
    public static void main(String[] arg0){
        OutterClass.InnerClass2 inner = new OutterClass.InnerClass2();
        System.out.println("--getName = "+inner.getName());
    }
}
class OutterClass{
    private String name = "OutterClassName";
    public String getName(){
        System.out.println("--OutterClass-getName-");
        return name;
    }

    static class InnerClass2{
        private String name = "InnerClassName2";
        public String getName(){
            System.out.println("--InnerClass2-getName-");
            return name;
        }
    }
}

InnerClass類添加static關(guān)鍵字盟猖,使得它變成一個靜態(tài)內(nèi)部類讨衣,那么使用OutterClass.this.name的時候會提示錯誤,說明無法訪問外圍類的非靜態(tài)字段

局部內(nèi)部類

定義:定義在外圍類的內(nèi)部代碼塊中式镐,它不時外圍類的一部分反镇,但是能訪問當前代碼塊內(nèi)的變量和所有的外圍類的成員,作用的范圍類似于局部變量只在當前代碼塊內(nèi)部娘汞,因為外部沒有任何引用它的路徑歹茶。
局部內(nèi)部類可以訪問定義的該類作用字段中的所有變量,包括代碼塊中的局部變量你弦,外圍類的所有成員和局部內(nèi)部類的所有字段惊豺,但是代碼塊中的局部變量或方法參數(shù)只能聲明成final才能被訪問。這是多線程的問題訪問保證安全性的措施禽作,而且這樣的好處是值是確定的尸昧。在局部內(nèi)部類中,也會存在普通內(nèi)部類存在的隱藏字段方法等問題旷偿,如果隱藏的是代碼塊的局部變量烹俗,那么久沒有辦法來訪問這個被隱藏的變量了爆侣。

匿名內(nèi)部類

定義:擴展了某個類或?qū)崿F(xiàn)了某個接口的的匿名類。
匿名內(nèi)部類沒有顯示聲明的構(gòu)造器幢妄,但是可以使用初始代碼塊來初始化兔仰。雖然匿名類使用起來很簡單,但是會降低代碼的可讀性蕉鸳。

接口中的嵌套

雖然可以使用斋陪,但是目前來說,接口作為一個行為協(xié)議置吓,盡量不要在內(nèi)部書寫除協(xié)議本身以外的東西无虚。
接口中嵌套類或者接口,本質(zhì)上和類中嵌套類一樣衍锚,只是把一個與接口聯(lián)系緊密的類型關(guān)聯(lián)到這個接口的內(nèi)部友题。例如:

interface OutterInterface{
    class InnerClass{}
    interface InnerInterface{}
    InnerClass getInnerClass();
    InnerInterface getInnerInterface();
}

這樣,內(nèi)部的接口或者內(nèi)部的類就和外圍接口緊密的綁定在一起戴质。注:任何在接口中定義的類都是publicstatic

類中嵌套接口

其實這個是很少很少用度宦,目前來說,我還沒有見過告匠,但是說明一下戈抄。它們在類中起到的作用僅僅只是組織相關(guān)類型的機制。由于接口沒有實現(xiàn)后专,所以它不能是非靜態(tài)的划鸽,默認的static關(guān)鍵字省略。

內(nèi)部類標識符

一般來說戚哎,我們的Demo.java文件編譯后文件名為Demo.class文件裸诽,但是如果java文件內(nèi)部包含了內(nèi)部類,那么文件會將內(nèi)部類分出一個class文件型凳,內(nèi)部類的文件名為外圍類名$內(nèi)部類名丈冬,例如Outer實體中還有Inner類,那么編譯出來后甘畅,會存在Outer.class文件和Outer$Inner.class文件埂蕊。那么如果是個匿名內(nèi)部類呢,它本身就沒有名字疏唾,這種情況下蓄氧,編譯器會簡單的產(chǎn)生一個數(shù)字作為它的標識符。例如Outer$1.class荸实。

寫到這里已經(jīng)把內(nèi)部類給寫完了匀们,雖然后面部分只用文字描述了一下,還有一些東西沒有寫在上面准给,具體的可以再去看看研究研究泄朴,比如匿名內(nèi)部類和局部內(nèi)部類的區(qū)別等重抖。

本文參考《Thinking in Java》第10章內(nèi)部類,《Java程序設(shè)計語言》第5章 嵌套類和接口

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末祖灰,一起剝皮案震驚了整個濱河市钟沛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌局扶,老刑警劉巖恨统,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異三妈,居然都是意外死亡畜埋,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門畴蒲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悠鞍,“玉大人,你說我怎么就攤上這事模燥】Ъ溃” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵蔫骂,是天一觀的道長么翰。 經(jīng)常有香客問我,道長辽旋,這世上最難降的妖魔是什么浩嫌? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮戴已,結(jié)果婚禮上固该,老公的妹妹穿的比我還像新娘。我一直安慰自己糖儡,他們只是感情好,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布怔匣。 她就那樣靜靜地躺著握联,像睡著了一般。 火紅的嫁衣襯著肌膚如雪每瞒。 梳的紋絲不亂的頭發(fā)上金闽,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音剿骨,去河邊找鬼代芜。 笑死,一個胖子當著我的面吹牛浓利,可吹牛的內(nèi)容都是我干的挤庇。 我是一名探鬼主播钞速,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼嫡秕!你這毒婦竟也來了渴语?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤昆咽,失蹤者是張志新(化名)和其女友劉穎驾凶,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體掷酗,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡调违,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了泻轰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翰萨。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖糕殉,靈堂內(nèi)的尸體忽然破棺而出亩鬼,到底是詐尸還是另有隱情,我是刑警寧澤阿蝶,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布雳锋,位于F島的核電站,受9級特大地震影響羡洁,放射性物質(zhì)發(fā)生泄漏玷过。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一筑煮、第九天 我趴在偏房一處隱蔽的房頂上張望辛蚊。 院中可真熱鬧,春花似錦真仲、人聲如沸袋马。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽虑凛。三九已至,卻和暖如春软啼,著一層夾襖步出監(jiān)牢的瞬間桑谍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工祸挪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锣披,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像雹仿,于是被迫代替她去往敵國和親增热。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法盅粪,類相關(guān)的語法钓葫,內(nèi)部類的語法,繼承相關(guān)的語法票顾,異常的語法础浮,線程的語...
    子非魚_t_閱讀 31,581評論 18 399
  • 第一章 對象導(dǎo)論 對象具有狀態(tài)、行為和標識奠骄。這意味著每一個對象都可以擁有內(nèi)部數(shù)據(jù)和方法豆同,并且每一個對象都可以唯一地...
    niaoge2016閱讀 816評論 0 0
  • 以前的我懵懵懂懂,沒有思想含鳞;以前的我一分鐘刷一次朋友圈影锈,每天在扣扣空間里發(fā)一些無聊甚至于抱怨的話;是所有男孩眼...
    雪狐白糖閱讀 202評論 0 0
  • 秋天將至蝉绷,萬物呈現(xiàn)出凄涼衰敗鸭廷,北方的候鳥已經(jīng)開始飛往南方避冬。在這樣一群南飛的雁群中熔吗,有一對新結(jié)發(fā)的雁夫婦辆床,公雁我...
    BlueCute閱讀 149評論 0 1
  • 今天是你走的第九天,還有104天桅狠。我想你讼载,今天心情不好但是和你聊天就好一點了≈械回來可以看到你是我最開心的事咨堤。 昨天...
    rainll閱讀 138評論 0 0