你真的理解了繼承和多態(tài)嗎?
Posted on 2005-06-07 15:18 laogao 閱讀(7679) 評(píng)論(14) 編輯 收藏 所屬分類: On Java 、Programming in General最近被同事問起一道SCJP的題目,是跟繼承和多態(tài)有關(guān)的。具體的題目我就不重復(fù)了,來看一段我自己敲的代碼:
1
package sean.work.test;
2
3
public 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
30
class A
{
31
String s = "[A]";
32
String getS()
{
33
return s;
34
}
35
}
36
37
class B extends A
{
38
String s = "[B]";
39
String getS()
{
40
return s;
41
}
42
}
43

2

3



4

5



6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30



31

32



33

34

35

36

37



38

39



40

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ò)。
