qileilove

          blog已經(jīng)轉(zhuǎn)移至github,大家請訪問 http://qaseven.github.io/

          Java 中的內(nèi)部類和匿名類

           Java 內(nèi)部類有什么好處?為什么需要內(nèi)部類?
            首先舉一個簡單的例子,如果你想實(shí)現(xiàn)一個接口,但是這個接口中的一個方法和你構(gòu)想的這個類中的一個方法的名稱,參數(shù)相同,你應(yīng)該怎么辦?這時候,你可以建一個內(nèi)部類實(shí)現(xiàn)這個接口。由于內(nèi)部類對外部類的所有內(nèi)容都是可訪問的,所以這樣做可以完成所有你直接實(shí)現(xiàn)這個接口的功能。
            不過你可能要質(zhì)疑,更改一下方法的不就行了嗎?
            的確,以此作為設(shè)計內(nèi)部類的理由,實(shí)在沒有說服力。
            真正的原因是這樣的,java 中的內(nèi)部類和接口加在一起,可以的解決常被 C++ 程序員抱怨 java 中存在的一個問題??沒有多繼承。實(shí)際上,C++ 的多繼承設(shè)計起來很復(fù)雜,而 java 通過內(nèi)部類加上接口,可以很好的實(shí)現(xiàn)多繼承的效果。
            內(nèi)部類:一個內(nèi)部類的定義是定義在另一個內(nèi)部的類。
            原因是:
            1.一個內(nèi)部類的對象能夠訪問創(chuàng)建它的對象的實(shí)現(xiàn),包括私有數(shù)據(jù)。
            2.對于同一個包中的其他類來說,內(nèi)部類能夠隱藏起來。
            3.匿名內(nèi)部類可以很方便的定義回調(diào)。
            4.使用內(nèi)部類可以非常方便的編寫事件驅(qū)動程序。
            1.內(nèi)部類
            提起 Java 內(nèi)部類(Inner Class)可能很多人不太熟悉,實(shí)際上類似的概念在 C++ 里也有,那就是嵌套類(Nested Class),關(guān)于這兩者的區(qū)別與聯(lián)系,在下文中會有對比。內(nèi)部類從表面上看,就是在類中又定義了一個類(下文會看到,內(nèi)部類可以在很多地方定義),而實(shí)際上并沒有那么簡單,乍看上去內(nèi)部類似乎有些多余,它的用處對于初學(xué)者來說可能并不是那么顯著,但是隨著對它的深入了解,你會發(fā)現(xiàn)Java的設(shè)計者在內(nèi)部類身上的確是用心良苦。學(xué)會使用內(nèi)部類,是掌握J(rèn)ava高級編程的一部分,它可以讓你更優(yōu)雅地設(shè)計你的程序結(jié)構(gòu)。下面從以下幾個方面來介紹:
            * 第一次見面
          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");
          }
          }



            在這個例子里類 Content 和 GDestination 被定義在了類 Goods 內(nèi)部,并且分別有著 protected 和 private 修飾符來控制訪問級別。Content 代表著 Goods 的內(nèi)容,而 GDestination 代表著 Goods 的目的地。它們分別實(shí)現(xiàn)了兩個接口 Content 和 Destination。在后面的 main 方法里,直接用 Contents c 和 Destination d 進(jìn)行操作,你甚至連這兩個內(nèi)部類的名字都沒有看見!這樣,內(nèi)部類的第一個好處就體現(xiàn)出來了??隱藏你不想讓別人知道的操作,也即封裝性。
            同時,我們也發(fā)現(xiàn)了在外部類作用范圍之外得到內(nèi)部類對象的第一個方法,那就是利用其外部類的方法創(chuàng)建并返回。上例中的 cont() 和 dest() 方法就是這么做的。那么還有沒有別的方法呢?當(dāng)然有,其語法格式如下:
            outerObject=new outerClass(Constructor Parameters);
            outerClass.innerClass innerObject=outerObject.new InnerClass(Constructor Parameters);
            注意在創(chuàng)建非靜態(tài)內(nèi)部類對象時,一定要先創(chuàng)建起相應(yīng)的外部類對象。至于原因,也就引出了我們下一個話題??
            * 非靜態(tài)內(nèi)部類對象有著指向其外部類對象的引用
            對剛才的例子稍作修改:
          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 類增加了一個 private 成員變量 valueRate,意義是貨物的價值系數(shù),在內(nèi)部類 Content 的方法 value() 計算價值時把它乘上。我們發(fā)現(xiàn),value() 可以訪問 valueRate,這也是內(nèi)部類的第二個好處??一個內(nèi)部類對象可以訪問創(chuàng)建它的外部類對象的內(nèi)容,甚至包括私有變量!這是一個非常有用的特性,為我們在設(shè)計時提供了更多的思路和捷徑。要想實(shí)現(xiàn)這個功能,內(nèi)部類對象就必須有指向外部類對象的引用。Java 編譯器在創(chuàng)建內(nèi)部類對象時,隱式的把其外部類對象的引用也傳了進(jìn)去并一直保存著。這樣就使得內(nèi)部類對象始終可以訪問其外部類對象,同時這也是為什么在外部類作用范圍之外向要創(chuàng)建內(nèi)部類對象必須先創(chuàng)建其外部類對象的原因。
            有人會問,如果內(nèi)部類里的一個成員變量與外部類的一個成員變量同名,也即外部類的同名成員變量被屏蔽了,怎么辦?沒事,Java里用如下格式表達(dá)外部類的引用:
            outerClass.this
            有了它,我們就不怕這種屏蔽的情況了。
            * 靜態(tài)內(nèi)部類
            和普通的類一樣,內(nèi)部類也可以有靜態(tài)的。不過和非靜態(tài)內(nèi)部類相比,區(qū)別就在于靜態(tài)內(nèi)部類沒有了指向外部的引用。這實(shí)際上和 C++ 中的嵌套類很相像了,Java 內(nèi)部類與 C++ 嵌套類最大的不同就在于是否有指向外部的引用這一點(diǎn)上,當(dāng)然從設(shè)計的角度以及以它一些細(xì)節(jié)來講還有區(qū)別。
            除此之外,在任何非靜態(tài)內(nèi)部類中,都不能有靜態(tài)數(shù)據(jù),靜態(tài)方法或者又一個靜態(tài)內(nèi)部類(內(nèi)部類的嵌套可以不止一層)。不過靜態(tài)內(nèi)部類中卻可以擁有這一切。這也算是兩者的第二個區(qū)別吧。
            * 局部內(nèi)部類
            是的,Java 內(nèi)部類也可以是局部的,它可以定義在一個方法甚至一個代碼塊之內(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");
          }
          }
            上面就是這樣一個例子。在方法dest中我們定義了一個內(nèi)部類,最后由這個方法返回這個內(nèi)部類的對象。如果我們在用一個內(nèi)部類的時候僅需要創(chuàng)建它的一個對象并創(chuàng)給外部,就可以這樣做。當(dāng)然,定義在方法中的內(nèi)部類可以使設(shè)計多樣化,用途絕不僅僅在這一點(diǎn)。


            下面有一個更怪的例子:
          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)建這個內(nèi)部類的對象,因?yàn)檫@已經(jīng)超出了它的作用域。不過在編譯的時候,內(nèi)部類 TrackingSlip 和其他類一樣同時被編譯,只不過它由它自己的作用域,超出了這個范圍就無效,除此之外它和其他內(nèi)部類并沒有區(qū)別。
            2.匿名類
            匿名類是不能有名稱的類,所以沒辦法引用他們。必須在創(chuàng)建時,作為new語句的一部分來聲明他們。
            這就要采用另一種形式的new語句,如下所示:
            new <類或接口> <類的主體>
            這種形式的new語句聲明一個新的匿名類,他對一個給定的類進(jìn)行擴(kuò)展,或?qū)崿F(xiàn)一個給定的接口。他還創(chuàng)建那個類的一個新實(shí)例,并把他作為語句的結(jié)果而返回。要擴(kuò)展的類和要實(shí)現(xiàn)的接口是new語句的操作數(shù),后跟匿名類的主體。
            假如匿名類對另一個類進(jìn)行擴(kuò)展,他的主體能夠訪問類的成員、覆蓋他的方法等等,這和其他任何標(biāo)準(zhǔn)的類都是相同的。假如匿名類實(shí)現(xiàn)了一個接口,他的主體必須實(shí)現(xiàn)接口的方法。
            注意匿名類的聲明是在編譯時進(jìn)行的,實(shí)例化在運(yùn)行時進(jìn)行。這意味著for循環(huán)中的一個new語句會創(chuàng)建相同匿名類的幾個實(shí)例,而不是創(chuàng)建幾個不同匿名類的一個實(shí)例。
            從技術(shù)上說,匿名類可被視為非靜態(tài)的內(nèi)部類,所以他們具備和方法內(nèi)部聲明的非靜態(tài)內(nèi)部類相同的權(quán)限和限制。
            假如要執(zhí)行的任務(wù)需要一個對象,但卻不值得創(chuàng)建全新的對象(原因可能是所需的類過于簡單,或是由于他只在一個方法內(nèi)部使用),匿名類就顯得很有用。匿名類尤其適合在Swing應(yīng)用程式中快速創(chuàng)建事件處理程式。
          interface pr {
          void print1();
          }
          public class noNameClass {
          public pr dest() {
          return new pr() {
          public void print1() {
          System.out.println("Hello world!!");
          }
          };
          }
          }
          public static void main(String args[]) {
          noNameClass c = new noNameClass();
          pr hw = c.dest();
          hw.print1();
          }
            pr 也可以是一個類,但是你外部調(diào)用的方法必須在你的這個類或接口中聲明,外部不能調(diào)用匿名類內(nèi)部的方法。
           Java 中內(nèi)部匿名類用的最多的地方也許就是在 Frame 中加入 Listner 了吧。
          import java.awt.*;
          import java.awt.event.*;
          public class QFrame extends Frame {
          public QFrame() {
          this.setTitle(\"my application\");
          addWindowListener(new WindowAdapter() {
          public void windowClosing(WindowEvent e) {
          dispose();
          System.exit(0);
          }
          });
          this.setBounds(10,10,200,200);
          }
          }
            內(nèi)部匿名類,就是建立一個內(nèi)部的類,但沒有給你命名,也就是沒有引用實(shí)例的變量。
          new WindowAdapter() {
          public void windowClosing(WindowEvent e) {
          dispose();
          System.exit(0);
          }
          }
            new 是建立一個 WindowAdapter 對象,后面一個 {} 表示這個括號中的操作作用于這個默認(rèn)的對名象,而上面的 Java 程序中后面是一個函數(shù)體。
            這個用法的作用是:創(chuàng)建一個對象的實(shí)例,并且 override 它的一個函數(shù)。
            打開 WindowAdapter 的代碼可以發(fā)現(xiàn)。它是一個抽象類。它是對 WindowListener 接口的一個實(shí)現(xiàn)。
            Frame.addWindowListner(); 的參數(shù)是一個 WindowListner ,而實(shí)現(xiàn)上是傳一個從WindowAdapter 派生出的一個匿名類。
            有一點(diǎn)需要注意的是,匿名內(nèi)部類由于沒有名字,所以它沒有構(gòu)造函數(shù)(但是如果這個匿名內(nèi)部類繼承了一個只含有帶參數(shù)構(gòu)造函數(shù)的父類,創(chuàng)建它的時候必須帶上這些參數(shù),并在實(shí)現(xiàn)的過程中使用 super 關(guān)鍵字調(diào)用相應(yīng)的內(nèi)容)。如果你想要初始化它的成員變量,有下面幾種方法:
            1. 如果是在一個方法的匿名內(nèi)部類,可以利用這個方法傳進(jìn)你想要的參數(shù),不過記住,這些參數(shù)必須被聲明為 final 。
            2. 將匿名內(nèi)部類改造成有名字的局部內(nèi)部類,這樣它就可以擁有構(gòu)造函數(shù)了。
            3. 在這個匿名內(nèi)部類中使用初始化代碼塊。

          posted on 2013-12-17 09:14 順其自然EVO 閱讀(267) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          <2013年12月>
          24252627282930
          1234567
          891011121314
          15161718192021
          22232425262728
          2930311234

          導(dǎo)航

          統(tǒng)計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 象州县| 舟山市| 菏泽市| 金川县| 九台市| 日喀则市| 仪征市| 澜沧| 龙游县| 盐源县| 澳门| 伊宁县| 长宁县| 台江县| 景东| 仁布县| 禹城市| 利川市| 淮南市| 库尔勒市| 格尔木市| 洞口县| 昔阳县| 黔江区| 兴国县| 石泉县| 朝阳县| 思茅市| 天津市| 梧州市| 聂荣县| 抚松县| 安西县| 屏东县| 峡江县| 长乐市| 天气| 静安区| 鹤山市| 汕头市| 绥江县|