java提高篇(七)-----詳解內部類

來源:互聯網
上載者:User
     

可以將一個類的定義放在另一個類的定義內部,這就是內部類。

       內部類是一個非常有用的特性但又比較難理解使用的特性(鄙人到現在都沒有怎麼使用過內部類,對內部類也只是略知一二)。        第一次見面

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

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就是內部類,對於初學者來說內部類實在是使用的不多,鄙人菜鳥一個同樣沒有怎麼使用過(貌似僅僅只在做swing 註冊事件中使用過),但是隨著編程能力的提高,我們會領悟到它的魅力所在,它可以使用能夠更加優雅的設計我們的程式結構。在使用內部類之間我們需要明白為什麼要使用內部類,內部類能夠為我們帶來什麼樣的好處。


       一、為什麼要使用內部類

       為什麼要使用內部類。在《Think in java》中有這樣一句話:使用內部類最迷人的原因是:每個內部類都能獨立地繼承一個(介面的)實現,所以無論外圍類是否已經繼承了某個(介面的)實現,對於內部類都沒有影響。

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

public interface Father {}public interface Mother {}public class Son implements Father, Mother {}public class Daughter implements Father{    class Mother_ implements Mother{            }}

      其實對於這個執行個體我們確實是看不出來使用內部類存在何種優點,但是如果Father、Mother不是介面,而是抽象類別或者具體類呢。這個時候我們就只能使用內部類才能實現多重繼承了。

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

      1、內部類可以用多個執行個體,每個執行個體都有自己的狀態資訊,並且與其他外圍對象的資訊相互獨立。

      2、在單個外圍類中,可以讓多個內部類以不同的方式實現同一個介面,或者繼承同一個類。

      3、建立內部類對象的時刻並不依賴於外圍類對象的建立。

      4、內部類並沒有令人迷惑的“is-a”關係,他就是一個獨立的實體。

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


       二、內部類基礎

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

       當我們在建立一個內部類的時候,它無形中就與外圍類有了一種聯絡,依賴於這種聯絡,它可以無限制地訪問外圍類的元素。

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

       在這個應用程式中,我們可以看到內部了InnerClass可以對外圍類OuterClass的屬性進行無縫的訪問,儘管它是private修飾的。這是因為當我們在建立某個外圍類的內部類對象時,此時內部類對象必定會捕獲一個指向那個外圍類對象的引用,只要我們在訪問外圍類的成員時,就會用這個引用來選擇外圍類的成員。

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

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

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...

       到這裡了我們需要明確一點,內部類是個編譯時間的概念,一旦編譯成功後,它就與外圍類屬於兩個完全不同的類(當然他們之間還是有聯絡的)。對於一個名為OuterClass的外圍類和一個名為InnerClass的內部類,在編譯成功後,會出現這樣兩個class檔案:OuterClass.class和OuterClass$InnerClass.class。

       在Java中內部類主要分為成員內部類、局部內部類、匿名內部類、靜態內部類。


       三、成員內部類

       成員內部類也是最普通的內部類,它是外圍類的一個成員,所以他是可以無限制的訪問外圍類的所有 成員屬性和方法,儘管是private的,但是外圍類要訪問內部類的成員屬性和方法則需要通過內部類執行個體來訪問。

       在成員內部類中要注意兩點,第一:成員內部類中不能存在任何static的變數和方法;第二:成員內部類是依附於外圍類的,所以只有先建立了外圍類才能夠建立內部類。

public class OuterClass {    private String str;        public void outerDisplay(){        System.out.println("outerClass...");    }        public class InnerClass{        public void innerDisplay(){            //使用外圍內的屬性            str = "chenssy...";            System.out.println(str);            //使用外圍內的方法            outerDisplay();        }    }        /*推薦使用getxxx()來擷取成員內部類,尤其是該內部類的建構函式無參數時 */    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()來擷取成員內部類,尤其是該內部類的建構函式無參數時 。


       四、局部內部類

       有這樣一種內部類,它是嵌套在方法和作用於內的,對於這個類的使用主要是應用與解決比較複雜的問題,想建立一個類來輔助我們的解決方案,到那時又不希望這個類是公用可用的,所以就產生了局部內部類,局部內部類和成員內部類一樣被編譯,只是它的範圍發生了改變,它只能在該方法和屬性中被使用,出了該方法和屬性就會失效。

       對於局部內部類實在是想不出什麼好例子,所以就引用《Think in java》中的經典例子了。

       定義在方法裡:

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");    }}

       定義在範圍內:

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();    }}


       五、匿名內部類

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

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

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

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、 匿名內部類是沒有存取修飾詞的。

      2、 new 匿名內部類,這個類首先是要存在的。如果我們將那個InnerClass介面注釋掉,就會出現編譯出錯。

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

      4、 匿名內部類是沒有構造方法的。因為它連名字都沒有何來構造方法。

PS:由於篇幅有限,對匿名內部類就介紹到這裡,有關更多關於匿名內部類的知識,我就會在下篇部落格(java提高篇-----詳解匿名內部類)做詳細的介紹,包括為何形參要定義成final,怎麼對匿名內部類進行初始化等等,敬請期待……


       六、靜態內部類

       在java提高篇-----關鍵字static中提到Static可以修飾成員變數、方法、代碼塊,其他它還可以修飾內部類,使用static修飾的內部類我們稱之為靜態內部類,不過我們更喜歡稱之為嵌套內部類。靜態內部類與非靜態內部類之間存在一個最大的區別,我們知道非靜態內部類在編譯完成之後會隱含地儲存著一個引用,該引用是指向建立它的外圍內,但是靜態內部類卻沒有。沒有這個引用就意味著:

      1、 它的建立是不需要依賴於外圍類的。

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

public class OuterClass {    private String sex;    public static String name = "chenssy";        /**     *靜態內部類     */    static class InnerClass1{        /* 在靜態內部類中可以存在靜態成員 */        public static String _name1 = "chenssy_static";                public void display(){            /*              * 靜態內部類只能訪問外圍類的靜態成員變數和方法             * 不能訪問外圍類的非靜態成員變數和方法             */            System.out.println("OutClass name :" + name);        }    }        /**     * 非靜態內部類     */    class InnerClass2{        /* 非靜態內部類中不能存在靜態成員 */        public String _name2 = "chenssy_inner";        /* 非靜態內部類中可以調用外圍類的任何成員,不管是靜態還是非靜態 */        public void display(){            System.out.println("OuterClass name:" + name);        }    }        /**     * @desc 外圍類方法     * @author chenssy     * @data 2013-10-25     * @return void     */    public void display(){        /* 外圍類訪問靜態內部類:內部類. */        System.out.println(InnerClass1._name1);        /* 靜態內部類 可以直接建立執行個體不需要依賴於外圍類 */        new InnerClass1().display();                /* 非靜態內部的建立需要依賴於外圍類 */        OuterClass.InnerClass2 inner2 = new OuterClass().new InnerClass2();        /* 方位非靜態內部類的成員需要使用非靜態內部類的執行個體 */        System.out.println(inner2._name2);        inner2.display();    }        public static void main(String[] args) {        OuterClass outer = new OuterClass();        outer.display();    }}----------------Output:chenssy_staticOutClass name :chenssychenssy_innerOuterClass name:chenssy

       上面這個例子充分展現了靜態內部類和非靜態內部類的區別。

       到這裡內部類的介紹就基本結束了。對於內部類其實本人認識也只是皮毛,逼近菜鳥一枚,認知有限。我會利用這幾天時間好好研究內部類!

鞏固基礎,提高技術,不懼困難,攀登高峰。。。。。。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.