<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Decode360's Blog

    業(yè)精于勤而荒于嬉 QQ:150355677 MSN:decode360@hotmail.com

      BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 ::  :: 管理 ::
      302 隨筆 :: 26 文章 :: 82 評(píng)論 :: 0 Trackbacks
    ?
    類與繼承
    ?
    ?
    ?
    ??? 類具有繼承性。子類對(duì)父類的繼承關(guān)系體現(xiàn)了現(xiàn)實(shí)世界中特殊和一般的關(guān)系。類的繼承性大大簡(jiǎn)化了程序設(shè)計(jì)的復(fù)雜性。和類的繼承性相聯(lián)系的對(duì)象的動(dòng)態(tài)綁定使對(duì)象的方法具有多態(tài)性。抽象類和最終類是兩種特殊的類。接口和抽象類非常類似,Java語(yǔ)言只支持單繼承,但接口使Java語(yǔ)言實(shí)際上實(shí)現(xiàn)了多繼承。
    ?
    ?
    1、面向?qū)ο蟮幕靖拍睿豪^承
    ?
    ??? 繼承是面向?qū)ο蟪绦蛟O(shè)計(jì)的又一個(gè)重要特性。繼承體現(xiàn)了類與類之間的一種特殊關(guān)系,即一般與特殊的關(guān)系。繼承就是一個(gè)新的類擁有全部被繼承類的成員變量和方法。繼承機(jī)制使得新類不僅有自己特有的成員變量和方法,而且有被繼承類的全部成員變量和方法。通過(guò)繼承,可以從已有類模塊產(chǎn)生新的類模塊,從而使兩個(gè)類模塊之間發(fā)生聯(lián)系。通過(guò)繼承產(chǎn)生的新的類模塊不僅重用了被繼承類的模塊資源,而且使兩個(gè)類模塊之間的聯(lián)系方式和人類認(rèn)識(shí)客觀事物的方式一致。
    ??? 面向?qū)ο蟪绦蛟O(shè)計(jì)的繼承特性使得大型應(yīng)用程序的維護(hù)和設(shè)計(jì)變得更加簡(jiǎn)單。一方面,大型應(yīng)用程序設(shè)計(jì)完成并交付使用后,經(jīng)常面臨用戶的需求發(fā)生變化,程序功能需要擴(kuò)充等問(wèn)題。這時(shí)程序的修改需要非常謹(jǐn)慎,因?yàn)槟硞€(gè)局部的修改可能會(huì)影響其他部分,而一個(gè)正在使用中的系統(tǒng)要進(jìn)行全面的測(cè)試,則既費(fèi)時(shí)間又有很多實(shí)際的困難。另一方面,一個(gè)新的應(yīng)用系統(tǒng)程序設(shè)計(jì)問(wèn)題,在許多方面會(huì)和以前設(shè)計(jì)過(guò)的某個(gè)或某些系統(tǒng)的模塊非常類似,怎樣加快大型應(yīng)用程序的開發(fā)速度,重用這些已經(jīng)開發(fā)成功的程序模塊,一直是軟件設(shè)計(jì)中非常希望有效解決的問(wèn)題。
    ??? 傳統(tǒng)的軟件設(shè)計(jì)解決上述兩類問(wèn)題的方法主要有兩種:
    ??? 對(duì)于程序功能擴(kuò)充問(wèn)題,通常是直接對(duì)原代碼進(jìn)行改動(dòng)。這種方法雖然可行,但有可能對(duì)正在使用的其他模塊產(chǎn)生影響,通常靠測(cè)試的方法消除這種影響。但是,要對(duì)一個(gè)正在使用的系統(tǒng)進(jìn)行全面測(cè)試,既非常困難,代價(jià)又很大。
    ??? 對(duì)于模塊重用問(wèn)題,通常是對(duì)原模塊進(jìn)行復(fù)制。對(duì)復(fù)制的模塊再根據(jù)需要進(jìn)行改動(dòng),以支持新的功能。這種方法雖然可行,但仍然需要設(shè)計(jì)人員做很多工作,而且需要重新測(cè)試。
    ??? 面向?qū)ο蟪绦蛟O(shè)計(jì)的繼承機(jī)制可以很好地解決上述兩類問(wèn)題。面向?qū)ο蟪绦蛟O(shè)計(jì)的繼承機(jī)制提供了一種重復(fù)利用原有程序模塊資源的途徑。通過(guò)新類對(duì)原有類的繼承,既可以擴(kuò)充舊的程序模塊功能以適應(yīng)新的用戶需求,也可以滿足新的應(yīng)用系統(tǒng)的功能要求。從而既可以大大方便原有系統(tǒng)的功能擴(kuò)充,也可以大大加快新系統(tǒng)的開發(fā)速度。另外,用這種軟件設(shè)計(jì)方法設(shè)計(jì)的新系統(tǒng)較用傳統(tǒng)的軟件方法設(shè)計(jì)的新系統(tǒng)需要進(jìn)行的測(cè)試工作少很多。
    ?
    ?
    2、繼承

    1、子類和父類
    ?
    ??? 利用面向?qū)ο蟪绦蛟O(shè)計(jì)的繼承機(jī)制,我們可以首先創(chuàng)建一個(gè)包括其他許多類共有的成員變量和方法的一般類,然后再通過(guò)繼承創(chuàng)建一個(gè)新類。由于繼承,這些新類已經(jīng)具有了一般類的成員變量和方法,此時(shí)只需再設(shè)計(jì)各個(gè)不同類特有的成員變量和方法。由繼承而得到的新類稱為子類,被繼承的類稱為父類或超類。子類直接的上層父類稱作直接父類。Java不支持多繼承,即一個(gè)子類只能有一個(gè)直接父類。
    ??? 例如,設(shè)父類super已經(jīng)定義,當(dāng)類sub1繼承類super時(shí),就表明類sub1是類super的子類,或者說(shuō)類super是類sub1的父類。子類sub1由兩部分組成:繼承部分和增加部分。繼承部分是從父類super繼承過(guò)來(lái)的,增加部分是子類sub1新增加的。這樣,子類繼承了父類的成員變量和方法,從而可以共享已設(shè)計(jì)完成的軟件模塊。不僅如此,父類super還可以作為多個(gè)子類的父類,如子類sub2也是父類super的子類。由于子類sub1和子類sub2有相同的父類,所以他們既有許多相同的性能,也有一些不同的功能。父類和子類之間的繼承關(guān)系如下圖所示:
    ?
    ??? java3-1.gif
    ??? 從上圖可知,具有繼承關(guān)系的若干個(gè)類組成一棵類樹。由于Java中所有的類都是從Object類繼承(或稱派生)來(lái)的,所以,Java中所有的類構(gòu)成一棵類樹。
    ??? 注意:在圖所示的有三層繼承關(guān)系的類中,最下層的Sub11和Sub12子子類,不僅繼承了直接父類Sub1的成員變量和方法,而且繼承了間接父類Super的成員變量和方法。如果沒有繼承機(jī)制,則一個(gè)軟件系統(tǒng)中的各個(gè)類是各自封閉的、相互無(wú)關(guān)的。當(dāng)多個(gè)類需要實(shí)現(xiàn)相似的功能時(shí),勢(shì)必會(huì)造成成員變量和方法的大量重復(fù)。而有了繼承機(jī)制,多個(gè)類就可相互關(guān)聯(lián),新類就可以從已有的類中通過(guò)繼承產(chǎn)生。
    ??? 繼承有兩種基本形式:多繼承單繼承。多繼承是指一個(gè)子類可以繼承自多個(gè)直接父類。單繼承是指一個(gè)子類只可以繼承自一個(gè)直接父類。Java語(yǔ)言只允許單繼承,不允許多繼承。
    ?
    2、創(chuàng)建子類
    ?
    ??? Java中的類都是Object類的子類(當(dāng)然,很多類是Object類的間接子類)。Object類定義了所有對(duì)象都必須具有的基本成員變量和方法。Java中的每個(gè)類都從Object類繼承了成員變量和方法,因而Java中的所有對(duì)象都具有Object類的成員變量和方法。
    ??? 由于Java中的所有類都是Object的直接子類或間接子類,所以Java中的所有類構(gòu)成一棵類的層次樹結(jié)構(gòu)。
    ??? 定義類有兩種基本方法:不指明父類和顯式地指明父類。Java語(yǔ)言規(guī)定,若定義類時(shí)不指明父類,則其父類是Object類。本節(jié)介紹顯式的指明父類的類定義方法。
    ??? 顯式的指明一個(gè)類的父類的方法是,在類定義時(shí)使用關(guān)鍵字extends,并隨后給出父類名。類定義語(yǔ)句格式為:
    ??? [<修飾符>]?class?<子類名> extends?<父類名>
    ??? 例如: class? Sub1? extends? Super
    ??? 就定義類Sub1繼承自類Super。此時(shí)我們說(shuō)類Sub1是類Super的子類,或者說(shuō)類Super是類Sub1的直接父類,直接父類通常簡(jiǎn)稱為父類。
    ?
    1.子類繼承父類的成員變量
    ??? 子類繼承了父類中的成員變量。具體的繼承原則是:
    ??? (1)能夠繼承父類中那些聲明為public和protected的成員變量。
    ??? (2)不能繼承父類中那些聲明為private和默認(rèn)的成員變量。
    ??? (3)如果子類聲明一個(gè)與父類成員變量同名的成員變量,則不能繼承父類的同名成員變量。此時(shí)稱子類的成員變量隱藏了父類中的同名成員變量。
    ??? 因此,如果父類中存在不允許其子類訪問(wèn)的成員變量,那么這些成員變量必須以private修飾符聲明該成員變量;如果父類中存在只允許其子類訪問(wèn)、不允許其他類訪問(wèn)的成員變量,那么這些成員變量必須以protected修飾符聲明該成員變量
    ?
    2.子類繼承父類的方法
    ??? 子類繼承父類方法的規(guī)則類似于子類繼承父類成員變量的規(guī)則。具體的繼承原規(guī)是:
    ??? (1)能夠繼承父類中那些聲明為public和protected的方法。
    ??? (2)不能繼承父類中那些聲明為private和默認(rèn)的方法。
    ??? (3)如果子類方法與父類方法同名,則不能繼承。此時(shí)稱子類方法重寫了父類中的同名方法。
    ??? (4)不能繼承父類的構(gòu)造方法。
    ??? 注意:和子類繼承父類成員變量的繼承原則不同的是,子類不能繼承父類的構(gòu)造方法。
    ?
    3.this引用和super引用

    ???? (1)this引用。
    ??? Java中,每個(gè)對(duì)象都具有對(duì)其自身引用的訪問(wèn)權(quán),這稱為this引用。訪問(wèn)本類的成員變量和方法的語(yǔ)句格式為:
    ??? this.<成員變量名>
    ??? this.<方法名>

    ??? 例如:下面定義的類X中有成員變量k,而在方法D中也用k作參數(shù),這樣兩個(gè)不同含義的變量k就有可能產(chǎn)生混淆,此時(shí)必須用this.k指代對(duì)象的成員變量k。
    class X
    {
    ??? int k;
    ??? void D(int k)
    ??? {
    ??????? this.k = 2*k;??? //this.k指成員變量k,k指參數(shù)k
    ??? }
    }

    ???? (2)super引用。
    ??? 使用關(guān)鍵字super可以引用被子類隱藏的父類的成員變量或方法,這稱為super引用。super引用的語(yǔ)句格式為:
    ??? super.<成員變量名>
    ??? super.<方法名>

    ??? super引用經(jīng)常用在子類的構(gòu)造方法中。前面說(shuō)過(guò),子類不能繼承父類的構(gòu)造方法,但有時(shí)子類的構(gòu)造方法和父類的構(gòu)造方法相同時(shí),或子類的構(gòu)造方法只需在父類構(gòu)造方法的基礎(chǔ)上做某些補(bǔ)充時(shí),子類構(gòu)造方法中需要調(diào)用父類的構(gòu)造方法時(shí),此時(shí)的語(yǔ)句格式為:
    ??? super(<參數(shù)列表>)
    ??? 其中,<參數(shù)列表>是調(diào)用父類構(gòu)造方法所需的參數(shù)。
    ?
    4.成員變量和方法的隱藏與覆蓋
    ??? 子類除了可以繼承父類中的成員變量和方法外,還可以增加自已特有的成員變量和方法。當(dāng)父類的某個(gè)成員變量不適合子類時(shí),子類可以重新定義該成員變量。前面說(shuō)過(guò),此種情況下,子類隱藏了父類的成員變量(程序設(shè)計(jì)中這種情況很少,一般也不提倡);當(dāng)父類的某個(gè)方法不適合子類時(shí),子類可以重新定義它,這稱為子類對(duì)父類方法的覆蓋(overriding)。
    ??? 子類對(duì)父類方法的覆蓋是面向?qū)ο蟪绦蛟O(shè)計(jì)中經(jīng)常使用的設(shè)計(jì)方法。在軟件功能擴(kuò)充和軟件重用中,可以通過(guò)設(shè)計(jì)新的子類,以及通過(guò)子類方法對(duì)父類方法的覆蓋,可以方便和快速地實(shí)現(xiàn)軟件功能的擴(kuò)充和軟件的重用。
    ??? 注意:方法的重寫(overloading)和方法的覆蓋(overriding)是兩個(gè)不同的概念,在軟件設(shè)計(jì)中實(shí)現(xiàn)的功能也不同。
    ?
    5.舉例

    【例4.1】繼承舉例。
    要求;設(shè)計(jì)一個(gè)Shape(形狀)類,再設(shè)計(jì)Shape類的兩個(gè)子類,一個(gè)是Ellipse(橢圓)類,另一個(gè)是Rectangle(矩形)類。每個(gè)類都包括若干成員變量和方法,但每個(gè)類都有一個(gè)draw()方法(畫圖方法),draw()方法中用輸出字符串表示畫圖。
    程序設(shè)計(jì)如下:
    class Shape??????????? //定義父類Shape
    {
    ??protected int lineSize;???????? //線寬
    ?
    ??public Shape()??????//構(gòu)造方法1
    ??{
    ??? lineSize = 1;
    ??}
    ?
    ? public Shape(int ls)?//構(gòu)造方法2
    ??{
    ??? lineSize = ls;
    ??}
    ?
    ? public void setLineSize(int ls)?//設(shè)置線寬
    ??{
    ??? lineSize = ls;
    ??}
    ?
    ? public int getLineSize()??????? //獲得線寬
    ??{
    ??? return lineSize;
    ??}
    ?
    ? public void draw()????????????? //畫圖
    ??{
    ??? System.out.println("Draw a Shape");
    ??}
    }
    ?
    class Ellipse extends Shape????//定義子類Ellipse
    {
    ? private int centerX;????//圓心X坐標(biāo)
    ?private int centerY;????//圓心Y坐標(biāo)
    ?private int width;?????//橢圓寬度
    ?private int height;?????//橢圓高度
    ?
    ?public Ellipse(int x, int y, int w, int h) ?//構(gòu)造方法
    ?{
    ??? super();??????//調(diào)用父類的構(gòu)造方法1
    ??? centerX = x;
    ??? centerY = y;
    ??? width = w;
    ??? height = h;
    ?}
    ?
    ? public void draw()?????//覆蓋父類的draw()方法
    ?{
    ??? System.out.println("draw a Ellipse");
    ?}
    }
    ?
    class Rectangle extends Shape???//定義子類Rectangle
    {
    ?private int left;?????//矩形左上角X坐標(biāo)
    ?private int top;?????//矩形左上角Y坐標(biāo)
    ?private int width;????//矩形長(zhǎng)度
    ?private int height;???//矩形寬度
    ?
    ?public Rectangle(int l, int t, int w, int h)?//構(gòu)造方法
    ?{
    ??? super(2);?????????? //調(diào)用父類的構(gòu)造方法2
    ??? left = l;
    ??? top = t;
    ??? width = w;
    ??? height = h;
    ?}
    ?
    ?public void draw()????//覆蓋父類的draw()方法
    ?{
    ??? System.out.println("draw a Rectangle");
    ?}
    }
    ?
    public class Inherit?????//定義類Inherit
    {
    ?public static void main(String args[])
    ??{
    ??? Ellipse ellipse = new Ellipse(30, 30, 50,60);?
    ???????????????????????? //創(chuàng)建子類Ellipse的對(duì)象
    ??? ellipse.setLineSize(2);?
    ???????????????????????? //調(diào)用父類方法重新設(shè)置lineSize 值為2
    ??? System.out.println("LineSize of ellipse : " +?ellipse.getLineSize());
    ?
    ??? Rectangle rectangle = new Rectangle(0, 0, 20, 30);?
    ???????????????????????? //創(chuàng)建子類rectangle對(duì)象
    ??? rectangle.setLineSize(3);?
    ???????????????????????? //調(diào)用父類方法重新設(shè)置lineSize屬性為3
    ??? System.out.println("LineSize of rectangle : " +?rectangle.getLineSize());
    ??? ellipse.draw();?????//訪問(wèn)子類方法
    ??? rectangle.draw();????//訪問(wèn)子類方法
    ?? }
    }
    程序運(yùn)行結(jié)果如下:
    LineSize of ellipse : 2
    LineSize of rectangle : 3
    draw a Ellipse
    draw a Rectangle

    程序設(shè)計(jì)說(shuō)明:
    (1)類Shape中定義了所有子類共同的成員變量lineSize(線寬),橢圓類Ellipse和矩形類Rectangle在繼承父類成員變量的基礎(chǔ)上,又各自定義了自己的成員變量。
    (2)父類Shape中定義了畫圖方法draw(),子類Ellipse和子類Rectangle中由于各自形狀不同,畫圖方法draw()也不同,所以子類Ellipse和Rectangle中重新定義了各自的draw()方法(即覆蓋了父類的draw())。注意:子類覆蓋父類方法時(shí),參數(shù)個(gè)數(shù)和參數(shù)類型必須相同
    (3)當(dāng)一個(gè)文件中包含有多個(gè)類時(shí),源程序文件名應(yīng)該和定義為public類型的類名相同。
    ?
    3、方法的三種繼承形式
    ?
    ??? 上節(jié)討論了子類對(duì)父類方法繼承的一般形式,本節(jié)我們進(jìn)一步總結(jié)子類對(duì)父類方法繼承的三種不同形式,以及系統(tǒng)中子類對(duì)象訪問(wèn)方法的匹配原則和繼承在面向?qū)ο蟪绦蛟O(shè)計(jì)中的作用。
    ?
    1.方法的三種繼承形式
    ???
    子類對(duì)父類方法繼承可以有三種不同形式:完全繼承、完全覆蓋和修改繼承。
    ??? (1)完全繼承。
    ??? 完全繼承是指子類全部繼承父類的方法。
    ????如果父類中定義的方法完全適合于子類,則子類就不需要重新定義該方法。子類對(duì)父類的繼承允許子類對(duì)象直接訪問(wèn)父類的方法,這就是子類對(duì)父類方法的完全繼承。
    ??? 例如例4.1中,如果子類不重新定義draw()方法,則ellipse.draw()和rectangle.draw()訪問(wèn)的都是父類中定義的draw()方法。
    ??? (2)完全覆蓋。
    ??? 完全覆蓋是指子類重新定義父類方法的功能,從而子類中的同名方法完全覆蓋了父類中的方法。
    ??? 如例4.1中,子類重新定義了父類的draw()方法,因此子類對(duì)象ellipse和rectangle訪問(wèn)的就是子類中重新定義的方法draw(),即ellipse.draw()和rectangle.draw()訪問(wèn)的都是子類中定義的draw()方法。
    ??? (3)部分繼承
    ??? 部分繼承是指子類覆蓋父類的方法,但子類重新定義的方法中調(diào)用父類中的同名方法,并根據(jù)問(wèn)題要求做部分修改。
    ?
    【例4.2】修改繼承舉例。
    class Shape???????????? //定義父類Shape
    {
    ?public void draw()
    ?{
    ??? System.out.println("Draw a Shape");
    ?}
    }
    ?
    class Ellipse extends Shape???? //定義子類Ellipse
    {
    ?public void draw()????//覆蓋父類的draw()方法
    ?{
    ??? super.draw();
    ??? System.out.println("draw a Ellipse");
    ?}
    }
    ?
    class Rectangle extends Shape???//定義子類Rectangle
    {
    ?public void draw()????//覆蓋父類的draw()方法
    ?{
    ??? super.draw();?????? //調(diào)用父類的draw()方法
    ??? System.out.println("draw a Rectangle");?//修改部分
    ?}
    }
    ?
    public class CInherit???//定義類Inherit
    {
    ?public static void main(String args[])
    ??{
    ??? Ellipse ellipse = new Ellipse();????? //創(chuàng)建子類Ellipse的對(duì)象
    ??? Rectangle rectangle = new Rectangle();//創(chuàng)建子類rectangle對(duì)象
    ?
    ??? ellipse.draw();?????//訪問(wèn)子類方法
    ??? rectangle.draw();???//訪問(wèn)子類方法
    ?}
    }
    程序運(yùn)行結(jié)果如下:
    Draw a Shape
    draw a Ellipse
    Draw a Shape
    draw a Rectangle

    ?
    程序設(shè)計(jì)說(shuō)明:
    (1)子類的draw()覆蓋了父類的draw()方法,但子類的draw()方法首先調(diào)用了父類的draw()方法。子類draw()方法調(diào)用父類draw()方法的語(yǔ)句是:
    ??? super.draw();
    (2)由于子類方法在調(diào)用父類方法的基礎(chǔ)上,又增加了子類中需要補(bǔ)充修改的功能,所以子類對(duì)象ellipse和rectangle訪問(wèn)的draw()方法,完成的功能是在父類方法基礎(chǔ)上的補(bǔ)充或修改。
    ?
    2.系統(tǒng)中子類對(duì)象訪問(wèn)方法的匹配原則
    ???
    在Java語(yǔ)言(以及在所有的面向?qū)ο蟪绦蛟O(shè)計(jì)語(yǔ)言)中,對(duì)象訪問(wèn)方法的匹配原則是:從對(duì)象定義的類開始,逐層向上匹配尋找對(duì)象要訪問(wèn)的方法。
    ??? 在完全繼承方式中,由于子類中沒有定義draw()方法,所以系統(tǒng)自動(dòng)到它的直接父類Shape中去匹配draw()方法,系統(tǒng)在父類Shape中匹配上了draw()方法,所以子類對(duì)象ellipse和rectangle訪問(wèn)的是父類定義的draw()方法。在完全覆蓋方式中,由于子類中定義了draw()方法,所以子類對(duì)象ellipse和rectangle訪問(wèn)的是子類定義的draw()方法。在修改繼承方式中,由于子類中定義了draw()方法,所以子類對(duì)象ellipse和rectangle訪問(wèn)的是子類定義的draw()方法,由于子類定義的draw()方法首先調(diào)用了父類中定義的draw()方法,然后又增加了需要修改或補(bǔ)充的功能,所以子類對(duì)象ellipse和rectangle訪問(wèn)的draw()方法,既包含了父類draw()方法的功能,又包含了子類修改或補(bǔ)充的功能。
    ?
    3.繼承在面向?qū)ο蟪绦蛟O(shè)計(jì)中的作用
    ??? 繼承在面向?qū)ο蟪绦蛟O(shè)計(jì)中有兩方面的意義:
    ??? 一方面,繼承性可以大大簡(jiǎn)化程序設(shè)計(jì)的代碼。我們可以把若干個(gè)相似類所具有的共同成員變量和方法定義在父類中,這樣這些子類的設(shè)計(jì)代碼就可以大大減少。
    ??? 另一方面,繼承(特別是部分修改繼承和完全覆蓋繼承)使得大型軟件的功能修改和功能擴(kuò)充較傳統(tǒng)的軟件設(shè)計(jì)方法容易了許多。當(dāng)要對(duì)系統(tǒng)的一些原有功能進(jìn)行補(bǔ)充性修改或添加一些新的功能時(shí),可以重新設(shè)計(jì)原先類的一個(gè)子類,利用部分修改繼承方法重新設(shè)計(jì)子類中要補(bǔ)充性修改或添加的功能;當(dāng)要廢棄系統(tǒng)的一些原有功能,重新設(shè)計(jì)完全不同的新的功能時(shí),可以重新設(shè)計(jì)原先類的一個(gè)子類,利用完全覆蓋繼承方法重新設(shè)計(jì)子類中的功能。
    ??? 繼承性是面向?qū)ο蠓椒ǖ囊粋€(gè)非常重要的特點(diǎn)。這是因?yàn)槔^承性使得我們可以根據(jù)問(wèn)題的特征,把若干個(gè)類設(shè)計(jì)成繼承關(guān)系。而類的繼承關(guān)系和人類認(rèn)識(shí)客觀世界的過(guò)程和方法基本吻合,從而使得人們能夠用和認(rèn)識(shí)客觀世界一致的方法來(lái)設(shè)計(jì)軟件。
    ?
    4、方法的多態(tài)性
    ?
    1.對(duì)象的動(dòng)態(tài)綁定和方法的多態(tài)性
    ??? 方法的多態(tài)性是面向?qū)ο蟪绦虻牧硪粋€(gè)重要特點(diǎn)。方法的多態(tài)(polymorphism)是指若以父類定義對(duì)象,并動(dòng)態(tài)綁定對(duì)象,則該對(duì)象的方法將隨綁定對(duì)象不同而不同。
    ??? 在只定義對(duì)象、沒有分配內(nèi)存空間時(shí),如下圖(a)所示,對(duì)象名中并沒有存放實(shí)際對(duì)象的首地址。在為已定義的對(duì)象分配了內(nèi)存空間后,如下圖(b)所示,對(duì)象名中存儲(chǔ)的就是對(duì)象的內(nèi)存空間的首地址。對(duì)象名和實(shí)際對(duì)象的這種聯(lián)系稱作對(duì)象的綁定(binding)。
    ?
    ? ?? java3-2.gif
    ??? Java語(yǔ)言還支持對(duì)象的動(dòng)態(tài)綁定。所謂對(duì)象的動(dòng)態(tài)綁定,是指定義為類樹上層的對(duì)象名,可以綁定為所定義層類以及下層類的對(duì)象。這樣,當(dāng)對(duì)象動(dòng)態(tài)綁定為哪一層子類對(duì)象時(shí),其方法就調(diào)用那一層子類的方法。因此,對(duì)象的動(dòng)態(tài)綁定和類的繼承相結(jié)合就使對(duì)象的方法具有多態(tài)性。
    ?
    【例4.3】方法的多態(tài)性示例。
    class Shape??????????????//定義父類 Shape
    {
    ?public void draw()?????//父類的draw()方法
    ??{
    ????System.out.println("Draw a Shape");
    ??}
    }
    ?
    class Circle extends Shape???? //定義子類Circle
    {
    ?public void draw()?????//覆蓋父類的draw()方法
    ?{
    ??? ??System.out.println("draw a Circle");
    ?}
    }
    ?
    class Ellipse extends Circle???//定義子類Ellipse
    {
    ?public void draw()?????//覆蓋父類的draw()方法
    ??? {
    ??? ??System.out.println("draw a Ellipse");
    ?}
    }
    ?
    public class FInherit???????//定義類FInherit
    {
    ?public static void main(String args[])
    ?{
    ??Shape s= new Shape();???? //動(dòng)態(tài)綁定為類Shape對(duì)象
    ??Shape c = new Circle();???//動(dòng)態(tài)綁定為類Circle對(duì)象
    ??Shape e = new Ellipse();??//
    動(dòng)態(tài)綁定為類Ellipse對(duì)象
    ?
    ??????? s.draw();??????//訪問(wèn)父類方法
    ??????? c.draw();??????//訪問(wèn)一級(jí)子類方法
    ??????? e.draw();??????//訪問(wèn)二級(jí)子類方法???????
    ?}
    }
    程序運(yùn)行結(jié)果如下:
    Draw a Shape
    draw a Circle
    draw a Ellipse

    程序說(shuō)明:
    (1)類Shape是父類, 類Circle是類Shape的直接子類,類Ellipse是類Circle的直接子類。這三個(gè)類中都定義了draw()方法。子類中的draw()方法覆蓋了父類中的同名方法。
    (2)FInherit 類的main()方法中,定義了三個(gè)對(duì)象,三個(gè)對(duì)象s、c和e都定義為Shape類,但對(duì)象s動(dòng)態(tài)綁定為Shape類的對(duì)象,對(duì)象c動(dòng)態(tài)綁定為Circle類的對(duì)象,對(duì)象e動(dòng)態(tài)綁定為Ellipse類的對(duì)象。這樣,語(yǔ)句s.draw()調(diào)用的就是Shape類的方法draw(),語(yǔ)句c.draw()調(diào)用的就是Circle類的方法draw(),語(yǔ)句e.draw()調(diào)用的就是Ellipse類的方法draw()。
    ?
    2.方法多態(tài)性的用途
    ??? 方法的多態(tài)性在程序設(shè)計(jì)中非常有用。例如Java API語(yǔ)言包的Vector類,Vector類中定義的一個(gè)方法如下:
    ??? copyInto(Object[] anArray)
    ??? 該方法的功能是把當(dāng)前對(duì)象的一個(gè)成分復(fù)制給對(duì)象數(shù)組anArray。其參數(shù)anArray定義為Object類的數(shù)組,由于Object類是所有類的根(即最上層的類),所以,該方法可用于任何類的對(duì)象。例如,程序中可以像下面這樣使用Vector類的copyInto()方法:
    ??? Vector v = new Vector();????????????//定義并創(chuàng)建Vector類的對(duì)象v
    ??? String[] s = new String[v.size()];??//定義并創(chuàng)建String類的對(duì)象s
    ??? v.copyInto(s);????????????????????? //把對(duì)象v的當(dāng)前成分復(fù)制給對(duì)象s
    ??? 上面語(yǔ)句段的最后一句將把對(duì)象v的當(dāng)前成分復(fù)制給對(duì)象s。如果沒有方法的多態(tài)性,若要定義Vector類的copyInto()方法適合所有類的對(duì)象時(shí),就要把該方法用不同類的參數(shù)重載很多個(gè);而方法的多態(tài)性支持Vector類的copyInto()方法用Object類參數(shù)(Object [] anArray)定義一次,就可以適合于所有類的對(duì)象了。
    ?
    ?
    3、抽象類和最終類
    ?
    ??? 在類的定義中,除了可說(shuō)明該類的父類外,還可以說(shuō)明該類是否是最終類或抽象類。
    ?
    1、抽象類
    ?
    ??? 類中允許定義抽象方法。所謂抽象方法是指只有方法的定義,沒有方法的實(shí)現(xiàn)體的方法。Java語(yǔ)言用關(guān)鍵字abstract來(lái)聲明抽象方法。例如:
    ??? abstract?void?draw()
    ??? 則聲明類中的draw()方法為抽象方法。但是,需要說(shuō)明的是:
    ??? (1)構(gòu)造方法不能被聲明為抽象的。
    ??? (2)abstract和static不能同時(shí)存在,即不能有abstract static方法。
    ??? 包含抽象方法的類稱為抽象類。換句話說(shuō),任何包含抽象方法的類必須被聲明為抽象類。因?yàn)槌橄箢愔邪瑳]有實(shí)現(xiàn)的方法,所以抽象類是不能直接用來(lái)定義對(duì)象。Java語(yǔ)言用關(guān)鍵字abstract來(lái)聲明抽象類,例如:
    ??? abstract?class?Shape?
    ??? 則聲明類Shape為抽象類。
    ??? 在程序設(shè)計(jì)中,抽象類主要用于定義為若干個(gè)功能類同的類的父類。
    【例4.4】抽象類舉例。
    問(wèn)題描述:設(shè)計(jì)橢圓類Ellipse和矩形類Rectangle,要求這兩個(gè)類都包含一個(gè)畫圖方法draw()。
    設(shè)計(jì)分析:橢圓類Ellipse和矩形類Rectangle有許多成員變量和方法相同,因此,可以先設(shè)計(jì)一個(gè)它們的共同的父類(也稱基類)Shape,并把畫圖方法draw()定義在父類中。但是,由于父類Shape只是抽象的形狀,畫圖方法draw()無(wú)法實(shí)現(xiàn),所以,父類中的畫圖方法draw()只能定義為抽象方法,而包含抽象方法的Shape類也只能定義為抽象類。
    abstract class Shape???????????//定義抽象類 Shape
    {
    ?public abstract void draw();??//定義抽象方法
    }
    ?
    class Ellipse extends Shape????//定義子類Ellipse
    {
    ?public void draw()?????????? //實(shí)現(xiàn)draw()方法
    ?{
    ??? System.out.println("draw a Ellipse");
    ?}
    }
    ?
    class Rectangle extends Shape??//定義子類Rectangle
    {
    ?public void draw()????????? ?//實(shí)現(xiàn)draw()方法
    ?{
    ??? System.out.println("draw a Rectangle");
    ?}
    }
    ?
    public class AInherit???????? //定義類Inherit
    {
    ?public static void main(String args[])
    ??{
    ??? Ellipse ellipse = new Ellipse();?????? //創(chuàng)建子類Ellipse的對(duì)象
    ??? Rectangle rectangle = new Rectangle();//創(chuàng)建子類rectangle對(duì)象
    ?
    ??? ellipse.draw();?????//訪問(wèn)子類ellipse的方法
    ??? rectangle.draw();????//訪問(wèn)子類rectangle的方法
    ?? }
    }

    上述例子說(shuō)明:
    (1)在一個(gè)軟件中,抽象類一定是某個(gè)類或某些類的父類。
    (2)若干個(gè)抽象類的子類要實(shí)現(xiàn)一些同名的方法。
    在后面討論的Java API中,系統(tǒng)的許多類都是用上面形式的結(jié)構(gòu)定義和實(shí)現(xiàn)的。
    ?
    2、最終類
    ?
    ??? 最終類是指不能被繼承的類,即不能再用最終類派生子類。在Java語(yǔ)言中,如果不希望某個(gè)類被繼承,可以聲明這個(gè)類為最終類。最終類用關(guān)鍵字final來(lái)說(shuō)明。例如:
    ??? public final class C
    ??? 就定義類C為最終類。
    ??? 如果創(chuàng)建最終類似乎不必要,而又想保護(hù)類中的一些方法不被覆蓋,可以用關(guān)鍵字final來(lái)指明那些不能被子類覆蓋的方法,這些方法稱為最終方法。例如:
    ??? public class A
    ????{
    ????? public final void M();
    ??? }

    ??? 就在類A中定義了一個(gè)最終方法M(),任何類A的子類都不能重新定義方法M()。
    ??? 在程序設(shè)計(jì)中,最終類可以保證一些關(guān)鍵類的所有方法,不會(huì)在以后的程序維護(hù)中,由于不經(jīng)意的定義子類而被修改;最終方法可以保證一些類的關(guān)鍵方法,不會(huì)在以后的程序維護(hù)中,由于不經(jīng)意的定義子類和覆蓋子類的方法而被修改。
    ??? 需要注意的是:一個(gè)類不能既是最終類又是抽象類,即關(guān)鍵字abstract和final不能合用。在類聲明中,如果需要同時(shí)出現(xiàn)關(guān)鍵字public和abstract(或final),習(xí)慣上,public放在abstract(或final)的前面。
    ?
    ?
    4、接口
    ?
    ??? 面向?qū)ο蟪绦蛟O(shè)計(jì)語(yǔ)言的一個(gè)重要特性是繼承。繼承是指子類可以繼承父類的成員變量和方法。如果子類只允許有一個(gè)直接父類,這樣的繼承稱作單繼承。如果子類允許有一個(gè)以上的直接父類,這樣的繼承稱作多繼承。單繼承具有結(jié)構(gòu)簡(jiǎn)單,層次清楚,易于管理,安全可靠的特點(diǎn)。多繼承具有功能強(qiáng)大的特點(diǎn)。
    ??? Java語(yǔ)言只支持單繼承機(jī)制,不支持多繼承。一般情況下,單繼承就可以解決大部分子類對(duì)父類的繼承問(wèn)題。但是,當(dāng)問(wèn)題復(fù)雜時(shí),若只使用單繼承,可能會(huì)給設(shè)計(jì)帶來(lái)許多麻煩。Java語(yǔ)言解決這個(gè)問(wèn)題的方法是使用接口。
    ??? 接口和抽象類非常相似,都是只定義了類中的方法,沒有給出方法的實(shí)現(xiàn)。
    ??? Java語(yǔ)言不僅規(guī)定一個(gè)子類只能直接繼承自一個(gè)父類,同時(shí)允許一個(gè)子類可以實(shí)現(xiàn)(也可以說(shuō)繼承自)多個(gè)接口。由于接口和抽象類的功能類同,因此,Java語(yǔ)言的多繼承機(jī)制是借助于接口來(lái)實(shí)現(xiàn)的。
    ?
    1、定義接口
    ?
    ??? 接口的定義格式為:
    ??? <修飾符> interface<接口名>
    ??? {
    ????? 成員變量1 =?<初值1>;
    ????? 成員變量2 =?<初值2>;
    ????? ……
    ????? 方法1;
    ?????方法2;
    ????? ……
    ??? }

    ??? 其中,<修飾符>可以是public,也可以缺省。當(dāng)為缺省時(shí),接口只能被與它處在同一包中的方法訪問(wèn);當(dāng)聲明為public時(shí),接口能被任何類的方法訪問(wèn)。<接口名>是接口的名字,可以是任何有效的標(biāo)識(shí)符。例如,
    ??? public interface PrintMessage?
    ??? {
    ????? public int count = 10;
    ????? public void printAllMessage();
    ????? public void printLastMessage();
    ????? public void printFirstMessage();
    ??? }
    ??? 就定義了一個(gè)接口PrintMessage。接口中的方法(printAllMessage()等)只有方法定義,沒有方法實(shí)現(xiàn)。所以接口實(shí)際上是一種特殊的抽象類。
    ??? 需要說(shuō)明的是:
    ????(1)若接口定義為默認(rèn)型訪問(wèn)權(quán)限,則接口中的成員變量全部隱含為final static型。這意味著它們不能被實(shí)現(xiàn)接口方法的類改變,并且為默認(rèn)訪問(wèn)權(quán)限。
    ????(2)接口中定義的所有成員變量都必須設(shè)置初值。
    ????(3)若接口定義為public型訪問(wèn)控制,則接口中的方法和成員變量全部隱含為public型。
    ????(4)當(dāng)接口保存于文件時(shí),其文件命名方法和保存類的文件命名方法類同。即保存接口的文件名必須與接口名相同。一個(gè)文件可以包含若干個(gè)接口,但最多只能有一個(gè)接口定義為public,其他的接口必須為默認(rèn)。

    ?
    2、實(shí)現(xiàn)接口
    ?
    ??? 一旦定義了一個(gè)接口,一個(gè)或更多的類就能實(shí)現(xiàn)這個(gè)接口。為了實(shí)現(xiàn)接口,類必須實(shí)現(xiàn)定義在接口中的所有方法。每個(gè)實(shí)現(xiàn)接口的類可以自由地決定接口方法的實(shí)現(xiàn)細(xì)節(jié)。
    ??? 定義類時(shí)實(shí)現(xiàn)接口用關(guān)鍵字implements。一個(gè)類只能繼承一個(gè)父類,但可以實(shí)現(xiàn)若干個(gè)接口。因此,類定義的完整格式是:
    ??? [<修飾符>]class<類名> [extends<父類名>] [implements <接口名1>,<接口名2>,……]
    ??? 其中,關(guān)鍵字implements后跟隨的若干個(gè)接口名表示該類要實(shí)現(xiàn)的接口;如果要實(shí)現(xiàn)多個(gè)接口,則用逗號(hào)分隔開接口名。
    【例4.5】編寫一個(gè)實(shí)現(xiàn)接口PrintMessage(為簡(jiǎn)化設(shè)計(jì)代碼,去掉其中的成員變量定義)的類,并編寫一個(gè)測(cè)試程序進(jìn)行測(cè)試。
    程序設(shè)計(jì)如下:
    //接口文件PrintMessage.java
    public interface PrintMessage
    {
    ?public int count = 10;
    ? public void printAllMessage();
    ? public void printLastMessage();
    ? public void printFirstMessage();
    }
    ?
    //實(shí)現(xiàn)接口的文件MyInter.java
    public class MyInter implements PrintMessage?//實(shí)現(xiàn)接口的類MyInter
    {
    ? private String[] v;????//類中的成員變量v
    ? private int i;????????//類中的成員變量i
    ?
    ? public MyInter()??????// MyInter類的構(gòu)造方法
    ? {
    ????v = new String[3];
    ????i = 0;
    ??? ??this.putMessage("Hello world!");?? //使用MyInter類的方法
    ??? ??this.putMessage("Hello China!");
    ??? ??this.putMessage("Hello XSYU!");
    ? ?}
    ?
    ? public void putMessage(String str)????//類中的方法
    ??{?
    ?????v[i++] = str;
    ? }
    ?
    ? public void printAllMessage()????????? //實(shí)現(xiàn)接口中的方法
    ??{
    ?????for(int k = 0; k < v.length; k++)
    ?????{
    ???????System.out.println(v[k]);
    ?????}
    ??}
    ??public void printLastMessage()???????? //實(shí)現(xiàn)接口中的方法
    ??{
    ?????System.out.println(v[v.length - 1]);
    ??}
    ?
    ??public void printFirstMessage()??????? //實(shí)現(xiàn)接口中的方法
    ??{
    ?????System.out.println(v[0]);
    ??}
    ?
    ??public static void main(String[] args)??
    ??{
    ?????MyInter mi=new MyInter();???????? //定義MyInter類的對(duì)象
    ?????System.out.println("print all messages");
    ?????mi.printAllMessage();???????????? //使用實(shí)現(xiàn)了的接口方法
    ?????System.out.println("print the first messages");
    ?????mi.printFirstMessage();????????? ?//使用實(shí)現(xiàn)了的接口方法
    ?????System.out.println("print the last messages");
    ?????mi.printLastMessage();??????????? //使用實(shí)現(xiàn)了的接口方法
    ??}
    }
    程序的運(yùn)行結(jié)果如下:
    print all messages
    Hello world!
    Hello China!
    Hello XSYU!
    print the first messages
    Hello world!
    print the last messages
    Hello XSYU!

    ??? 程序說(shuō)明:在定義類MyInter時(shí),后邊跟有implements PrintMessage,表示該類中要實(shí)現(xiàn)接口PrintMessage。此時(shí)類MyInter中必須實(shí)現(xiàn)接口PrintMessage中定義的三個(gè)方法。由于類MyInter隱含繼承了類Object,現(xiàn)在又實(shí)現(xiàn)了接口PrintMessage,所以類MyInter是一個(gè)多繼承。可見,接口支持了Java的多繼承。
    ?
    3、系統(tǒng)定義的接口
    ?
    ??? Java API中定義了許多接口,一旦安裝了JDK運(yùn)行環(huán)境,就可以像使用用戶自己定義的接口一樣使用系統(tǒng)定義的接口。例如,Enumeration是系統(tǒng)定義的一個(gè)接口。Enumeration接口的定義如下:
    ??? public interface Enumeration?
    ??? {
    ????? Object nextElement();????? ?//返回后續(xù)元素
    ????? boolean hasMoreElements();??//是否還有后續(xù)元素
    ??? }
    ??? 許多系統(tǒng)定義的類都實(shí)現(xiàn)了Enumeration接口。如有必要,用戶自己定義的類也可以實(shí)現(xiàn)Enumeration接口。




    -The End-

    posted on 2008-09-23 15:15 decode360-3 閱讀(1536) 評(píng)論(1)  編輯  收藏 所屬分類: Java

    評(píng)論

    # re: Java學(xué)習(xí)(三).類與繼承 2013-06-17 11:23 44
    萬(wàn)歲
      回復(fù)  更多評(píng)論
      

    主站蜘蛛池模板: 亚洲伊人久久大香线蕉苏妲己| 免费欧洲美女牲交视频| 亚洲av无码无在线观看红杏| 国产精品1024在线永久免费| 亚洲av无码国产精品色在线看不卡| 亚洲av无码一区二区三区在线播放| 在线免费观看a级片| 亚洲精品人成网线在线播放va| 一个人看的www在线观看免费| 亚洲妓女综合网99| 91免费资源网站入口| 亚洲av无码成人精品国产| 全黄a免费一级毛片人人爱| 黄网站色视频免费观看45分钟| 国产精品亚洲美女久久久| 中文字幕不卡高清免费| 亚洲午夜视频在线观看| 国产免费AV片在线播放唯爱网| 亚洲中文无码mv| 亚洲成av人片在线观看天堂无码 | 尤物永久免费AV无码网站| 亚洲AV色欲色欲WWW| 亚洲A丁香五香天堂网| a毛片在线看片免费| 亚洲欧洲自拍拍偷午夜色| 日韩一区二区在线免费观看| 一区二区三区精品高清视频免费在线播放| 亚洲综合久久夜AV | 最近免费中文字幕大全免费版视频 | 9420免费高清在线视频| 亚洲欧美日韩中文高清www777| xvideos亚洲永久网址| 国产一精品一av一免费爽爽| 亚洲伊人久久大香线焦| 亚洲国产精品尤物yw在线| 少妇人妻偷人精品免费视频| 亚洲av永久中文无码精品| 亚洲国产精品一区二区成人片国内 | 久久国产亚洲精品| 国产亚洲精品AA片在线观看不加载| 99久久国产免费-99久久国产免费|