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

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

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

    Read Sean

    Read me, read Sean.
    posts - 508, comments - 655, trackbacks - 9, articles - 4

    你真的理解了繼承和多態(tài)嗎?

    Posted on 2005-06-07 15:18 laogao 閱讀(7682) 評論(14)  編輯  收藏 所屬分類: On JavaProgramming in General

    最近被同事問起一道SCJP的題目,是跟繼承和多態(tài)有關(guān)的。具體的題目我就不重復(fù)了,來看一段我自己敲的代碼:

     1package sean.work.test;
     2
     3public class DoYouReallyUnderstandPolymorphism {
     4    
     5    public static void main(String[] args) {
     6        A a = new A();
     7        B b = new B();
     8        a.s = "[AA]";
     9        b.s = "[BB]";
    10        System.out.println(a.s);      // prints "[AA]"
    11        System.out.println(b.s);      // prints "[BB]"
    12        System.out.println(a.getS()); // prints "[AA]"
    13        System.out.println(b.getS()); // prints "[BB]"
    14        System.out.println("====================");
    15        a = b; // a now refers to object b
    16        System.out.println(a.s);      // prints "[A]"  <<--1-- the class A copy
    17        System.out.println(b.s);      // prints "[BB]"
    18        System.out.println(a.getS()); // prints "[BB]"
    19        System.out.println(b.getS()); // prints "[BB]"
    20        System.out.println("====================");
    21        ((A)b).s = "[AA]"// <<--2-- changes the class A copy in object b 
    22        System.out.println(a.s);      // prints "[AA]" <<--3-- class A copy changed
    23        System.out.println(b.s);      // prints "[BB]"
    24        System.out.println(a.getS()); // prints "[BB]"
    25        System.out.println(b.getS()); // prints "[BB]"
    26    }

    27
    28}

    29
    30class A {
    31    String s = "[A]";
    32    String getS() {
    33        return s;
    34    }

    35}

    36
    37class B extends A{
    38    String s = "[B]";
    39    String getS() {
    40        return s;
    41    }

    42}

    43

    這里我們的B類繼承自A類,重寫了getS()方法,于是我們可以利用到多態(tài)。如果你留意15、16、21、22這幾行的話,你也許就會知道我在說什么了。假如你覺得這樣的打印結(jié)果是理所當(dāng)然,那么我想你可以完全忽略這篇隨筆,因為我要講的就集中在這幾行,而你已經(jīng)很清楚的理解背后的含義。

    下面跟感興趣的朋友們說說我的理解:

    直觀的講,我們很容易輕信當(dāng)"a = b;"以后,變量a指向的對象是B類的b那個對象,自然a.s就應(yīng)該等同于b.s,然而事實并非如此。當(dāng)B繼承A時,父類A的字段s并沒有被B的字段s取代,而是保留了一份拷貝,所謂重寫(Override),那是對方法而言的。于是,當(dāng)我們new B()時,在實際創(chuàng)建的對象中,包含了兩個版本的字段s,一個"[A]"(屬于A類)一個"[B]"(屬于B類)。而方法getS()只有一個版本。這就是在繼承過程中字段和方法的區(qū)別。也就是說,重寫的概念和字段無關(guān)。在第16行,我們通過a.s訪問的是b這個對象中保留的A類的字段s;而在21行,我們改變的正是這個A類版本的s字段。

    多態(tài)的精髓在于動態(tài)確定對象的行為,而對象的行為體現(xiàn)在方法而非字段,字段代表的更多的是對象的狀態(tài)。于是只有方法的多態(tài)而沒有字段的多態(tài)。從上面的代碼可以看出,不管你用什么類型的變量存放對象b的引用,最終調(diào)用的方法版本都是b的真實類型那個版本,這就是多態(tài)的威力。

    從編譯的角度來看,上面代碼中的s字段和getS()方法的不同在于:s字段是在編譯期靜態(tài)鏈接(你可以改變它的值,但是它在對象中的相對地址已經(jīng)確定好),而getS()方法是在運行期動態(tài)鏈接的。

    說了這么多,真的不知道我表達(dá)清楚沒有,畢竟沒有系統(tǒng)研究過OO和編譯原理,說得不當(dāng)?shù)牡胤竭€請多多包涵。最后,請不要學(xué)我這里的編碼風(fēng)格,因為很顯然應(yīng)該對main方法中的代碼段執(zhí)行Extract Method重構(gòu)了,我這里只是為了注釋方便。

    // BTW,時不時抽空想想這樣的問題感覺真不錯。

    Feedback

    # re: 你真的理解了繼承和多態(tài)嗎?  回復(fù)  更多評論   

    2005-06-07 16:26 by 小毅
    謝謝您,學(xué)到很容易忽略的東西,覺得很重要
    再謝謝你...

    # 原來字段和方法有這樣的區(qū)別哦  回復(fù)  更多評論   

    2005-06-08 09:02 by emu
    >>只有方法的多態(tài)而沒有字段的多態(tài)

    以前都沒有想過這個問題哦。

    前兩天遇到一個內(nèi)嵌類的方法重載時候的古怪問題,不知是否與此有關(guān),正好回頭去研究一下。

    # re: 你真的理解了繼承和多態(tài)嗎?  回復(fù)  更多評論   

    2005-06-08 12:16 by FS
    Java的對象模型偶不太清楚,不過通過對Delphi的對象模型的了解,我感覺應(yīng)該和Java沒有太大的區(qū)別。

    按照樓主所說的,當(dāng)執(zhí)行了語句“a=b”后,變量a和b的內(nèi)容應(yīng)該是不變的,所變的是a指向的對象實體中的指向類的指針的內(nèi)容,在Delphi中,這個指針叫做vptr,CPP中也是如此叫法。vptr直接指到類的VMT(不知道Java中如何叫法);因此,再次使用a.s訪問實例變量,則要進入b指向的對象實體中進行字段的查找,但由于b里面存在兩個s字段,并且以類型信息為區(qū)分方式,所以找到的是類型A的字段s。至于編譯處理方面,我想應(yīng)該是把類型信息作為字段的前綴或者后綴。

    同樣,當(dāng)執(zhí)行了“((A)b).s=“[AA]””后,由于已經(jīng)把b的類型轉(zhuǎn)化為A類型,而且b指針的未改變,所以在b的對象實體中,實際改變的是屬于類型A的字段s的內(nèi)容。個人感覺,其實樓主已經(jīng)說出了,對b中不同類型的同名字段的訪問方式。

    最后留下一個問題:這種對象模型中的字段處理方式如果在繼承字段訪問量小的情況下是很浪費空間的。那么,這樣處理的優(yōu)點又是什么?

    # re: 你真的理解了繼承和多態(tài)嗎?  回復(fù)  更多評論   

    2005-06-08 12:18 by FS
    按照OO這種東西在語言設(shè)計上的考慮,對象所涉及到的所有內(nèi)容就是三塊:對象指針,對象實體和對象所屬類型的內(nèi)存布局。而對象實體中只存放狀態(tài)信息,即對象所屬類型的實例字段的內(nèi)容,因此,從這個角度考慮,多態(tài)和實例字段的信息是沒有任何關(guān)系的。但對于類字段,即申明為static的字段就未必了。

    # re: 你真的理解了繼承和多態(tài)嗎?  回復(fù)  更多評論   

    2005-07-20 23:24 by 丑男
    System.out.println(a.s); // prints "[A]"
    這句確實我沒想到!看來真被難倒了

    # re: 你真的理解了繼承和多態(tài)嗎?  回復(fù)  更多評論   

    2006-05-15 17:30 by songxu
    非常感謝

    # re: 你真的理解了繼承和多態(tài)嗎?  回復(fù)  更多評論   

    2006-07-15 17:22 by tcpipok
    我是想到了,不過就像樓主所說的,這種編碼風(fēng)格不好,子類盡量不要用父類用過的變量名,真的是容易搞混,這種代碼用來考試可以,但用來編程不好.

    # re: 你真的理解了繼承和多態(tài)嗎?  回復(fù)  更多評論   

    2006-08-17 20:34 by 小小
    請問:
    java中字符串的連接是不是一種多態(tài)的表現(xiàn)?

    # re: 你真的理解了繼承和多態(tài)嗎?  回復(fù)  更多評論   

    2007-03-05 22:52 by 谷鈺
    @tcpipok
    我不知道你是否理解OO真正的精髓,以上用法展示了面向?qū)ο笤O(shè)計的核心思想,通過虛方法調(diào)用,實現(xiàn)不同子類對同一行為(方法)的不同實現(xiàn)。希望以下例子可以給大家一些啟發(fā):

    # re: 你真的理解了繼承和多態(tài)嗎?  回復(fù)  更多評論   

    2007-03-05 23:05 by 谷鈺
    public class Employee {

    protected String name = "張三";

    private double salary = 1000;

    public Employee(String name, double salary) {
    this.name = name;
    this.salary = salary;
    }

    public Employee() {
    this("王老五", 1);
    }

    public String getDetails() {
    return "姓名:" + this.name + ", 工資: " + this.salary;
    }

    public double getSalary() {
    return this.salary;
    }

    public void setSalary(double salary) {
    this.salary = salary;
    }

    public String getName() {
    return this.name;
    }

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

    public boolean equals(Object o) {
    if (this == o) {
    return true;
    }

    if (o instanceof Employee) {
    Employee te = (Employee) o;

    return this.salary == te.getSalary() && this.name.equals(te.getName());
    }

    return false;
    }

    public int hashCode() {
    if (this.name == null) {
    return 0;
    }
    return this.name.hashCode();
    }

    public String toString() {
    return "$$" + this.getDetails();
    // return super.toString();
    }

    public static void printAll(Employee[] all) {
    System.out.println("所有員工:");
    for (int i = 0; i < all.length; ++i) {
    System.out.println(all[i].getDetails());
    }
    }

    public static void main(String[] args) {
    Employee e = new Employee("張三", 2500);
    System.out.println(e.getDetails());

    Manager m = new Manager("李四", 4500, "軟件開發(fā)部");
    System.out.println(m.getDetails());

    Employee ee = new Manager("王五", 4500, "軟件銷售部");
    System.out.println(ee.getDetails());

    if (ee instanceof Manager) {
    Manager mmm = (Manager) ee;
    System.out.println(mmm.getDepartment());
    }

    Employee e2 = new Employee("張三", 2500);
    System.out.println(e.equals(e2));

    System.out.println(e);
    System.out.println("##" + e);

    Employee[] eAll = {
    e, m, ee
    };
    printAll(eAll);
    }
    }

    class Manager extends Employee {
    private String department = "44";

    public Manager() {
    super();
    this.department = "計算機";
    }

    public Manager(String name, double salary, String department) {
    super(name, salary);

    this.department = department;
    }

    public String getDetails() {
    return super.getDetails() + ", 管理部門: " + this.department;
    }

    public String getDepartment() {
    return this.department;
    }
    }

    # re: 你真的理解了繼承和多態(tài)嗎?  回復(fù)  更多評論   

    2007-03-05 23:11 by 谷鈺
    核心在
    public static void printAll(Employee[] all) {
    System.out.println("所有員工:");
    for (int i = 0; i < all.length; ++i) {
    System.out.println(all[i].getDetails());
    }
    }
    函數(shù)的設(shè)計上,通過Employee隱藏所有雇員子類的細(xì)節(jié),通過getDetails()方法來隱藏不同雇員(Manger,Engineer等)對詳細(xì)信息的函數(shù)細(xì)節(jié)。

    只有這樣的函數(shù)才能最大程度上復(fù)用,既當(dāng)設(shè)計有新的Employee子類定義也不許要對此函數(shù)做任何的修改,個人認(rèn)為面向?qū)ο蟮淖畲蠛锰幨翘岣叱绦虻膹?fù)用性,降低維護成本。這些需要多態(tài),繼承才能作到。:)

    # re: 你真的理解了繼承和多態(tài)嗎?  回復(fù)  更多評論   

    2007-03-06 09:38 by 大胃
    謝謝 谷鈺 的回復(fù)!

    # re: 你真的理解了繼承和多態(tài)嗎?  回復(fù)  更多評論   

    2008-05-21 17:06 by tonytang
    to 谷鈺,你的代碼也沒有回答tcpipok的疑問啊。子類和父類定義相同的屬性有意義嗎?

    # re: 你真的理解了繼承和多態(tài)嗎?  回復(fù)  更多評論   

    2008-06-25 11:00 by shell
    寫的太好了。
    學(xué)習(xí)了很多。
    主站蜘蛛池模板: 久久亚洲精品人成综合网| 午夜影院免费观看| 国产成人精品亚洲2020| 国产成A人亚洲精V品无码性色| 日本特黄特黄刺激大片免费| 最近中文字幕免费完整| 99久久免费国产精精品| 美女露隐私全部免费直播| 中文日韩亚洲欧美制服| 久久久久亚洲AV无码永不| 亚洲色大成网站WWW久久九九| 国产又粗又猛又爽又黄的免费视频 | 老司机亚洲精品影院无码| 国产成人99久久亚洲综合精品| 在线成人a毛片免费播放| 18禁黄网站禁片免费观看不卡| 国产免费人人看大香伊| 免费观看的毛片大全| 久久国产乱子免费精品| 中国一级毛片免费看视频| 人碰人碰人成人免费视频| 羞羞网站在线免费观看| 老司机福利在线免费观看| 99亚洲精品卡2卡三卡4卡2卡| 亚洲精品永久在线观看| 亚洲最大的成人网| 国产亚洲精品AA片在线观看不加载 | 久久久久亚洲AV无码专区桃色| 免费a级毛片无码a∨性按摩| 青青草国产免费久久久91| 全免费a级毛片免费看无码| 成人黄动漫画免费网站视频 | 亚洲性一级理论片在线观看| 亚洲影视一区二区| 亚洲一区无码中文字幕乱码| 亚洲精品中文字幕乱码| 亚洲精品**中文毛片| 亚洲综合色一区二区三区| 亚洲综合小说另类图片动图| 久久精品国产亚洲av瑜伽| 免费精品视频在线|