Read Sean

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

          最近被同事問起一道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這幾行的話,你也許就會(huì)知道我在說什么了。假如你覺得這樣的打印結(jié)果是理所當(dāng)然,那么我想你可以完全忽略這篇隨筆,因?yàn)槲乙v的就集中在這幾行,而你已經(jīng)很清楚的理解背后的含義。

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

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

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

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

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

          // BTW,時(shí)不時(shí)抽空想想這樣的問題感覺真不錯(cuò)。

          Feedback

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

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

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

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

          以前都沒有想過這個(gè)問題哦。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

          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 = "計(jì)算機(jī)";
          }

          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ù)  更多評(píng)論   

          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è)計(jì)上,通過Employee隱藏所有雇員子類的細(xì)節(jié),通過getDetails()方法來隱藏不同雇員(Manger,Engineer等)對(duì)詳細(xì)信息的函數(shù)細(xì)節(jié)。

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

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

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

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

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

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

          2008-06-25 11:00 by shell
          寫的太好了。
          學(xué)習(xí)了很多。
          主站蜘蛛池模板: 马尔康县| 正定县| 漳平市| 沂南县| 广南县| 泉州市| 乌鲁木齐县| 开平市| 宽城| 依兰县| 江达县| 伊宁市| 温泉县| 丰镇市| 突泉县| 元氏县| 三原县| 道真| 石阡县| 延边| 思茅市| 潮州市| 铜鼓县| 临汾市| 曲松县| 抚州市| 日喀则市| 成都市| 沛县| 甘泉县| 绥中县| 那曲县| 辽阳县| 蓬溪县| 探索| 宁安市| 聊城市| 韶关市| 麻江县| 梁平县| 安岳县|