內(nèi)部類有哪些焕阿?它們存在的意義是什么咪啡?

一、內(nèi)部類的分類及區(qū)別

內(nèi)部類的表現(xiàn)形式為一個(gè)類可以在另一個(gè)類的內(nèi)部存在暮屡,其中,內(nèi)部包含其它類的類稱為外部類毅桃,被包含的類稱為內(nèi)部類褒纲。在如下的示例代碼中准夷,Outer就是外部類,Inner就是內(nèi)部類莺掠。

public class Outer {
    public class Inner {
    }
}

常見的內(nèi)部類主要分為以下四種形式:

  • 成員內(nèi)部類
  • 靜態(tài)內(nèi)部類
  • 局部?jī)?nèi)部類
  • 匿名內(nèi)部類

值得說明的是衫嵌,包含內(nèi)部類的外部類在編譯過后會(huì)成為兩個(gè)完全不同的類,分別是Outer.classOuter$Inner.class彻秆。

1.1 成員內(nèi)部類

此時(shí)的內(nèi)部類相當(dāng)于是外部類的一個(gè)普通成員楔绞,只要當(dāng)外部類被實(shí)例化成一個(gè)對(duì)象后,借用該對(duì)象就可以對(duì)內(nèi)部類進(jìn)行操作唇兑。

通常成員內(nèi)部類表現(xiàn)為如下的代碼形式:

public class Outer {    
    private String outerValue = "outer";
    // 成員內(nèi)部類酒朵,相當(dāng)于外部類的一個(gè)成員變量
    public class Inner {
        private String innerValue = "inner";    
        public void printInner(){
            System.out.println("訪問外部類的私有屬性:" + outerValue);
            System.out.println("訪問內(nèi)部類的私有屬性:" + innerValue);
        }
    }
}
public class Test {
    public static void main(String[] args) {
        // 01使用成員內(nèi)部類
        Outer outer = new Outer();
        Outer.Inner inner = outer.new Inner();
        inner.printInner();
    }
}

在使用成員內(nèi)部類的時(shí)候,需要記住以下幾點(diǎn):

  • 內(nèi)部類可以直接使用外部類的任何屬性和方法扎附,即使是private的蔫耽。
  • 外部類不能直接使用內(nèi)部類的屬性及方法,只能通過內(nèi)部類的對(duì)象來使用留夜。
  • 內(nèi)部類中不能包含static的內(nèi)容匙铡。
  • 內(nèi)部類中需要使用外部類的對(duì)象時(shí),使用outer.this來指代碍粥。

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

靜態(tài)內(nèi)部類又稱為嵌套內(nèi)部類鳖眼,是在外部類中具有static關(guān)鍵字聲明的內(nèi)部類,相當(dāng)于是外部類的類變量嚼摩。

通常靜態(tài)內(nèi)部類表現(xiàn)為如下的代碼形式:

public class Outer2 {
    private String outerValue1 = "outerValue1";
    private static String outerValue2 = "outerValue2";
    // 靜態(tài)內(nèi)部類/嵌套類
    public static class Inner2{
        private String innerValue1 = "innerValue1";
        public void printInfo(){
            System.out.println("訪問外部類中的私有屬性:" + new Outer2().outerValue1);
            System.out.println("訪問外部類中的靜態(tài)變量:" + Outer2.outerValue2);
            System.out.println("訪問內(nèi)部類中的私有屬性:" + innerValue1);
        }
    }
}
public class Test {
    public static void main(String[] args) {    
        // 02使用靜態(tài)內(nèi)部類/嵌套類
        Outer2.Inner2 inner2 = new Outer2.Inner2();
        inner2.printInfo();
}

在使用靜態(tài)內(nèi)部類的時(shí)候具帮,需要記住以下幾點(diǎn):

  • 靜態(tài)內(nèi)部類的內(nèi)部不能直接訪問外部類的非靜態(tài)成員,只能通過實(shí)例化外部類的對(duì)象低斋,然后通過該對(duì)象進(jìn)行訪問蜂厅。
  • 創(chuàng)建靜態(tài)內(nèi)部類的對(duì)象時(shí),不需要像成員內(nèi)部類一樣需要先行創(chuàng)建外部類的對(duì)象膊畴,而是直接使用內(nèi)部類的名稱即可創(chuàng)建掘猿。

1.3 局部?jī)?nèi)部類

局部?jī)?nèi)部類是出現(xiàn)在外部類的方法或者某個(gè)代碼塊的作用域范圍內(nèi)的內(nèi)部類,通常表現(xiàn)為如下的代碼形式:

public class Outer3 {
    private String outerValue = "outerValue";
    // 在方法內(nèi)部定義的局部?jī)?nèi)部類
    public void outerInfo(){
        final String methodValue = "methodValue";
        class Inner3 {
            String innerValue = "innerValue";
            public void printInner3(){
                System.out.println("訪問外部類的屬性:" + outerValue);
                System.out.println("訪問外部類的方法的常量:" + methodValue);
                System.out.println("訪問內(nèi)部類的屬性:" + innerValue);
            }
        }
        Inner3 inner3 = new Inner3();
        inner3.printInner3();
    }
    
    // 在作用域內(nèi)部定義的局部?jī)?nèi)部類
    public void outerInfo2(boolean flag){
        if(flag){
            final String methodValue2 = "methodValue2";
            class Inner4{
                String innerValue2 = "innerValue2";
                public void printInner4(){
                    System.out.println("訪問外部類的屬性:" + outerValue);
                    System.out.println("訪問外部類的方法的常量:" + methodValue2);
                    System.out.println("訪問內(nèi)部類的屬性:" + innerValue2);
                }
            }
            Inner4 inner4 = new Inner4();
            inner4.printInner4();
        }else{
            System.out.println("outerInfo2:else");
        }
    }
}
public class Test {
    public static void main(String[] args) {
        // 03使用局部?jī)?nèi)部類
        Outer3 outer3 = new Outer3();
        outer3.outerInfo();
        outer3.outerInfo2(true);
}

在使用局部?jī)?nèi)部類的時(shí)候唇跨,需要記住以下幾點(diǎn):

  • 局部?jī)?nèi)部類僅在作用域范圍內(nèi)有效稠通,超出范圍則不再可使用。
  • 局部?jī)?nèi)部類中可以使用方法或者代碼塊級(jí)別的變量买猖,但是要求它們必須是final類型的常量改橘。

1.4 匿名內(nèi)部類

匿名內(nèi)部類沒有類名,通常被設(shè)計(jì)為只使用一次玉控,用以簡(jiǎn)化代碼的書寫飞主,通常表現(xiàn)為如下的代碼形式:

public class Outer4 {
    private String outerValue = "outerValue";
    // 在方法內(nèi)部使用匿名內(nèi)部類
    public Inner5 getInnerInfo(final String name, String value){
        final String methodValue = "methodValue";
        return new Inner5(){
            private String innerValue = name;
            @Override
            public String getInner5() {
                System.out.println("訪問外部類的私有屬性:" + outerValue);
                System.out.println("訪問外部類的方法的常量" + methodValue);
                return innerValue;
            }
        };
    }
}
interface Inner5 {
    String getInner5();
}
public class Test {
    public static void main(String[] args) {
        // 04使用匿名內(nèi)部類
        Outer4 outer4 = new Outer4();
        Inner5 inner5 = outer4.getInnerInfo("zhangsan", "lisi");
        System.out.println(inner5.getInner5());
    }
}

在使用匿名內(nèi)部類的時(shí)候,需要記住以下幾點(diǎn):

  • 匿名內(nèi)部類必須繼承一個(gè)父類或者實(shí)現(xiàn)一個(gè)接口。
  • 在匿名內(nèi)部類中需要使用外部類的方法的屬性時(shí)碌识,要求該屬性必須為final類型的常量碾篡。
  • 在匿名內(nèi)部類中需要使用外部類的方法的形參時(shí),要求該形參必須為final類型的常量筏餐。

二开泽、內(nèi)部類的作用及意義

學(xué)會(huì)使用內(nèi)部類,是掌握J(rèn)ava高級(jí)編程的一部分魁瞪,能讓你更優(yōu)雅地設(shè)計(jì)自己的程序結(jié)構(gòu)穆律。

2.1 為了實(shí)現(xiàn)封裝特性

我們?cè)诹硪黄恼?a href="http://www.reibang.com/p/7b08e274bc0b" target="_blank">《面向?qū)ο笕筇匦缘目偨Y(jié)》中有詳細(xì)介紹關(guān)于封裝的常用方法,其中就有使用內(nèi)部類的方法导俘。

外部類對(duì)內(nèi)部類的封裝主要表現(xiàn)為在其它地方想要使用內(nèi)部類的話需要受到外部類的限制峦耘。

public class Animal {
    public class Dog {
    }
    public Dog getDog(){
        return new Dog();
    }
}
public class Test {
    public static void main(String[] args) {
        Animal a = new Animal();
        Dog d = a.new Dog();
        Dog o = a.getDog();
        // 無法直接實(shí)例化內(nèi)部類的對(duì)象,如下語句無法編譯通過
        Dog g = new Dog();
    }
}

在如上的例子中趟畏,內(nèi)部類雖然是public的贡歧,但是想要使用內(nèi)部類必須借助外部類的對(duì)象才能做到,這就是封裝赋秀。更進(jìn)一步利朵,如果你只希望在外部類中才能使用內(nèi)部類,而在其它地方都不能使用的話猎莲,只需要將內(nèi)部類改為private即可绍弟。

public class Animal {
    private class Dog {
    }
    public Dog getDog(){
        return new Dog();
    }
    public static void main(String[] args) {
        Animal a = new Animal();
        // 在外部類中可以使用private的內(nèi)部類,但是仍然只能通過外部類的對(duì)象才能訪問
        Dog d = a.new Dog();
        Dog o = a.getDog();
    }
}
public class Test {
    public static void main(String[] args) {
        Animal a = new Animal();
        // 下面兩行語句都無法編譯通過著洼,因?yàn)镈og是private的內(nèi)部類樟遣,無法在外部類之外的其它任何地方使用
        Dog d = a.new Dog();
        Dog o = a.getDog();
    }
}

2.2 為了完善多重繼承

在Java中一個(gè)子類只能繼承一個(gè)父類,如果想繼承多個(gè)父類身笤,只能將這些父類改為接口宣羊,然后子類實(shí)現(xiàn)多個(gè)接口寥袭。然而接口中的方法都是抽象的,是必須在子類中予以實(shí)現(xiàn)的,這就帶來了很大的不便等缀,那有沒有什么方法能讓子類同時(shí)繼承多個(gè)父類中的已經(jīng)實(shí)現(xiàn)好了的方法呢胃惜?當(dāng)然是通過內(nèi)部類除破,具體示例如下:

public class Animal {
    public String bark(){
        return "WOW";
    }
}
public class Creature {
    public String drink(){
        return "water";
    }
}
public class Mammal {
    public class Dog extends Creature {
    }
    public class Cat extends Animal {
    }
    public String getDogAction(){
        return new Dog().drink();
    }
    public String getCatAction(){
        return new Cat().bark();
    }
}
public class Test {
    public static void main(String[] args) {
        Mammal m = new Mammal();
        // 對(duì)于Mammal來說并扇,其通過內(nèi)部類Dog和Cat間接地分別繼承了Creature和Animal中的方法
        System.out.println(m.getCatAction());
        System.out.println(m.getDogAction());
    }
}

2.3 讓Java也有閉包

首先我們需要先理解什么是閉包?

閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)文搂∈实叮——百度百科

那么映射到Java里面,所謂的閉包就可以指在內(nèi)部類中可以訪問并使用外部類中的所有變量和方法的特性煤蹭。那么這樣的閉包究竟有什么用處呢笔喉?別急取视,我們先來看下面這樣一個(gè)例子:

public class Fruit {
    private Integer num = 10;
    private void addOneFruit(){
        num++;
        System.out.println("fruit增加到" + num + "個(gè)");
    }
    public class Child {
        public void getOneFruit(){
            num--;
            System.out.println("fruit減少到" + num + "個(gè)");
        }   
        public void putOneFruit(){
            addOneFruit();
        }
        
    }
}
public class Test {
    public static void main(String[] args) {
        Fruit fruit = new Fruit();
        Child child = fruit.new Child();
        // 通過內(nèi)部類對(duì)象引用來操作外部類的私有屬性和方法(稱為閉包)
        child.getOneFruit();
        child.putOneFruit();
    }
}

在上面的例子中,main方法中原來是無論如何都不能訪問Fruit的私有屬性numaddOneFruit的然遏,但是通過內(nèi)部類Child贫途,這一切就成了可能吧彪,從而實(shí)現(xiàn)了閉包待侵。下面是列舉的關(guān)于閉包的好處:

  • 使得環(huán)境外部(類、函數(shù))可以有一種途徑訪問環(huán)境內(nèi)的私有成員姨裸。
  • 使得當(dāng)前環(huán)境的對(duì)象得以一直保留在內(nèi)存中秧倾,即使將該對(duì)象的引用置為null。

關(guān)于上述第一點(diǎn)傀缩,你是否會(huì)有這樣的疑問:為什么不使用setter和getter方法對(duì)環(huán)境內(nèi)的私有成員進(jìn)行訪問呢那先?

如果單從能否實(shí)現(xiàn)角度看,我們要訪問環(huán)境內(nèi)的私有成員赡艰,當(dāng)然可以通過setter和getter方法售淡,但是從面向?qū)ο缶幊探嵌瓤矗瑒t不能依靠這種方法慷垮。比如Fruit的減少只能通過Child的操作來進(jìn)行揖闸,這是模擬現(xiàn)實(shí)世界中的操作“吃”,只有Child才能吃掉Fruit達(dá)到讓Fruit減少的目的料身,總不能讓Fruit自己吃自己吧汤纸。

關(guān)于上述的第二點(diǎn),我們可以改下main方法進(jìn)行說明:

public class Test {
    public static void main(String[] args) {
        Fruit fruit = new Fruit();
        Child child = fruit.new Child();
        // 通過內(nèi)部類對(duì)象引用來操作外部類的私有熟悉和方法(稱為閉包)
        child.getOneFruit();
        child.putOneFruit();
        // 即使將外部類的引用置為null芹血,但因其內(nèi)部類引用仍然存在贮泞,內(nèi)部類需要依賴外部類,所以外部類的對(duì)象仍然得以保留幔烛,不會(huì)被GC回收
        fruit = null;
        child.putOneFruit();
        child.getOneFruit();
        child = null;
        // 內(nèi)部類的引用被置為null后啃擦,內(nèi)部類的對(duì)象和外部類的對(duì)象從此就沒有引用指向它們,很快會(huì)被GC回收
        // 下面兩行編譯無法通過
        child.getOneFruit();
        child.putOneFruit();
    }
}

三饿悬、常見的使用內(nèi)部類的例子

待補(bǔ)充......

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末令蛉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子乡恕,更是在濱河造成了極大的恐慌言询,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件傲宜,死亡現(xiàn)場(chǎng)離奇詭異运杭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)函卒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門辆憔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事虱咧⌒荛唬” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵腕巡,是天一觀的道長(zhǎng)玄坦。 經(jīng)常有香客問我,道長(zhǎng)绘沉,這世上最難降的妖魔是什么煎楣? 我笑而不...
    開封第一講書人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮车伞,結(jié)果婚禮上择懂,老公的妹妹穿的比我還像新娘。我一直安慰自己另玖,他們只是感情好困曙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著谦去,像睡著了一般慷丽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上哪轿,一...
    開封第一講書人閱讀 51,698評(píng)論 1 305
  • 那天盈魁,我揣著相機(jī)與錄音,去河邊找鬼窃诉。 笑死杨耙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的飘痛。 我是一名探鬼主播珊膜,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼宣脉!你這毒婦竟也來了车柠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤塑猖,失蹤者是張志新(化名)和其女友劉穎竹祷,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體羊苟,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡塑陵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蜡励。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片令花。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡阻桅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出兼都,到底是詐尸還是另有隱情嫂沉,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布扮碧,位于F島的核電站趟章,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏芬萍。R本人自食惡果不足惜尤揣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一搔啊、第九天 我趴在偏房一處隱蔽的房頂上張望柬祠。 院中可真熱鬧,春花似錦负芋、人聲如沸漫蛔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽莽龟。三九已至,卻和暖如春锨天,著一層夾襖步出監(jiān)牢的瞬間毯盈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工病袄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留搂赋,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓益缠,卻偏偏與公主長(zhǎng)得像脑奠,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子幅慌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

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

  • Java 內(nèi)部類 分四種:成員內(nèi)部類宋欺、局部?jī)?nèi)部類、靜態(tài)內(nèi)部類和匿名內(nèi)部類胰伍。 1齿诞、成員內(nèi)部類: 即作為外部類的一個(gè)成...
    ikaroskun閱讀 1,232評(píng)論 0 13
  • 抽象類 在繼承的層次結(jié)構(gòu)中菩咨,每個(gè)新子類都使類變得越來越明確具體吠式。如果從一個(gè)子類追溯到父類陡厘,類就會(huì)變得更通用和抽象。...
    Steven1997閱讀 1,376評(píng)論 0 5
  • 書案疾筆特占,軒窗外盡是些向日葵花糙置,明亮的晃眼。昨夜小雨東風(fēng)是目,被踩進(jìn)泥土中的花瓣谤饭,依稀相似小時(shí)候貪口的桂花甜糕。 只是...
    蠻小吉閱讀 395評(píng)論 0 0
  • 走著 走著 就發(fā)現(xiàn) 自己不見了 現(xiàn)在的 如同行尸走肉的 不是我 我似乎是 消失在了 平庸的路上
    愚者知之閱讀 117評(píng)論 0 0