keep moving!

          We must not cease from exploration. And the end of all our exploring will be to arrive where we began and to know the place for the first time.
          隨筆 - 37, 文章 - 2, 評(píng)論 - 3, 引用 - 0
          數(shù)據(jù)加載中……

          Java內(nèi)部類1

          提起Java內(nèi)部類(Inner Class)可能很多人不太熟悉,實(shí)際上類似的概念在C++里也有,那就是嵌套類(Nested Class),關(guān)于這兩者的區(qū)別與聯(lián)系,在下文中會(huì)有對(duì)比。內(nèi)部類從表面上看,就是在類中又定義了一個(gè)類(下文會(huì)看到,內(nèi)部類可以在很多地方定義),而實(shí)際上并沒(méi)有那么簡(jiǎn)單,乍看上去內(nèi)部類似乎有些多余,它的用處對(duì)于初學(xué)者來(lái)說(shuō)可能并不是那么顯著,但是隨著對(duì)它的深入了解,你會(huì)發(fā)現(xiàn)Java的設(shè)計(jì)者在內(nèi)部類身上的確是用心良苦。學(xué)會(huì)使用內(nèi)部類,是掌握J(rèn)ava高級(jí)編程的一部分,它可以讓你更優(yōu)雅地設(shè)計(jì)你的程序結(jié)構(gòu)。下面從以下幾個(gè)方面來(lái)介紹:

            第一次見(jiàn)面

          public interface Contents {
           int value();
          }

          public interface Destination {
           String readLabel();
          }

          public class Goods {
           private class Content implements Contents {
            private int i = 11;
            public int value() { 
             return i; 
            }
           }

           protected class GDestination implements Destination {
            private String label;
            private GDestination(String whereTo) {
             label = whereTo;
            }
            public String readLabel() { 
             return label; 
            }
           }

           public Destination dest(String s) {
            return new GDestination(s);
           }
           public Contents cont() {
            return new Content();
           }
          }

          class TestGoods {
           public static void main(String[] args) {
            Goods p = new Goods();
            Contents c = p.cont();
            Destination d = p.dest("Beijing");
           }
          }  

            在這個(gè)例子里類Content和GDestination被定義在了類Goods內(nèi)部,并且分別有著protected和private修飾符來(lái)控制訪問(wèn)級(jí)別。Content代表著Goods的內(nèi)容,而GDestination代表著Goods的目的地。它們分別實(shí)現(xiàn)了兩個(gè)接口Content和Destination。在后面的main方法里,直接用 Contents c和Destination d進(jìn)行操作,你甚至連這兩個(gè)內(nèi)部類的名字都沒(méi)有看見(jiàn)!這樣,內(nèi)部類的第一個(gè)好處就體現(xiàn)出來(lái)了??隱藏你不想讓別人知道的操作,也即封裝性。 

            同時(shí),我們也發(fā)現(xiàn)了在外部類作用范圍之外得到內(nèi)部類對(duì)象的第一個(gè)方法,那就是利用其外部類的方法創(chuàng)建并返回。上例中的cont()和dest()方法就是這么做的。那么還有沒(méi)有別的方法呢?當(dāng)然有,其語(yǔ)法格式如下:

          outerObject=new outerClass(Constructor Parameters);

          outerClass.innerClass innerObject=outerObject.new InnerClass(Constructor Parameters); 

            注意在創(chuàng)建非靜態(tài)內(nèi)部類對(duì)象時(shí),一定要先創(chuàng)建起相應(yīng)的外部類對(duì)象。至于原因,也就引出了我們下一個(gè)話題??非靜態(tài)內(nèi)部類對(duì)象有著指向其外部類對(duì)象的引用,對(duì)剛才的例子稍作修改:

          public class Goods {

           private valueRate=2;

           private class Content implements Contents {
            private int i = 11*valueRate;
            public int value() { 
             return i; 
            }
           }

           protected class GDestination implements Destination {
            private String label;
            private GDestination(String whereTo) {
             label = whereTo;
            }
            public String readLabel() { 
             return label; 
            }
           }

           public Destination dest(String s) {
            return new GDestination(s);
           }
           public Contents cont() {
            return new Content();
           }


            修改的部分用藍(lán)色顯示了。在這里我們給Goods類增加了一個(gè)private成員變量valueRate,意義是貨物的價(jià)值系數(shù),在內(nèi)部類Content的方法value()計(jì)算價(jià)值時(shí)把它乘上。我們發(fā)現(xiàn),value()可以訪問(wèn)valueRate,這也是內(nèi)部類的第二個(gè)好處??一個(gè)內(nèi)部類對(duì)象可以訪問(wèn)創(chuàng)建它的外部類對(duì)象的內(nèi)容,甚至包括私有變量!這是一個(gè)非常有用的特性,為我們?cè)谠O(shè)計(jì)時(shí)提供了更多的思路和捷徑。要想實(shí)現(xiàn)這個(gè)功能,內(nèi)部類對(duì)象就必須有指向外部類對(duì)象的引用。Java編譯器在創(chuàng)建內(nèi)部類對(duì)象時(shí),隱式的把其外部類對(duì)象的引用也傳了進(jìn)去并一直保存著。這樣就使得內(nèi)部類對(duì)象始終可以訪問(wèn)其外部類對(duì)象,同時(shí)這也是為什么在外部類作用范圍之外向要?jiǎng)?chuàng)建內(nèi)部類對(duì)象必須先創(chuàng)建其外部類對(duì)象的原因。

            有人會(huì)問(wèn),如果內(nèi)部類里的一個(gè)成員變量與外部類的一個(gè)成員變量同名,也即外部類的同名成員變量被屏蔽了,怎么辦?沒(méi)事,Java里用如下格式表達(dá)外部類的引用:

          outerClass.this  

            有了它,我們就不怕這種屏蔽的情況了。

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

            和普通的類一樣,內(nèi)部類也可以有靜態(tài)的。不過(guò)和非靜態(tài)內(nèi)部類相比,區(qū)別就在于靜態(tài)內(nèi)部類沒(méi)有了指向外部的引用。這實(shí)際上和C++中的嵌套類很相像了,Java內(nèi)部類與C++嵌套類最大的不同就在于是否有指向外部的引用這一點(diǎn)上,當(dāng)然從設(shè)計(jì)的角度以及以它一些細(xì)節(jié)來(lái)講還有區(qū)別。

            除此之外,在任何非靜態(tài)內(nèi)部類中,都不能有靜態(tài)數(shù)據(jù),靜態(tài)方法或者又一個(gè)靜態(tài)內(nèi)部類(內(nèi)部類的嵌套可以不止一層)。不過(guò)靜態(tài)內(nèi)部類中卻可以擁有這一切。這也算是兩者的第二個(gè)區(qū)別吧。

            局部?jī)?nèi)部類 

            是的,Java內(nèi)部類也可以是局部的,它可以定義在一個(gè)方法甚至一個(gè)代碼塊之內(nèi)。

          public class Goods1 {
           public Destination dest(String s) {
            class GDestination implements Destination {
             private String label;
             private GDestination(String whereTo) {
              label = whereTo;
             }
             public String readLabel() { return label; }
            }
            return new GDestination(s);
           }

           public static void main(String[] args) {
            Goods1 g= new Goods1();
            Destination d = g.dest("Beijing");
           }


            上面就是這樣一個(gè)例子。在方法dest中我們定義了一個(gè)內(nèi)部類,最后由這個(gè)方法返回這個(gè)內(nèi)部類的對(duì)象。如果我們?cè)谟靡粋€(gè)內(nèi)部類的時(shí)候僅需要?jiǎng)?chuàng)建它的一個(gè)對(duì)象并創(chuàng)給外部,就可以這樣做。當(dāng)然,定義在方法中的內(nèi)部類可以使設(shè)計(jì)多樣化,用途絕不僅僅在這一點(diǎn)。

            下面有一個(gè)更怪的例子:

          public class Goods2{
           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("slip");
             String s = ts.getSlip();
            } 
           }

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

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


            你不能在if之外創(chuàng)建這個(gè)內(nèi)部類的對(duì)象,因?yàn)檫@已經(jīng)超出了它的作用域。不過(guò)在編譯的時(shí)候,內(nèi)部類TrackingSlip和其他類一樣同時(shí)被編譯,只不過(guò)它由它自己的作用域,超出了這個(gè)范圍就無(wú)效,除此之外它和其他內(nèi)部類并沒(méi)有區(qū)別。

            匿名內(nèi)部類 

            java的匿名內(nèi)部類的語(yǔ)法規(guī)則看上去有些古怪,不過(guò)如同匿名數(shù)組一樣,當(dāng)你只需要?jiǎng)?chuàng)建一個(gè)類的對(duì)象而且用不上它的名字時(shí),使用內(nèi)部類可以使代碼看上去簡(jiǎn)潔清楚。它的語(yǔ)法規(guī)則是這樣的:

          new interfacename(){......}; 或 new superclassname(){......};  

            下面接著前面繼續(xù)舉例子:

          public class Goods3 {
           public Contents cont(){
            return new Contents(){
             private int i = 11;
             public int value() { 
              return i; 
             }
            };
           }
          }  

            這里方法cont()使用匿名內(nèi)部類直接返回了一個(gè)實(shí)現(xiàn)了接口Contents的類的對(duì)象,看上去的確十分簡(jiǎn)潔。

            在java的事件處理的匿名適配器中,匿名內(nèi)部類被大量的使用。例如在想關(guān)閉窗口時(shí)加上這樣一句代碼:

          frame.addWindowListener(new WindowAdapter(){
           public void windowClosing(WindowEvent e){
            System.exit(0); 
           }
          }); 

            有一點(diǎn)需要注意的是,匿名內(nèi)部類由于沒(méi)有名字,所以它沒(méi)有構(gòu)造函數(shù)(但是如果這個(gè)匿名內(nèi)部類繼承了一個(gè)只含有帶參數(shù)構(gòu)造函數(shù)的父類,創(chuàng)建它的時(shí)候必須帶上這些參數(shù),并在實(shí)現(xiàn)的過(guò)程中使用super關(guān)鍵字調(diào)用相應(yīng)的內(nèi)容)。如果你想要初始化它的成員變量,有下面幾種方法:

            如果是在一個(gè)方法的匿名內(nèi)部類,可以利用這個(gè)方法傳進(jìn)你想要的參數(shù),不過(guò)記住,這些參數(shù)必須被聲明為final。 

            將匿名內(nèi)部類改造成有名字的局部?jī)?nèi)部類,這樣它就可以擁有構(gòu)造函數(shù)了。 

            在這個(gè)匿名內(nèi)部類中使用初始化代碼塊。 

            為什么需要內(nèi)部類? 

            java內(nèi)部類有什么好處?為什么需要內(nèi)部類?

            首先舉一個(gè)簡(jiǎn)單的例子,如果你想實(shí)現(xiàn)一個(gè)接口,但是這個(gè)接口中的一個(gè)方法和你構(gòu)想的這個(gè)類中的一個(gè)方法的名稱,參數(shù)相同,你應(yīng)該怎么辦?這時(shí)候,你可以建一個(gè)內(nèi)部類實(shí)現(xiàn)這個(gè)接口。由于內(nèi)部類對(duì)外部類的所有內(nèi)容都是可訪問(wèn)的,所以這樣做可以完成所有你直接實(shí)現(xiàn)這個(gè)接口的功能。

            不過(guò)你可能要質(zhì)疑,更改一下方法的不就行了嗎?

            的確,以此作為設(shè)計(jì)內(nèi)部類的理由,實(shí)在沒(méi)有說(shuō)服力。

            真正的原因是這樣的,java中的內(nèi)部類和接口加在一起,可以的解決常被C++程序員抱怨java中存在的一個(gè)問(wèn)題??沒(méi)有多繼承。實(shí)際上,C++的多繼承設(shè)計(jì)起來(lái)很復(fù)雜,而java通過(guò)內(nèi)部類加上接口,可以很好的實(shí)現(xiàn)多繼承的效果。

          posted on 2008-09-07 11:41 大石頭 閱讀(191) 評(píng)論(0)  編輯  收藏 所屬分類: 基礎(chǔ)

          主站蜘蛛池模板: 融水| 临颍县| 奇台县| 颍上县| 金湖县| 岫岩| 微山县| 五指山市| 孙吴县| 徐闻县| 班玛县| 原平市| 山东省| 平邑县| 云霄县| 峡江县| 丰顺县| 改则县| 冷水江市| 南澳县| 望谟县| 邓州市| 二连浩特市| 安仁县| 宾阳县| 建平县| 噶尔县| 崇义县| 同心县| 东至县| 灵台县| 彭阳县| 襄樊市| 靖远县| 常州市| 邵阳市| 瓮安县| 蒲城县| 和田县| 深水埗区| 霍邱县|