面向對象編程有三大特性:封裝、繼承、多態。
封裝隱藏了類的內部實現機制,可以在不影響使用的情況下改變類的內部結構,同時也保護了數據。對外界而已它的內部細節是隱藏的,暴露給外界的只是它的訪問方法。
繼承是為了重用父類代碼。兩個類若存在IS-A的關系就可以使用繼承。,同時繼承也為實現多態做了鋪墊。那么什么是多態呢?多態的實現機制又是什么?請看我一一為你揭開:
所謂多態就是指程序中定義的引用變量所指向的具體類型和通過該引用變量發出的方法調用在編程時并不確定,而是在程序運行期間才確定,即一個引用變量倒底會指向哪個類的實例對象,該引用變量發出的方法調用到底是哪個類中實現的方法,必須在由程序運行期間才能決定。因為在程序運行時才確定具體的類,這樣,不用修改源程序代碼,就可以讓引用變量綁定到各種不同的類實現上,從而導致該引用調用的具體方法隨之改變,即不修改程序代碼就可以改變程序運行時所綁定的具體代碼,讓程序可以選擇多個運行狀態,這就是多態性。練習(1):創建一個Cycle類,它具有子類Unicycle,Bycycle,Tricycle.演示每一個類型的實例都可以經由ride()方法向上轉型為Cycle.
向上轉型就是允許將多種從同一基類的導出類看成同一類型。
多態方法調用就是允許一種類型表現出與其他相似類型之間的區別,只要他們是從同一基類導出而來的。這種區別由各個導出類型方法的具體不同實現而表現出來的,雖然這些方法都是由基類調用的。
1
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
|
public class Test1 { public static void main(String[] args){ Unicycle unicycle = new Unicycle( "Unicycle" ); Bicycle bicycle = new Bicycle( "Bicycle" ); Tricycle tricycle = new Tricycle( "Tricycle" ); Cycle.ride(unicycle); Cycle.ride(bicycle); Cycle.ride(tricycle); } } class Cycle{ private String name; public Cycle(String str){ name = str; } public static void ride(Cycle c){ System.out.println(c.name + "is riding" ); } } class Unicycle extends Cycle{ private String name; public Unicycle(String str) { super (str); name = str; } } class Bicycle extends Cycle{ private String name; public Bicycle(String str) { super (str); name = str; } } class Tricycle extends Cycle { private String name; public Tricycle(String str) { super (str); name = str; } } |
輸出:
Unicycleis riding
Bicycleis riding
Tricycleis riding
在以上示例中,三種子類能被視作Cycle傳入到方法ride()中就是向上轉型。
但向上轉型只是看成,而不是強制轉換,所以最后方法調用的結果不同,這就是多態。
多態又稱之為動態綁定。什么是動態綁定?
與動態綁定相反的是靜態綁定。c語言所有方法都是默認靜態綁定。靜態綁定也稱為前期綁定,就是在程序運行前就綁定完成。也就是說,代碼寫了什么樣,就是什么樣。
而動態綁定是直到運行時才去決定該方法調用該綁定哪個實體。
java中的所有static和final方法都是靜態綁定,其他的所有方法都是動態綁定。
練習(2):在幾何圖形示例中添加@Override注解。
練習(3):在基類Shape.java中添加一個新方法,用于打印一條消息,但導出類中不要覆蓋這個方法。請解釋發生了什么?,F在,在其中一個導出類中覆蓋該方法,而在其他的導出類中不予覆蓋,觀察又有什么發生。最后,在所有的導出類中覆蓋這個方法。
練習(4):向Shapes.java中添加一個新的Shape類型,并在main()方法中驗證:多態對新類型的作用是否與在舊類型中的一樣。
以上三個練習在一份示例中完成。
1
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
public class Test234 { private static RandonShapeGenerator gen = new RandonShapeGenerator(); public static void main(String[] args){ Shape[] shapes = new Shape[ 9 ]; for ( int i = 0 ; i < shapes.length; i++) { shapes[i] = gen.next(); } for (Shape s : shapes) { s.draw(); s.newMethod(); //每個子類都調用了一次添加的新方法, // 因為子類繼承父類自然把所有方法都繼承過去了,只不過沒有顯示出來, // 其實是隱式的存在的,子類調用的其實是繼承自父類的沒有重寫的方法, // 看起來像是調用了父類的方法 } Shape s = new Recf(); //這里是聲明了一個Shape類型的引用,但實際的對象還是Recf. s.draw(); //輸出的是Recf類重寫的方法,證明多態對新類的作用于在舊類中是一樣的 } } class Shape{ //基類 public void draw(){} public void erase(){} public void newMethod(){ System.out.println( "new method" ); //添加的新方法 } } class Circle extends Shape{ @Override //添加注解,一般IDE可以自動添加 public void draw() { System.out.println( "draw circle" ); } @Override public void erase() { System.out.println( "erase circle" ); } } class Square extends Shape{ @Override public void draw() { System.out.println( "draw Square" ); } @Override public void erase() { System.out.println( "erase Square" ); } @Override public void newMethod() { System.out.println( "Square new method" ); //重寫后該類輸出內容就發生改變,沒有重寫時該類的該方法與父類運行結果相同 //無論重寫與否,其實調用的都是自身的方法 //只是沒有重寫時方法調用結果與父類的相同 } } class Triangle extends Shape{ @Override public void draw() { System.out.println( "draw Triangle" ); } @Override public void erase() { System.out.println( "erase Triangle" ); } } class Recf extends Shape{ //新添加的方法 @Override public void draw() { System.out.println( "recf draw" ); } @Override public void erase() { System.out.println( "recf erase" ); } } class RandonShapeGenerator{ //是一種工廠,用以隨機獲取一種Shape的子類 private Random rand = new Random( 100 ); public Shape next(){ switch (rand.nextInt( 3 )){ default : case 0 : return new Circle(); case 1 : return new Square(); case 2 : return new Triangle(); } } } |
練習(5):以練習1為基礎,才Cycle中添加wheels()方法,它將返回輪子的數量。修改ride()方法,讓它調用wheels()方法,并驗證多態起作用了。
在練習(1)的代碼中給基類添加
1
2
3
|
public void wheels(){ System.out.println( "輪子數量是" + num); } |
然后在main中:
1
2
3
|
unicycle.wheels(); bicycle.wheels(); tricycle.wheels(); |
最后輸出結果都順利輸出了方法中的語句,證明多態確實起作用了。
練習(6):修改Music3.java,是what()方法成為根Object的toString()方法。試用System.out.pringtln()方法打印Instrument對象(不用向上轉型).
練習(7):向Music3.java添加一個新的類型Instrument,并驗證多態性是否作用于所添加的新類型.
練習(8):修改Music3.java,使其可以向Shapes.java中的方式那樣隨機創建Instrument對象。
三個練習將在一份代碼完成。
1
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
public class Test678 { public static void main(String[] args){ Instrument[] orchestar = { new Wind(), new Percussion(), new Stringed(), new Brass(), new Woodwing() }; tuneAll(orchestar); newInstrument ni = new newInstrument(); ni.play(Note.B_FLAT); //驗證多態性是否適用于所添加的新類型,答案是確實適用。 } public static void tune(Instrument instrument){ instrument.play(Note.MIDDLE_C); //無論傳進聲明子類,都播放MIDDLE_C } public static void tuneAll(Instrument[] e){ for (Instrument i : e) { tune(i); System.out.println(i.toString()); } } } class RandomInsFactory{ //工廠類,用于隨機生成一個Instrument的子類 private Random ran = new Random( 47 ); public Instrument next(){ switch (ran.nextInt( 5 )){ default : case 0 : return new Wind(); case 1 : return new Percussion(); case 2 : return new Stringed(); case 3 : return new Brass(); case 4 : return new Woodwing(); case 5 : return new newInstrument(); } } } enum Note{ //枚舉類,存放了哪些音樂 MIDDLE_C,C_HARPE,B_FLAT; } class Instrument { void play(Note note){ System.out.println( "Instrument.play() : " + note); } String what(){ return "Instrument" ; } void adjust(){ System.out.println( "adjusting Instrument" ); } @Override public String toString() { // /添加一個toString方法,調用當前what方法, // 子類會自動繼承該方法并分別返回給自what()里的內容 return what(); } } class Wind extends Instrument{ @Override void play(Note note) { System.out.println( "Wind.play() : " + note); } @Override String what() { return "Wind" ; } @Override void adjust() { System.out.println( "adjusting Wind" ); } } class Percussion extends Instrument{ @Override void play(Note note) { System.out.println( "Percussion.play() : " + note); } @Override String what() { return "Percussion" ; } @Override void adjust() { System.out.println( "adjusting Percussion" ); } } class Stringed extends Instrument{ @Override void play(Note note) { System.out.println( "Stringed.play() : " + note); } @Override String what() { return "Stringed" ; } @Override void adjust() { System.out.println( "adjusting Stringed" ); } } class Brass extends Wind{ //繼承自Wind @Override void play(Note note) { System.out.println( "Brass.play() : " + note); } @Override void adjust() { System.out.println( "adjusting Brass" ); } } class Woodwing extends Wind{ @Override void play(Note note) { System.out.println( "Woodwing.play() : " + note); } @Override String what() { return "Woodwing" ; } } class newInstrument extends Instrument{ //新添加的類型 @Override void play(Note note) { System.out.println( "newIns.play()" + note); } } |
練習(9):創建Rodent(嚙齒動物):Mouse(老鼠),Gerbil(鼴鼠),Hamster(大頰鼠),等等這樣一個的繼承層次結構。在基類中,提供對所有的Rodent都通用的方法,在導出類中,根據特定的Rodent類型覆蓋這些方法,以便觀察它們執行不同的行為。創建一個Rodent數組,填充不同的Rodent類型,然后調用基類方法,觀察發生了什么情況。
這跟前面Instrument的例子相似,在Instrument中有what()這個對所有Instrument都通用的方法,而在每個子類中我們都覆蓋了這個方法并賦予了不同的行為,最終在main中創建了Instrument數組,調用了基類方法,最后得到的結果是不同類調用基類方法得到的輸出是該類重寫后的結果。不再重復。
練習(10):創建一個包含兩個方法的基類。在第一個方法中可以調用第二個方法。然后產生一個繼承自該基類的導出類,且覆蓋基類中的第二個方法。為該導出類創建一個對象,將它向上轉型為基類并調用第一個方法,解釋發生的情況。
1
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
|
public class Test10 { public static void main(String[] args){ jilei j = new daochulei(); //創建導出類的對象并轉型為基類 j.first(); //調用第一個方法 //結果輸出daochulei is running //原因,就像前面提過的,導出類繼承了基類的所有東西,沒有重寫的方法隱藏了起來 //其實在daochulei中還隱士的有void first()這個方法里調用了自身重寫的second() //當daochulei調用first()方法后,它就調用了自身重寫的second()方法。 //導出類調用基類方法其實不是真的調用,而是調用自身繼承自基類的方法, // 只不過這個方法沒重寫時,內部形式與基類相同 } } class jilei{ void first(){ //調用第二個方法 second(); } void second(){ System.out.println( "first is running" ); } } class daochulei extends jilei{ @Override void second() { System.out.println( "daochulei is running" ); } } |
練習(11)跳過
練習(12):修改練習(9),使其能夠演示基類和導出類的初始化順序。然后向基類和導出類中添加成員對象,并說明構建期間初始化發生的順序。
1
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
public class Test912 { public static void main(String[] args){ new Hamster(); } } class Rodent{ public Rodent(){ shanghai = 100 ; System.out.println( "Rodent" ); } private int shanghai; public void bite(){ System.out.println( "造成傷害" +shanghai + "點" ); } } class Mouse extends Rodent{ private int sh; public Mouse(){ sh = 1000 ; System.out.println( "Mouse" ); } @Override public void bite() { System.out.println( "造成傷害" +sh + "點" ); } } class Gerbil extends Mouse{ private int shang; public Gerbil(){ shang = 2000 ; System.out.println( "Gerbil" ); } @Override public void bite() { System.out.println( "造成傷害" +shang + "點" ); } } class Hamster extends Gerbil{ private Mouse mouse = new Mouse(); //成員對象 //該類初始化輸出結果 //Rodent // Mouse // Gerbil // Rodent // Mouse // Hamster //可以分析出,初始化時先調用基類的構造方法, // 然后初始化成員變量,因為其中有Mouse這個成員對象,所有對Mouse進行初始化, // 完成后再調用自身的構造方法 private int hai; public Hamster(){ hai = 3000 ; System.out.println( "Hamster" ); } @Override public void bite() { System.out.println( "造成傷害" + hai + "點" ); } } |
總結
以上就是本文關于Java編程—在測試中考慮多態的全部內容,希望對大家有所幫助。如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!
原文鏈接:https://www.2cto.com/kf/201609/546918.html