鍍金池/ 教程/ Java/ 詳解內(nèi)部類
Java 集合細節(jié)(四):保持 compareTo 和 equals 同步
Iterator
使用序列化實現(xiàn)對象的拷貝
fail-fast 機制
關(guān)鍵字 final
Vector
HashTable
Java 集合細節(jié)(一):請為集合指定初始容量
強制類型轉(zhuǎn)換
數(shù)組之一:認識 JAVA 數(shù)組
Java 集合細節(jié)(三):subList 的缺陷
hashCode
ArrayList
數(shù)組之二
List 總結(jié)
LinkedList
Java 提高篇(九)—–實現(xiàn)多重繼承
Java 的四舍五入
關(guān)鍵字 static
理解 Java 的三大特性之多態(tài)
抽象類與接口
集合大家族
異常(二)
Java 集合細節(jié)(二):asList 的缺陷
Map 總結(jié)
TreeSet
equals() 方法總結(jié)
Java 提高篇(十)—–詳解匿名內(nèi)部類
HashMap
Stack
詳解內(nèi)部類
TreeMap
異常(一)
詳解 Java 定時任務(wù)
HashSet
字符串
理解 Java 的三大特性之繼承
理解 Java 的三大特性之封裝
代碼塊

詳解內(nèi)部類

可以將一個類的定義放在另一個類的定義內(nèi)部,這就是內(nèi)部類。

內(nèi)部類是一個非常有用的特性但又比較難理解使用的特性(鄙人到現(xiàn)在都沒有怎么使用過內(nèi)部類,對內(nèi)部類也只是略知一二)。

第一次見面

內(nèi)部類我們從外面看是非常容易理解的,無非就是在一個類的內(nèi)部在定義一個類。


    public class OuterClass {
        private String name ;
        private int age;

        public String getName() {
            return name;
        }

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

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        class InnerClass{
            public InnerClass(){
                name = "chenssy";
                age = 23;
            }
        }
    }

在這里 InnerClass 就是內(nèi)部類,對于初學(xué)者來說內(nèi)部類實在是使用的不多,鄙人菜鳥一個同樣沒有怎么使用過(貌似僅僅只在做 swing 注冊事件中使用過),但是隨著編程能力的提高,我們會領(lǐng)悟到它的魅力所在,它可以使用能夠更加優(yōu)雅的設(shè)計我們的程序結(jié)構(gòu)。在使用內(nèi)部類之間我們需要明白為什么要使用內(nèi)部類,內(nèi)部類能夠為我們帶來什么樣的好處。

一、為什么要使用內(nèi)部類

為什么要使用內(nèi)部類?在《Think in java》中有這樣一句話:使用內(nèi)部類最吸引人的原因是:每個內(nèi)部類都能獨立地繼承一個(接口的)實現(xiàn),所以無論外圍類是否已經(jīng)繼承了某個(接口的)實現(xiàn),對于內(nèi)部類都沒有影響。

在我們程序設(shè)計中有時候會存在一些使用接口很難解決的問題,這個時候我們可以利用內(nèi)部類提供的、可以繼承多個具體的或者抽象的類的能力來解決這些程序設(shè)計問題??梢赃@樣說,接口只是解決了部分問題,而內(nèi)部類使得多重繼承的解決方案變得更加完整。


    public interface Father {

    }

    public interface Mother {

    }

    public class Son implements Father, Mother {

    }

    public class Daughter implements Father{

        class Mother_ implements Mother{

        }
    }

其實對于這個實例我們確實是看不出來使用內(nèi)部類存在何種優(yōu)點,但是如果 Father、Mother 不是接口,而是抽象類或者具體類呢?這個時候我們就只能使用內(nèi)部類才能實現(xiàn)多重繼承了。

其實使用內(nèi)部類最大的優(yōu)點就在于它能夠非常好的解決多重繼承的問題,但是如果我們不需要解決多重繼承問題,那么我們自然可以使用其他的編碼方式,但是使用內(nèi)部類還能夠為我們帶來如下特性(摘自《Think in java》):

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

2、在單個外圍類中,可以讓多個內(nèi)部類以不同的方式實現(xiàn)同一個接口,或者繼承同一個類。

3、創(chuàng)建內(nèi)部類對象的時刻并不依賴于外圍類對象的創(chuàng)建。

4、內(nèi)部類并沒有令人迷惑的“is-a”關(guān)系,他就是一個獨立的實體。

5、內(nèi)部類提供了更好的封裝,除了該外圍類,其他類都不能訪問。

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

在這個部分主要介紹內(nèi)部類如何使用外部類的屬性和方法,以及使用.this與.new。

當(dāng)我們在創(chuàng)建一個內(nèi)部類的時候,它無形中就與外圍類有了一種聯(lián)系,依賴于這種聯(lián)系,它可以無限制地訪問外圍類的元素。


    public class OuterClass {
        private String name ;
        private int age;

        /**省略getter和setter方法**/

        public class InnerClass{
            public InnerClass(){
                name = "chenssy";
                age = 23;
            }

            public void display(){
                System.out.println("name:" + getName() +"   ;age:" + getAge());
            }
        }

        public static void main(String[] args) {
            OuterClass outerClass = new OuterClass();
            OuterClass.InnerClass innerClass = outerClass.new InnerClass();
            innerClass.display();
        }
    }
    --------------
    Output:
    name:chenssy   ;age:23

在這個應(yīng)用程序中,我們可以看到內(nèi)部了 InnerClass 可以對外圍類 OuterClass 的屬性進行無縫的訪問,盡管它是 private 修飾的。這是因為當(dāng)我們在創(chuàng)建某個外圍類的內(nèi)部類對象時,此時內(nèi)部類對象必定會捕獲一個指向那個外圍類對象的引用,只要我們在訪問外圍類的成員時,就會用這個引用來選擇外圍類的成員。

其實在這個應(yīng)用程序中我們還看到了如何來引用內(nèi)部類:引用內(nèi)部類我們需要指明這個對象的類型:OuterClasName.InnerClassName。同時如果我們需要創(chuàng)建某個內(nèi)部類對象,必須要利用外部類的對象通過.new 來創(chuàng)建內(nèi)部類: OuterClass.InnerClass innerClass = outerClass.new InnerClass();。

同時如果我們需要生成對外部類對象的引用,可以使用 OuterClassName.this,這樣就能夠產(chǎn)生一個正確引用外部類的引用了。當(dāng)然這點實在編譯期就知曉了,沒有任何運行時的成本。


    public class OuterClass {
        public void display(){
            System.out.println("OuterClass...");
        }

        public class InnerClass{
            public OuterClass getOuterClass(){
                return OuterClass.this;
            }
        }

        public static void main(String[] args) {
            OuterClass outerClass = new OuterClass();
            OuterClass.InnerClass innerClass = outerClass.new InnerClass();
            innerClass.getOuterClass().display();
        }
    }
    -------------
    Output:
    OuterClass...

到這里了我們需要明確一點,內(nèi)部類是個編譯時的概念,一旦編譯成功后,它就與外圍類屬于兩個完全不同的類(當(dāng)然他們之間還是有聯(lián)系的)。對于一個名為 OuterClass 的外圍類和一個名為 InnerClass 的內(nèi)部類,在編譯成功后,會出現(xiàn)這樣兩個 class 文件:OuterClass.class 和OuterClass$InnerClass.class。

在 Java 中內(nèi)部類主要分為成員內(nèi)部類、局部內(nèi)部類、匿名內(nèi)部類、靜態(tài)內(nèi)部類。

三、成員內(nèi)部類

成員內(nèi)部類也是最普通的內(nèi)部類,它是外圍類的一個成員,所以他是可以無限制的訪問外圍類的所有 成員屬性和方法,盡管是 private 的,但是外圍類要訪問內(nèi)部類的成員屬性和方法則需要通過內(nèi)部類實例來訪問。

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


    public class OuterClass {
        private String str;

        public void outerDisplay(){
            System.out.println("outerClass...");
        }

        public class InnerClass{
            public void innerDisplay(){
                //使用外圍內(nèi)的屬性
                str = "chenssy...";
                System.out.println(str);
                //使用外圍內(nèi)的方法
                outerDisplay();
            }
        }

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

        public static void main(String[] args) {
            OuterClass outer = new OuterClass();
            OuterClass.InnerClass inner = outer.getInnerClass();
            inner.innerDisplay();
        }
    }
    --------------------
    chenssy...
    outerClass...

推薦使用 getxxx() 來獲取成員內(nèi)部類,尤其是該內(nèi)部類的構(gòu)造函數(shù)無參數(shù)時 。

四、局部內(nèi)部類

有這樣一種內(nèi)部類,它是嵌套在方法和作用于內(nèi)的,對于這個類的使用主要是應(yīng)用與解決比較復(fù)雜的問題,想創(chuàng)建一個類來輔助我們的解決方案,到那時又不希望這個類是公共可用的,所以就產(chǎn)生了局部內(nèi)部類,局部內(nèi)部類和成員內(nèi)部類一樣被編譯,只是它的作用域發(fā)生了改變,它只能在該方法和屬性中被使用,出了該方法和屬性就會失效。

對于局部內(nèi)部類實在是想不出什么好例子,所以就引用《Think in Java》中的經(jīng)典例子了。

定義在方法里:


    public class Parcel5 {
        public Destionation destionation(String str){
            class PDestionation implements Destionation{
                private String label;
                private PDestionation(String whereTo){
                    label = whereTo;
                }
                public String readLabel(){
                    return label;
                }
            }
            return new PDestionation(str);
        }

        public static void main(String[] args) {
            Parcel5 parcel5 = new Parcel5();
            Destionation d = parcel5.destionation("chenssy");
        }
    }

定義在作用域內(nèi):


    public class Parcel6 {
        private void internalTracking(boolean b){
            if(b){
                class TrackingSlip{
                    private String id;
                    TrackingSlip(String s) {
                        id = s;
                    }
                    String getSlip(){
                        return id;
                    }
                }
                TrackingSlip ts = new TrackingSlip("chenssy");
                String string = ts.getSlip();
            }
        }

        public void track(){
            internalTracking(true);
        }

        public static void main(String[] args) {
            Parcel6 parcel6 = new Parcel6();
            parcel6.track();
        }
    }

五、匿名內(nèi)部類

在做 Swing 編程中,我們經(jīng)常使用這種方式來綁定事件


    button2.addActionListener(  
                    new ActionListener(){  
                        public void actionPerformed(ActionEvent e) {  
                            System.out.println("你按了按鈕二");  
                        }  
                    });

我們咋一看可能覺得非常奇怪,因為這個內(nèi)部類是沒有名字的,在看如下這個例子:


    public class OuterClass {
        public InnerClass getInnerClass(final int   num,String str2){
            return new InnerClass(){
                int number = num + 3;
                public int getNumber(){
                    return number;
                }
            };        /* 注意:分號不能省 */
        }

        public static void main(String[] args) {
            OuterClass out = new OuterClass();
            InnerClass inner = out.getInnerClass(2, "chenssy");
            System.out.println(inner.getNumber());
        }
    }

    interface InnerClass {
        int getNumber();
    }

    ----------------
    Output:
    5

這里我們就需要看清幾個地方

1、匿名內(nèi)部類是沒有訪問修飾符的。

2、new 匿名內(nèi)部類,這個類首先是要存在的。如果我們將那個 InnerClass 接口注釋掉,就會出現(xiàn)編譯出錯。

3、注意 getInnerClass() 方法的形參,第一個形參是用 final 修飾的,而第二個卻沒有。同時我們也發(fā)現(xiàn)第二個形參在匿名內(nèi)部類中沒有使用過,所以當(dāng)所在方法的形參需要被匿名內(nèi)部類使用,那么這個形參就必須為 final。

4、匿名內(nèi)部類是沒有構(gòu)造方法的。因為它連名字都沒有何來構(gòu)造方法。

PS:由于篇幅有限,對匿名內(nèi)部類就介紹到這里,有關(guān)更多關(guān)于匿名內(nèi)部類的知識,我就會在下篇博客(Java 提高篇—–詳解匿名內(nèi)部類)做詳細的介紹,包括為何形參要定義成 final,怎么對匿名內(nèi)部類進行初始化等等,敬請期待……

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

在 Java 提高篇—–關(guān)鍵字 static 中提到 Static 可以修飾成員變量、方法、代碼塊,其他它還可以修飾內(nèi)部類,使用 static 修飾的內(nèi)部類我們稱之為靜態(tài)內(nèi)部類,不過我們更喜歡稱之為嵌套內(nèi)部類。靜態(tài)內(nèi)部類與非靜態(tài)內(nèi)部類之間存在一個最大的區(qū)別,我們知道非靜態(tài)內(nèi)部類在編譯完成之后會隱含地保存著一個引用,該引用是指向創(chuàng)建它的外圍內(nèi),但是靜態(tài)內(nèi)部類卻沒有。沒有這個引用就意味著:

1、它的創(chuàng)建是不需要依賴于外圍類的。

2、它不能使用任何外圍類的非static成員變量和方法。


    public class OuterClass {
        private String sex;
        public static String name = "chenssy";

        /**
         *靜態(tài)內(nèi)部類
         */
        static class InnerClass1{
            /* 在靜態(tài)內(nèi)部類中可以存在靜態(tài)成員 */
            public static String _name1 = "chenssy_static";

            public void display(){
                /* 
                 * 靜態(tài)內(nèi)部類只能訪問外圍類的靜態(tài)成員變量和方法
                 * 不能訪問外圍類的非靜態(tài)成員變量和方法
                 */
                System.out.println("OutClass name :" + name);
            }
        }

        /**
         * 非靜態(tài)內(nèi)部類
         */
        class InnerClass2{
            /* 非靜態(tài)內(nèi)部類中不能存在靜態(tài)成員 */
            public String _name2 = "chenssy_inner";
            /* 非靜態(tài)內(nèi)部類中可以調(diào)用外圍類的任何成員,不管是靜態(tài)的還是非靜態(tài)的 */
            public void display(){
                System.out.println("OuterClass name:" + name);
            }
        }

        /**
         * @desc 外圍類方法
         * @author chenssy
         * @data 2013-10-25
         * @return void
         */
        public void display(){
            /* 外圍類訪問靜態(tài)內(nèi)部類:內(nèi)部類. */
            System.out.println(InnerClass1._name1);
            /* 靜態(tài)內(nèi)部類 可以直接創(chuàng)建實例不需要依賴于外圍類 */
            new InnerClass1().display();

            /* 非靜態(tài)內(nèi)部的創(chuàng)建需要依賴于外圍類 */
            OuterClass.InnerClass2 inner2 = new OuterClass().new InnerClass2();
            /* 方位非靜態(tài)內(nèi)部類的成員需要使用非靜態(tài)內(nèi)部類的實例  */
            System.out.println(inner2._name2);
            inner2.display();
        }

        public static void main(String[] args) {
            OuterClass outer = new OuterClass();
            outer.display();
        }
    }
    ----------------
    Output:
    chenssy_static
    OutClass name :chenssy
    chenssy_inner
    OuterClass name:chenssy

上面這個例子充分展現(xiàn)了靜態(tài)內(nèi)部類和非靜態(tài)內(nèi)部類的區(qū)別。

到這里內(nèi)部類的介紹就基本結(jié)束了!對于內(nèi)部類其實本人認識也只是皮毛,逼近菜鳥一枚,認知有限!我會利用這幾天時間好好研究內(nèi)部類!

鞏固基礎(chǔ),提高技術(shù),不懼困難,攀登高峰?。。。。。?/code>