Java內部類

轉載出處:http://www.reibang.com/p/e385ce41ca5b

概念

Java類中不僅可以定義變量和方法荚孵,還可以定義類纬朝,這樣定義在類內部的類就被稱為內部類。根據定義的方式不同判没,內部類分為靜態(tài)內部類隅茎,成員內部類,局部內部類俏竞,匿名內部類四種。
Java為什么要引入內部類這個概念呢?原因在于跃捣,內部類定義在類的內部,可以方便訪問外部類的變量和方法酣胀,并且和其它類進行隔離闻镶。

Thinking in java中對內部類的描述

1丸升、內部類可以用多個實例,每個實例都有自己的狀態(tài)信息墩剖,并且與其他外圍對象的信息相互獨立夷狰。

2、在單個外圍類中爷绘,可以讓多個內部類以不同的方式實現同一個接口进倍,或者繼承同一個類。

3毙籽、創(chuàng)建內部類對象的時刻并不依賴于外圍類對象的創(chuàng)建毡庆。

4、內部類并沒有令人迷惑的“is-a”關系毅否,他就是一個獨立的實體蝇刀。

5、內部類提供了更好的封裝捆探,除了該外圍類,其他類都不能訪問曾雕。

一 靜態(tài)內部類

定義在類內部的靜態(tài)類剖张,就是靜態(tài)內部類揩环。

1.語法

定義一個靜態(tài)內部類

public class Out {
    private static int a;
    private int b;

    public static class Inner {
        public void print() {
            System.out.println(a);
        }
    }
}

Inner就是靜態(tài)內部類。靜態(tài)內部類可以訪問外部類所有的靜態(tài)變量和方法顾犹,即使是private的也一樣褒墨。靜態(tài)內部類和一般類一致,可以定義靜態(tài)變量柬唯、方法圃庭,構造方法等。
其它類使用靜態(tài)內部類需要使用“外部類.靜態(tài)內部類”方式拘央,如下所示:

Out.Inner inner = new Out.Inner();
inner.print();

2.應用場景

Effictive Java中的builder模式就是利用的靜態(tài)內部類來建立外部類實例

Java集合類HashMap內部就有一個靜態(tài)內部類Entry灰伟。Entry是HashMap存放元素的抽象儒旬,HashMap內部維護Entry數組用了存放元素,但是Entry對使用者是透明的挡爵。像這種和外部類關系密切的甚垦,且不依賴外部類實例的涣雕,都可以使用靜態(tài)內部類挣郭。

二 成員內部類

定義在類內部的非靜態(tài)類疗韵,就是成員內部類。

1.語法

定義一個成員內部類:

public class Out {
    private static int a;
    private int b;

    public class Inner {
        public void print() {
            System.out.println(a);
            System.out.println(b);
        }

        public Out getOut(){
            return Out.this;
        }
    }

    /*推薦使用getxxx()來獲取成員內部類,尤其是該內部類的構造函數無參數時 */
    public InnerClass getInnerClass(){
        return new Inner();
    }
}

成員內部類可以訪問外部類所有的變量和方法彩库,包括靜態(tài)和實例,私有和非私有骇钦。和靜態(tài)內部類不同的是,每一個成員內部類的實例都依賴一個外部類的實例窥翩。其它類使用內部類必須要先創(chuàng)建一個外部類的實例寇蚊。如下所示:

Out out = new Out();
Out.Inner inner = out.new Inner();  //out.getInnerClass();
inner.print();

在成員內部類中要注意兩點
第一:成員內部類中不能存在任何static的變量和方法棍好;
第二:成員內部類是依附于外圍類的,所以只有先創(chuàng)建了外圍類才能夠創(chuàng)建內部類扒怖。

分析:非static的內部類业稼,在外部類加載的時候,并不會加載它俯邓,所以它里面不能有靜態(tài)變量或者靜態(tài)方法。
1熔号、static類型的屬性和方法,在類加載的時候就會存在于內存中川慌。
2、要使用某個類的static屬性或者方法兑燥,那么這個類必須要加載到jvm中琴拧。
基于以上兩點,可以看出挣饥,如果一個非static的內部類如果具有static的屬性或者方法沛膳,那么就會出現一種情況:內部類未加載,但是卻試圖在內存中創(chuàng)建static的屬性和方法短荐,這當然是錯誤的。原因:類還不存在叹哭,但卻希望操作它的屬性和方法。

三 局部內部類

定義在方法或作用域中的類糠排,就是局部內部類乳讥。

1.語法

定義一個局部內部類

public class Out {
    private static int a;
    private int b;

    public void test(final int c) {
        final int d = 1;
        class Inner {
            public void print() {
                System.out.println(a);
                System.out.println(b);
                System.out.println(c);
                System.out.println(d);
            }
        }

        //如果想在外部得到該內部類廓俭,必須繼承或者實現一個現有的類或接口,并且返回new Inner2();
        class Inner2 extends People{

            @Override
            public String readName() {
                return null;
            }

            public People getInner2(){
                return new Inner2();
            }
        }
    }

    public static void testStatic(final int c) {
        final int d = 1;
        class Inner {
            public void print() {
                System.out.println(a);
                //定義在靜態(tài)方法中的局部類不可以訪問外部類的實例變量
                //System.out.println(b);
                System.out.println(c);
                System.out.println(d);
            }
        }
    }
}

2.注意

1.定義在實例方法中的局部類可以訪問外部類的所有變量和方法
2.定義在靜態(tài)方法中的局部類只能訪問外部類的靜態(tài)變量和方法
3.局部類還可以訪問方法的參數和方法中的局部變量汹忠,這些參數和變量必須要聲明為final的(如果參數傳入進來而沒有被內部類使用則可以不用聲明為final的)宽菜。

四 匿名內部類

1.寫法

new 父類構造器(參數列表)|實現接口()  
    {  
     //匿名內部類的類體部分  
    }

在這里我們看到使用匿名內部類我們必須要繼承一個父類或者實現一個接口铅乡,當然也僅能只繼承一個父類或者實現一個接口烈菌。同時它也是沒有class關鍵字花履,這是因為匿名內部類是直接使用new來生成一個對象的引用诡壁。當然這個引用是隱式的荠割。

public abstract class Bird {
    private String name;

    public String getName() {
        return name;
    }

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

public class Test {
    
    public void test(Bird bird){
        System.out.println(bird.getName() + "能夠飛 " + bird.fly() + "米");
    }
    
    public static void main(String[] args) {
        Test test = new Test();
        test.test(new Bird() {
            
            public int fly() {
                return 10000;
            }
            
            public String getName() {
                return "大雁";
            }
        });
    }
}
------------------
Output:
大雁能夠飛 10000米

在Test類中蔑鹦,test()方法接受一個Bird類型的參數,同時我們知道一個抽象類是沒有辦法直接new的铺纽,我們必須要先有實現類才能new出來它的實現類實例火鼻。所以在mian方法中直接使用匿名內部類來創(chuàng)建一個Bird實例魁索。

 由于匿名內部類不能是抽象類,所以它必須要實現它的抽象父類或者接口里面所有的抽象方法粗蔚。

  對于這段匿名內部類代碼其實是可以拆分為如下形式:
public class WildGoose extends Bird{
    public int fly() {
        return 10000;
    }
    
    public String getName() {
        return "大雁";
    }
}

WildGoose wildGoose = new WildGoose();
test.test(wildGoose);

例子2

public class Super {

    public void test1(){
        System.out.println("superClass");
    }

}

Super s = new Super(){
            @Override
            public void test1() {
                System.out.println("sonClass");
            }
        };
s.test1();

在這里系統(tǒng)會創(chuàng)建一個繼承自Bird類的匿名類的對象呢诬,該對象轉型為對Bird類型的引用短绸。

  對于匿名內部類的使用它是存在一個缺陷的当辐,就是它僅能被使用一次鲤看,創(chuàng)建匿名內部類時它會立即創(chuàng)建一個該類的實例,該類的定義會立即消失找筝,所以匿名內部類是不能夠被重復使用慷吊。對于上面的實例,如果我們需要對test()方法里面內部類進行多次使用急鳄,建議重新定義類谤民,而不是使用匿名內部類赖临。

2.注意事項

在使用匿名內部類的過程中兢榨,我們需要注意如下幾點:

  1顺饮、使用匿名內部類時,我們必須是繼承一個類或者實現一個接口兼雄,但是兩者不可兼得,同時也只能繼承一個類或者實現一個接口块攒。

 2囱井、匿名內部類中是不能定義構造函數的趣避。

 3、匿名內部類中不能存在任何的靜態(tài)成員變量和靜態(tài)方法程帕。

  4、匿名內部類為局部內部類讲逛,所以局部內部類的所有限制同樣對匿名內部類生效妆绞。

 5、匿名內部類不能是抽象的括饶,它必須要實現繼承的類或者實現的接口的所有抽象方法

五 final

為何局部內部類(包含匿名內部類)来涨,它所使用到的外部的參數(來自外部方法或者外部類)必須是聲明為final的呢

分析

先定義一個接口:

public interface MyInterface {
    void doSomething();
}

然后創(chuàng)建這個接口的匿名子類:

public class TryUsingAnonymousClass {
    public void useMyInterface() {
        final Integer number = 123;
        System.out.println(number);

        MyInterface myInterface = new MyInterface() {
            @Override
            public void doSomething() {
                System.out.println(number);
            }
        };
        myInterface.doSomething();

        System.out.println(number);
    }
}

這個匿名子類會被編譯成一個單獨的類蹦掐,反編譯的結果是這樣的:

class TryUsingAnonymousClass$1
        implements MyInterface {
    private final TryUsingAnonymousClass this$0;
    private final Integer paramInteger;

    TryUsingAnonymousClass$1(TryUsingAnonymousClass this$0, Integer paramInteger) {
        this.this$0 = this$0;
        this.paramInteger = paramInteger;
    }

    public void doSomething() {
        System.out.println(this.paramInteger);
    }
}

可以看到名為number的局部變量是作為構造方法的參數傳入匿名內部類的(以上代碼經過了手動修改,真實的反編譯結果中有一些不可讀的命名)藤滥。

如果Java允許匿名內部類訪問非final的局部變量的話,那我們就可以在TryUsingAnonymousClass$1中修改paramInteger向图,但是這不會對number的值有影響标沪,因為它們是不同的reference。

這就會造成數據不同步的問題檩赢。

所以,Java為了避免數據不同步的問題贞瞒,做出了局部內部類(包含匿名內部類)只可以訪問final的局部變量的限制军浆。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末彰阴,一起剝皮案震驚了整個濱河市拍冠,隨后出現的幾起案子,更是在濱河造成了極大的恐慌庆杜,老刑警劉巖晃财,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異罗洗,居然都是意外死亡钢猛,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門贩绕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人馏鹤,你說我怎么就攤上這事娇哆。” “怎么了迂尝?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵垄开,是天一觀的道長。 經常有香客問我榜田,道長锻梳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任辩块,我火速辦了婚禮废亭,結果婚禮上具钥,老公的妹妹穿的比我還像新娘骂删。我一直安慰自己,他們只是感情好宁玫,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布欧瘪。 她就那樣靜靜地躺著,像睡著了一般凭迹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上脾猛,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天鱼鸠,我揣著相機與錄音,去河邊找鬼愉昆。 笑死麻蹋,一個胖子當著我的面吹牛,可吹牛的內容都是我干的芳室。 我是一名探鬼主播刹勃,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼荔仁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了次洼?” 一聲冷哼從身側響起滓玖,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤质蕉,失蹤者是張志新(化名)和其女友劉穎模暗,沒想到半個月后念祭,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡隶糕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年枚驻,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片尔邓。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡锉矢,死狀恐怖梯嗽,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情沽损,我是刑警寧澤灯节,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站绵估,受9級特大地震影響显晶,放射性物質發(fā)生泄漏。R本人自食惡果不足惜壹士,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一磷雇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧躏救,春花似錦唯笙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽苞慢。三九已至挽放,卻和暖如春辑畦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留分扎,地道東北人。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓菲饼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親饼煞。 傳聞我的和親對象是個殘疾皇子砖瞧,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內容

  • Java 內部類 分四種:成員內部類床未、局部內部類斋扰、靜態(tài)內部類和匿名內部類传货。 1损离、成員內部類: 即作為外部類的一個成...
    ikaroskun閱讀 1,232評論 0 13
  • Java允許在一個類里面定義另一個類十饥,類里面的類就是內部類逗堵。內部類看似簡單汁咏,其實里面大有乾坤攘滩,下面我們就來好好聊一...
    iDaniel閱讀 2,004評論 1 9
  • 內部類(Nested Class)仍是獨立的類,只不過被包含于其他類中女揭。編譯之后內部類會被編譯成獨立的.class...
    七弦桐語閱讀 426評論 0 4
  • 1. 聲明變量的時候,可以有下面兩種方式 上面兩種形式實際上是等價的 隱含類型局部變量(Local Variabl...
    Jason_Yuan閱讀 2,366評論 0 3
  • 什么是關鍵字和保留字? 關鍵字:已經被Java做為數據類型民逼、程序結構等使用的標識符稱為關鍵字 保留字:在Java的...
    前程科技閱讀 189評論 0 0