談?wù)刯ava中的內(nèi)部類

概述

最近看ThreadLocal的實(shí)現(xiàn)原理的過程中了解到ThreadLocalMap是定義在ThreadLocal中的靜態(tài)內(nèi)部類敬惦,默默的問了問自己為什么要定義為靜態(tài)內(nèi)部類捻脖?定義為普通內(nèi)部類有沒有關(guān)系?發(fā)現(xiàn)自己對java的內(nèi)部類知識不是很了解输虱,于是惡補(bǔ)了下內(nèi)部類的用法和區(qū)別音念,希望這邊文章可以對跟我有相似問題的同學(xué)有所幫助掂碱。

內(nèi)部類介紹

? 顧名思義,內(nèi)部類就是在一個類的內(nèi)部定義另一個類春感。就像我們在類中聲明成員變量一樣砌创,變量可以使用訪問控制符,static鲫懒,可以聲明為成員變量或者函數(shù)內(nèi)部的局部變量嫩实。內(nèi)部類也可以有上述的用法,java中的內(nèi)部類可以分為普通內(nèi)部類(成員內(nèi)部類)窥岩,靜態(tài)內(nèi)部類甲献,局部內(nèi)部類和匿名內(nèi)部類。

成員內(nèi)部類

?成員內(nèi)部類就是像普通的成員函數(shù)一樣聲明的內(nèi)部類颂翼,下面我們先給出一個簡單的示例晃洒,InnerClass是OutClass的成員內(nèi)部類,可以訪問OutClass的成員變量朦乏。

/**
 * Created by yuanqiongqiong on 2019/6/14.
 */
public class OutClass {
    private int i;

    public OutClass(int i) {
        this.i = i;
    }

    public class InnerClass {
        private int j;

        public InnerClass(int j) {
            this.j = j;
        }

        public void print() {
            System.out.println("OutClass.i = " + i);
            System.out.println("InnerClass.j = " + j);
        }
    }
}

//測試
public class OutClassTest {

    public static void main(String [] args) {
        OutClass outClass = new OutClass(1);
        OutClass.InnerClass innerClass = outClass.new InnerClass(2);
        innerClass.print();
    }
}

我認(rèn)為主要有以下幾點(diǎn)用法:
(1)可以使用public锥累,private,protected或者默認(rèn)的訪問權(quán)限控制符聲明集歇,表示該內(nèi)部類的訪問權(quán)限桶略;
(2)可以訪問外部類的成員變量和方法;
(3)成員內(nèi)部類不能聲明靜態(tài)成員;
? 對于(2)际歼,我們可以認(rèn)為在內(nèi)部類持有外部類的引用惶翻,這樣內(nèi)部類就可以通過這個引用訪問外部類的所有的成員的方法和變量。同時鹅心,這也解釋為什么要通過外部類對象來創(chuàng)建內(nèi)部類吕粗。為了驗(yàn)證這個問題,我們對OutClass.java編譯得到OutClass.class和OutClass$InnerClass.class旭愧,然后我們通過idea直接打開內(nèi)部類class文件(我們之所以用idea自帶的反編譯器颅筋,是因?yàn)橄啾扔趈avap,idea反編譯的文件可讀性更高)输枯,具體如下:


成員內(nèi)部類反編譯.jpeg

java在編譯內(nèi)部類時會在其構(gòu)造函數(shù)中默認(rèn)添加外部類引用的參數(shù)议泵,從而持有外部類的引用。
對于(3)桃熄,成員內(nèi)部類就相當(dāng)于外部類的一個普通的成員變量先口,當(dāng)jvm加載外部類的時候并不會加載非靜態(tài)變量,因此也就不會加載內(nèi)部類瞳收。如果內(nèi)部類可以聲明靜態(tài)變量碉京,那么就會出現(xiàn)類還沒有加載卻要初始化靜態(tài)變量的現(xiàn)象,因此java不會允許這種情況通過編譯螟深。

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

? 靜態(tài)內(nèi)部類就是有static修飾的內(nèi)部類谐宙,類似靜態(tài)變量或者靜態(tài)函數(shù)。相比于成員內(nèi)部類對外部類的依賴界弧,靜態(tài)內(nèi)部類基本不依賴外部類凡蜻。通過其官方名稱"static nested classes"(靜態(tài)嵌套類),更能說明其與外部類沒有關(guān)系夹纫,只是自己的類聲明嵌套在外部類的java文件中咽瓷。靜態(tài)內(nèi)部類只能訪問外部類的靜態(tài)成員和方法,這點(diǎn)非常好理解舰讹,因?yàn)殪o態(tài)內(nèi)部類和外部類沒關(guān)系茅姜。靜態(tài)內(nèi)部類和靜態(tài)變量或方法一樣可以使用public,private月匣,protected或者默認(rèn)的訪問權(quán)限控制符聲明钻洒,這點(diǎn)也很好理解。下面我們給出個靜態(tài)內(nèi)部類的示例:

public class OutClass {
    private static int k=3;
    private int i;

    public OutClass(int i) {
        this.i = i;
    }

    static class InnerClass {
        private int j;

        public InnerClass(int j) {
            this.j = j;
        }

        public void print() {
            System.out.println("OutClass.k = " + k);
            System.out.println("InnerClass.j = " + j);
        }
    }
}

//測試類
public class OutClassTest {

    public static void main(String [] args) {
        OutClass.InnerClass innerClass = new OutClass.InnerClass(2);
        innerClass.print();
    }
}

示例很簡單锄开,從測試類可以看出內(nèi)部類的聲明不再依賴外部類素标,可以完全獨(dú)立聲明對象,其實(shí)靜態(tài)內(nèi)部類不再持有外部類的引用萍悴,下面我們看下反編譯的class文件:


靜態(tài)內(nèi)部類反編譯.png

匿名內(nèi)部類

?對于匿名內(nèi)部類头遭,大家肯定在日常開發(fā)中都有使用但卻不知道叫法寓免,比如函數(shù)回調(diào),線程聲明等都會使用匿名內(nèi)部類计维。如下面這段常用的代碼:

public class ThreadTest {
    public static void main(String []args) {
        final User user= new User("chenxiaosuo");
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("user = " + user);
            }
        }, "threadtest");
        thread.start();
    }

    static class User {
        private String name;

        public User(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
}

Runable是一個接口袜香,我們通過new來實(shí)現(xiàn)了一個匿名內(nèi)部類,這個類沒有類名(其實(shí)真實(shí)編譯后會以"外部類$數(shù)字"的形式作為類名)鲫惶。
?不知道大家有沒有這個困惑蜈首,在匿名內(nèi)部類中使用的變量必須聲明為final類型,表示變量不可變欠母,否則編譯報錯欢策,如上的變量user。我們先分析下上述代碼赏淌,變量user是個局部變量踩寇,當(dāng)函數(shù)testAnonymousInnerClass結(jié)束后會回收,而此時threadtest可能還在繼續(xù)執(zhí)行猜敢,如果訪問變量user應(yīng)該會報錯姑荷,而實(shí)際卻不會出現(xiàn)這種情況盒延。我們看下編譯后的類文件發(fā)生了什么缩擂,匿名內(nèi)部會復(fù)制一份訪問的變量到內(nèi)部,這樣就解決上述的聲明周期問題添寺。因?yàn)檫@種拷貝胯盯,如果java允許這個變量發(fā)生改變,那么肯定就造成了數(shù)據(jù)不一致的問題计露,這也就解釋為什么匿名內(nèi)部類的訪問的變量必須為final類型了博脑。


匿名內(nèi)部類反編譯.png

局部內(nèi)部類

可以把局部內(nèi)部類與我們函數(shù)里的局部變量作為類比,局部內(nèi)部類就是聲明在一個函數(shù)或者某個作用域中的內(nèi)部類票罐〔嫒ぃ可以看出局部內(nèi)部類只作用于其所聲明的函數(shù)或者局部作用域中,因此在局部內(nèi)部類中不能使用public该押,private和protected疗杉。由于局部內(nèi)部類比較簡單,這里不再舉例說明蚕礼。

內(nèi)部類作用

以上介紹了內(nèi)部的使用及相關(guān)分析烟具,最后我們再思考一個問題,為什么要使用內(nèi)部類奠蹬?在java編程思想中講到朝聋,使用內(nèi)部類可以使得java類繼承多個類,使得java多繼承的方案更加的完善囤躁。是不是很抽象冀痕?下面我這邊總結(jié)兩個我感覺使用內(nèi)部類的理由:
(1)使用成員內(nèi)部類可以使得內(nèi)部類訪問外部類的成員變量荔睹;
(2)使用匿名內(nèi)部類使得我們代碼變得更簡潔,不需要定義一些只是用一次的類言蛇;

原文

袁瓊瓊的技術(shù)博客应媚,歡迎指針
http://yuanqiongqiong.cn/2019/06/18/%E8%B0%88%E8%B0%88java%E4%B8%AD%E7%9A%84%E5%86%85%E9%83%A8%E7%B1%BB/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市猜极,隨后出現(xiàn)的幾起案子中姜,更是在濱河造成了極大的恐慌,老刑警劉巖跟伏,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丢胚,死亡現(xiàn)場離奇詭異,居然都是意外死亡受扳,警方通過查閱死者的電腦和手機(jī)携龟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來勘高,“玉大人峡蟋,你說我怎么就攤上這事』” “怎么了蕊蝗?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長赖舟。 經(jīng)常有香客問我蓬戚,道長,這世上最難降的妖魔是什么宾抓? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任子漩,我火速辦了婚禮,結(jié)果婚禮上石洗,老公的妹妹穿的比我還像新娘幢泼。我一直安慰自己,他們只是感情好讲衫,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布缕棵。 她就那樣靜靜地躺著,像睡著了一般焦人。 火紅的嫁衣襯著肌膚如雪挥吵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天花椭,我揣著相機(jī)與錄音忽匈,去河邊找鬼。 笑死矿辽,一個胖子當(dāng)著我的面吹牛丹允,可吹牛的內(nèi)容都是我干的郭厌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼雕蔽,長吁一口氣:“原來是場噩夢啊……” “哼折柠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起批狐,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤扇售,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后嚣艇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體承冰,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年食零,在試婚紗的時候發(fā)現(xiàn)自己被綠了困乒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡贰谣,死狀恐怖娜搂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吱抚,我是刑警寧澤百宇,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站频伤,受9級特大地震影響恳谎,放射性物質(zhì)發(fā)生泄漏芝此。R本人自食惡果不足惜憋肖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望婚苹。 院中可真熱鬧岸更,春花似錦、人聲如沸膊升。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽廓译。三九已至评肆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間非区,已是汗流浹背瓜挽。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留征绸,地道東北人久橙。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓俄占,卻偏偏與公主長得像,于是被迫代替她去往敵國和親淆衷。 傳聞我的和親對象是個殘疾皇子缸榄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評論 2 355