內(nèi)部類

內(nèi)部類基礎(chǔ)

在Java中,可以將一個類定義在另一個類里面或者一個方法里面该镣,這樣的類稱為內(nèi)部類廓译。廣泛意義上的內(nèi)部類一般來說包括這四種:成員內(nèi)部類局部內(nèi)部類乙濒、匿名內(nèi)部類靜態(tài)內(nèi)部類陕赃。下面就先來了解一下這四種內(nèi)部類的用法卵蛉。

成員內(nèi)部類

成員內(nèi)部類是最普通的內(nèi)部類,它的定義為位于另一個類的內(nèi)部么库,形如下面的形式:

class Circle {

    double radius = 0;
    public Circle(double radius) {
        this.radius = radius;
    }
class Draw {     //內(nèi)部類

           public void drawSahpe() {

            System.out.println("drawshape");
        
        }
    }
}

這樣看起來傻丝,類Draw像是類Circle的一個成員,Circle稱為外部類诉儒。成員內(nèi)部類可以無條件訪問外部類的所有成員屬性和成員方法(包括private成員和靜態(tài)成員)葡缰。

class Circle {

private double radius = 0;

public static int count =1;

public Circle(double radius) {

    this.radius = radius;

}

 

class Draw {     //內(nèi)部類

    public void drawSahpe() {

        System.out.println(radius);  //外部類的private成員

        System.out.println(count);   //外部類的靜態(tài)成員

    }

}

}

不過要注意的是,當(dāng)成員內(nèi)部類擁有和外部類同名的成員變量或者方法時忱反,會發(fā)生隱藏現(xiàn)象泛释,即默認(rèn)情況下訪問的是成員內(nèi)部類的成員。如果要訪問外部類的同名成員温算,需要以下面的形式進(jìn)行訪問:

外部類.this.成員變量

外部類.this.成員方法

雖然成員內(nèi)部類可以無條件地訪問外部類的成員怜校,而外部類想訪問成員內(nèi)部類的成員卻不是這么隨心所欲了。在外部類中如果要訪問成員內(nèi)部類的成員注竿,必須先創(chuàng)建一個成員內(nèi)部類的對象茄茁,再通過指向這個對象的引用來訪問:

class Circle {

private double radius = 0;



public Circle(double radius) {

    this.radius = radius;

    getDrawInstance().drawSahpe();   //必須先創(chuàng)建成員內(nèi)部類的對象,再進(jìn)行訪問

}

 

private Draw getDrawInstance() {

    return new Draw();

}

 

class Draw {     //內(nèi)部類

    public void drawSahpe() {

        System.out.println(radius);  //外部類的private成員

    }

}

}

成員內(nèi)部類是依附外部類而存在的巩割,也就是說裙顽,如果要創(chuàng)建成員內(nèi)部類的對象,前提是必須存在一個外部類的對象宣谈。創(chuàng)建成員內(nèi)部類對象的一般方式如下:

public class Test {

public static void main(String[] args)  {

    //第一種方式:

    Outter outter = new Outter();

    Outter.Inner inner = outter.new Inner();  //必須通過Outter對象來創(chuàng)建

     

    //第二種方式:

    Outter.Inner inner1 = outter.getInnerInstance();

}

}



class Outter {

private Inner inner = null;

public Outter() {

 

}

 

public Inner getInnerInstance() {

if(inner == null)

inner = new Inner();

return inner;

}

  

class Inner {

public Inner() {

}

}

}

內(nèi)部類可以擁有private訪問權(quán)限愈犹、protected訪問權(quán)限、public訪問權(quán)限及包訪問權(quán)限蒲祈。比如上面的例子甘萧,如果成員內(nèi)部類Inner用private修飾,則只能在外部類的內(nèi)部訪問梆掸,如果用public修飾扬卷,則任何地方都能訪問;如果用protected修飾酸钦,則只能在同一個包下或者繼承外部類的情況下訪問怪得;如果是默認(rèn)訪問權(quán)限,則只能在同一個包下訪問卑硫。這一點和外部類有一點不一樣徒恋,外部類只能被public和包訪問兩種權(quán)限修飾。我個人是這么理解的欢伏,由于成員內(nèi)部類看起來像是外部類的一個成員入挣,所以可以像類的成員一樣擁有多種權(quán)限修飾。

局部內(nèi)部類

局部內(nèi)部類是定義在一個方法或者一個作用域里面的類硝拧,它和成員內(nèi)部類的區(qū)別在于局部內(nèi)部類的訪問僅限于方法內(nèi)或者該作用域內(nèi)径筏。

class People{

    public People() {
    
     
    
    }
    class Man{
    
        public Man(){
        
         
        
        }
    
     
    
        public People getWoman(){
        
            class Woman extends People{   //局部內(nèi)部類
            
                int age =0;
            
            }
    
        return new Woman();
    
    }

}

注意葛假,局部內(nèi)部類就像是方法里面的一個局部變量一樣,是不能有public滋恬、protected聊训、private以及static修飾符的。

匿名內(nèi)部類

匿名內(nèi)部類應(yīng)該是平時我們編寫代碼時用得最多的恢氯,在編寫事件監(jiān)聽的代碼時使用匿名內(nèi)部類不但方便带斑,而且使代碼更加容易維護(hù)。下面這段代碼是一段Android事件監(jiān)聽代碼:

scan_bt.setOnClickListener(new OnClickListener() {

 

    @Override
    
    public void onClick(View v) {
    
    // TODO Auto-generated method stub
    
     
    
    }

});

 

history_bt.setOnClickListener(new OnClickListener() {

 

    @Override
    
    public void onClick(View v) {
    
    // TODO Auto-generated method stub
    
     
    
    }

});

匿名內(nèi)部類是唯一一種沒有構(gòu)造器的類勋拟。正因為其沒有構(gòu)造器勋磕,所以匿名內(nèi)部類的使用范圍非常有限,大部分匿名內(nèi)部類用于接口回調(diào)指黎。匿名內(nèi)部類在編譯的時候由系統(tǒng)自動起名為Outter$1.class朋凉。一般來說,匿名內(nèi)部類用于繼承其他類或是實現(xiàn)接口醋安,并不需要增加額外的方法杂彭,只是對繼承方法的實現(xiàn)或是重寫。

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

靜態(tài)內(nèi)部類也是定義在另一個類里面的類吓揪,只不過在類的前面多了一個關(guān)鍵字static亲怠。靜態(tài)內(nèi)部類是不需要依賴于外部類的,這點和類的靜態(tài)成員屬性有點類似柠辞,并且它不能使用外部類的非static成員變量或者方法团秽,這點很好理解,因為在沒有外部類的對象的情況下叭首,可以創(chuàng)建靜態(tài)內(nèi)部類的對象习勤,如果允許訪問外部類的非static成員就會產(chǎn)生矛盾,因為外部類的非static成員必須依附于具體的對象焙格。

public class Test {

    public static void main(String[] args)  {
    
        Outter.Inner inner = new Outter.Inner();
    
    }

}

 

class Outter {

    public Outter() {
    
     
    
    }

 

    static class Inner {
    
        public Inner() {
        
         
        
        }

    }

}

深入理解內(nèi)部類

為什么成員內(nèi)部類可以無條件訪問外部類的成員图毕?

在此之前,我們已經(jīng)討論過了成員內(nèi)部類可以無條件訪問外部類的成員眷唉,那具體究竟是如何實現(xiàn)的呢予颤?下面通過反編譯字節(jié)碼文件看看究竟。事實上冬阳,編譯器在進(jìn)行編譯的時候蛤虐,會將成員內(nèi)部類單獨(dú)編譯成一個字節(jié)碼文件。

雖然我們在定義的內(nèi)部類的構(gòu)造器是無參構(gòu)造器肝陪,編譯器還是會默認(rèn)添加一個參數(shù)驳庭,該參數(shù)的類型為指向外部類對象的一個引用,所以成員內(nèi)部類中的Outter this&0 指針便指向了外部類對象氯窍,因此可以在成員內(nèi)部類中隨意訪問外部類的成員嚷掠。從這里也間接說明了成員內(nèi)部類是依賴于外部類的捏检,如果沒有創(chuàng)建外部類的對象荞驴,則無法對Outter this&0引用進(jìn)行初始化賦值不皆,也就無法創(chuàng)建成員內(nèi)部類的對象了。

為什么局部內(nèi)部類和匿名內(nèi)部類只能訪問局部final變量熊楼?

如果局部變量的值在編譯期間就可以確定霹娄,則直接在匿名內(nèi)部里面創(chuàng)建一個拷貝。如果局部變量的值無法在編譯期間確定鲫骗,則通過構(gòu)造器傳參的方式來對拷貝進(jìn)行初始化賦值犬耻。

從上面可以看出,在run方法中訪問的變量a根本就不是test方法中的局部變量a执泰。這樣一來就解決了前面所說的 生命周期不一致的問題枕磁。但是新的問題又來了,既然在run方法中訪問的變量a和test方法中的變量a不是同一個變量术吝,當(dāng)在run方法中改變變量a的值的話计济,會出現(xiàn)什么情況?

對排苍,會造成數(shù)據(jù)不一致性沦寂,這樣就達(dá)不到原本的意圖和要求。為了解決這個問題淘衙,java編譯器就限定必須將變量a限制為final變量传藏,不允許對變量a進(jìn)行更改(對于引用類型的變量,是不允許指向新的對象)彤守,這樣數(shù)據(jù)不一致性的問題就得以解決了毯侦。

靜態(tài)內(nèi)部類有特殊的地方嗎?

從前面可以知道具垫,靜態(tài)內(nèi)部類是不依賴于外部類的侈离,也就說可以在不創(chuàng)建外部類對象的情況下創(chuàng)建內(nèi)部類的對象。另外做修,靜態(tài)內(nèi)部類是不持有指向外部類對象的引用的霍狰,這個讀者可以自己嘗試反編譯class文件看一下就知道了,是沒有Outter this&0引用的饰及。

內(nèi)部類的使用場景和好處

為什么在Java中需要內(nèi)部類蔗坯?總結(jié)一下主要有以下四點:

1.每個內(nèi)部類都能獨(dú)立的繼承一個接口的實現(xiàn),所以無論外部類是否已經(jīng)繼承了某個(接口的)實現(xiàn)燎含,對于內(nèi)部類都沒有影響宾濒。內(nèi)部類使得多繼承的解決方案變得完整,

2.方便將存在一定邏輯關(guān)系的類組織在一起屏箍,又可以對外界隱藏绘梦。

3.方便編寫事件驅(qū)動程序

4.方便編寫線程代碼

個人覺得第一點是最重要的原因之一橘忱,內(nèi)部類的存在使得Java的多繼承機(jī)制變得更加完善。

常見的與內(nèi)部類相關(guān)的筆試面試題

1.根據(jù)注釋填寫(1)卸奉,(2)钝诚,(3)處的代碼

public class Test{

    public static void main(String[] args){
    
           // 初始化Bean1
        
           (1)
        
           bean1.I++;
        
           // 初始化Bean2
        
           (2)
        
           bean2.J++;
        
           //初始化Bean3
        
           (3)
        
           bean3.k++;
    
    }

    class Bean1{
    
       public int I = 0;
    
    }

 

    static class Bean2{
    
       public int J = 0;
    
    }

}

 

class Bean{

    class Bean3{
    
       public int k = 0;
    
    }

}

從前面可知,對于成員內(nèi)部類榄棵,必須先產(chǎn)生外部類的實例化對象凝颇,才能產(chǎn)生內(nèi)部類的實例化對象。而靜態(tài)內(nèi)部類不用產(chǎn)生外部類的實例化對象即可產(chǎn)生內(nèi)部類的實例化對象疹鳄。

創(chuàng)建靜態(tài)內(nèi)部類對象的一般形式為:

外部類類名.內(nèi)部類類名 xxx = new 外部類類名.內(nèi)部類類名()

創(chuàng)建成員內(nèi)部類對象的一般形式為:

外部類類名.內(nèi)部類類名 xxx = 外部類對象名.new 內(nèi)部類類名()

因此拧略,(1),(2)瘪弓,(3)處的代碼分別為:

Test test = new Test();

Test.Bean1 bean1 = test.new Bean1();  

Test.Bean2 b2 = new Test.Bean2();

Bean bean = new Bean();     

Bean.Bean3 bean3 =  bean.new Bean3();   

2.下面這段代碼的輸出結(jié)果是什么垫蛆?

public class Test {

    public static void main(String[] args)  {

        Outter outter = new Outter();

        outter.new Inner().print();

    }

}





class Outter

{

    private int a = 1;

    class Inner {

        private int a = 2;

        public void print() {

            int a = 3;

            System.out.println("局部變量:" + a);

            System.out.println("內(nèi)部類變量:" + this.a);

            System.out.println("外部類變量:" + Outter.this.a);

        }

    }

}
3
2
1

最后補(bǔ)充一點知識:關(guān)于成員內(nèi)部類的繼承問題。一般來說腺怯,內(nèi)部類是很少用來作為繼承用的袱饭。但是當(dāng)用來繼承的話,要注意兩點:

1)成員內(nèi)部類的引用方式必須為 Outter.Inner.

2)構(gòu)造器中必須有指向外部類對象的引用瓢喉,并通過這個引用調(diào)用super()宁赤。這段代碼摘自《Java編程思想》

class WithInner {

    class Inner{

         

    }

}

class InheritInner extends WithInner.Inner { 

      

    // InheritInner() 是不能通過編譯的,一定要加上形參 

    InheritInner(WithInner wi) { 

        wi.super(); //必須有這句調(diào)用

    } 

  

    public static void main(String[] args) { 

        WithInner wi = new WithInner(); 

        InheritInner obj = new InheritInner(wi); 

    } 

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末栓票,一起剝皮案震驚了整個濱河市决左,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌走贪,老刑警劉巖佛猛,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異坠狡,居然都是意外死亡继找,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門逃沿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來婴渡,“玉大人,你說我怎么就攤上這事凯亮”呔剩” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵假消,是天一觀的道長柠并。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么臼予? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任鸣戴,我火速辦了婚禮,結(jié)果婚禮上粘拾,老公的妹妹穿的比我還像新娘窄锅。我一直安慰自己,他們只是感情好半哟,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布酬滤。 她就那樣靜靜地躺著,像睡著了一般寓涨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上氯檐,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天戒良,我揣著相機(jī)與錄音,去河邊找鬼冠摄。 笑死糯崎,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的河泳。 我是一名探鬼主播沃呢,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拆挥!你這毒婦竟也來了薄霜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤纸兔,失蹤者是張志新(化名)和其女友劉穎惰瓜,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體汉矿,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡崎坊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了洲拇。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奈揍。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖赋续,靈堂內(nèi)的尸體忽然破棺而出男翰,到底是詐尸還是另有隱情,我是刑警寧澤蚕捉,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布奏篙,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏秘通。R本人自食惡果不足惜为严,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望肺稀。 院中可真熱鬧第股,春花似錦、人聲如沸话原。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽繁仁。三九已至涉馅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間黄虱,已是汗流浹背稚矿。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留捻浦,地道東北人晤揣。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像朱灿,于是被迫代替她去往敵國和親昧识。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

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

  • Java 內(nèi)部類 分四種:成員內(nèi)部類盗扒、局部內(nèi)部類跪楞、靜態(tài)內(nèi)部類和匿名內(nèi)部類。 1环疼、成員內(nèi)部類: 即作為外部類的一個成...
    ikaroskun閱讀 1,232評論 0 13
  • 一、繼承 當(dāng)兩個事物之間存在一定的所屬關(guān)系伪阶,即就像孩子從父母那里得到遺傳基因一樣煞檩,當(dāng)然,java要遺傳的更完美栅贴,這...
    玉圣閱讀 1,052評論 0 2
  • 1斟湃、內(nèi)部類分類: 成員內(nèi)部類 局部內(nèi)部類 匿名內(nèi)部類 靜態(tài)內(nèi)部類 2、成員內(nèi)部類 1.概念: 定義在一個類內(nèi)部的類...
    M_JCs閱讀 902評論 0 9
  • 漫長告別注意檐薯,重要角色死亡注意凝赛,后續(xù)可能安樂死出沒注意注暗。 CONSTELLATION Summary: 邁克羅夫特...
    LOFTER我是你爸爸閱讀 445評論 0 0
  • 韻 轍: 坡 梭 轍, 韻 母:e, o, uo. 注 意 事 項 凡參加短詩接龍的作品都要依照規(guī)定的韻轍接龍 接...
    艷菊疏影閱讀 1,104評論 0 0