你真的理解了繼承和多態嗎?
Posted on 2005-06-07 15:18 laogao 閱讀(7675) 評論(14) 編輯 收藏 所屬分類: On Java 、Programming in General最近被同事問起一道SCJP的題目,是跟繼承和多態有關的。具體的題目我就不重復了,來看一段我自己敲的代碼:
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()方法,于是我們可以利用到多態。如果你留意15、16、21、22這幾行的話,你也許就會知道我在說什么了。假如你覺得這樣的打印結果是理所當然,那么我想你可以完全忽略這篇隨筆,因為我要講的就集中在這幾行,而你已經很清楚的理解背后的含義。
下面跟感興趣的朋友們說說我的理解:
直觀的講,我們很容易輕信當"a = b;"以后,變量a指向的對象是B類的b那個對象,自然a.s就應該等同于b.s,然而事實并非如此。當B繼承A時,父類A的字段s并沒有被B的字段s取代,而是保留了一份拷貝,所謂重寫(Override),那是對方法而言的。于是,當我們new B()時,在實際創建的對象中,包含了兩個版本的字段s,一個"[A]"(屬于A類)一個"[B]"(屬于B類)。而方法getS()只有一個版本。這就是在繼承過程中字段和方法的區別。也就是說,重寫的概念和字段無關。在第16行,我們通過a.s訪問的是b這個對象中保留的A類的字段s;而在21行,我們改變的正是這個A類版本的s字段。
多態的精髓在于動態確定對象的行為,而對象的行為體現在方法而非字段,字段代表的更多的是對象的狀態。于是只有方法的多態而沒有字段的多態。從上面的代碼可以看出,不管你用什么類型的變量存放對象b的引用,最終調用的方法版本都是b的真實類型那個版本,這就是多態的威力。
從編譯的角度來看,上面代碼中的s字段和getS()方法的不同在于:s字段是在編譯期靜態鏈接(你可以改變它的值,但是它在對象中的相對地址已經確定好),而getS()方法是在運行期動態鏈接的。
說了這么多,真的不知道我表達清楚沒有,畢竟沒有系統研究過OO和編譯原理,說得不當的地方還請多多包涵。最后,請不要學我這里的編碼風格,因為很顯然應該對main方法中的代碼段執行Extract Method重構了,我這里只是為了注釋方便。
// BTW,時不時抽空想想這樣的問題感覺真不錯。
