Java編程—在測(cè)試中考慮多態(tài)
面向?qū)ο缶幊逃腥筇匦裕悍庋b、繼承、多態(tài)。
封裝隱藏了類(lèi)的內(nèi)部實(shí)現(xiàn)機(jī)制,可以在不影響使用的情況下改變類(lèi)的內(nèi)部結(jié)構(gòu),同時(shí)也保護(hù)了數(shù)據(jù)。對(duì)外界而已它的內(nèi)部細(xì)節(jié)是隱藏的,暴露給外界的只是它的訪問(wèn)方法。
繼承是為了重用父類(lèi)代碼。兩個(gè)類(lèi)若存在IS-A的關(guān)系就可以使用繼承。,同時(shí)繼承也為實(shí)現(xiàn)多態(tài)做了鋪墊。那么什么是多態(tài)呢?多態(tài)的實(shí)現(xiàn)機(jī)制又是什么?請(qǐng)看我一一為你揭開(kāi):
所謂多態(tài)就是指程序中定義的引用變量所指向的具體類(lèi)型和通過(guò)該引用變量發(fā)出的方法調(diào)用在編程時(shí)并不確定,而是在程序運(yùn)行期間才確定,即一個(gè)引用變量倒底會(huì)指向哪個(gè)類(lèi)的實(shí)例對(duì)象,該引用變量發(fā)出的方法調(diào)用到底是哪個(gè)類(lèi)中實(shí)現(xiàn)的方法,必須在由程序運(yùn)行期間才能決定。因?yàn)樵诔绦蜻\(yùn)行時(shí)才確定具體的類(lèi),這樣,不用修改源程序代碼,就可以讓引用變量綁定到各種不同的類(lèi)實(shí)現(xiàn)上,從而導(dǎo)致該引用調(diào)用的具體方法隨之改變,即不修改程序代碼就可以改變程序運(yùn)行時(shí)所綁定的具體代碼,讓程序可以選擇多個(gè)運(yùn)行狀態(tài),這就是多態(tài)性。練習(xí)(1):創(chuàng)建一個(gè)Cycle類(lèi),它具有子類(lèi)Unicycle,Bycycle,Tricycle.演示每一個(gè)類(lèi)型的實(shí)例都可以經(jīng)由ride()方法向上轉(zhuǎn)型為Cycle.
向上轉(zhuǎn)型就是允許將多種從同一基類(lèi)的導(dǎo)出類(lèi)看成同一類(lèi)型。
多態(tài)方法調(diào)用就是允許一種類(lèi)型表現(xiàn)出與其他相似類(lèi)型之間的區(qū)別,只要他們是從同一基類(lèi)導(dǎo)出而來(lái)的。這種區(qū)別由各個(gè)導(dǎo)出類(lèi)型方法的具體不同實(shí)現(xiàn)而表現(xiàn)出來(lái)的,雖然這些方法都是由基類(lèi)調(diào)用的。
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
在以上示例中,三種子類(lèi)能被視作Cycle傳入到方法ride()中就是向上轉(zhuǎn)型。
但向上轉(zhuǎn)型只是看成,而不是強(qiáng)制轉(zhuǎn)換,所以最后方法調(diào)用的結(jié)果不同,這就是多態(tài)。
多態(tài)又稱(chēng)之為動(dòng)態(tài)綁定。什么是動(dòng)態(tài)綁定?
與動(dòng)態(tài)綁定相反的是靜態(tài)綁定。c語(yǔ)言所有方法都是默認(rèn)靜態(tài)綁定。靜態(tài)綁定也稱(chēng)為前期綁定,就是在程序運(yùn)行前就綁定完成。也就是說(shuō),代碼寫(xiě)了什么樣,就是什么樣。
而動(dòng)態(tài)綁定是直到運(yùn)行時(shí)才去決定該方法調(diào)用該綁定哪個(gè)實(shí)體。
java中的所有static和final方法都是靜態(tài)綁定,其他的所有方法都是動(dòng)態(tài)綁定。
練習(xí)(2):在幾何圖形示例中添加@Override注解。
練習(xí)(3):在基類(lèi)Shape.java中添加一個(gè)新方法,用于打印一條消息,但導(dǎo)出類(lèi)中不要覆蓋這個(gè)方法。請(qǐng)解釋發(fā)生了什么?,F(xiàn)在,在其中一個(gè)導(dǎo)出類(lèi)中覆蓋該方法,而在其他的導(dǎo)出類(lèi)中不予覆蓋,觀察又有什么發(fā)生。最后,在所有的導(dǎo)出類(lèi)中覆蓋這個(gè)方法。
練習(xí)(4):向Shapes.java中添加一個(gè)新的Shape類(lèi)型,并在main()方法中驗(yàn)證:多態(tài)對(duì)新類(lèi)型的作用是否與在舊類(lèi)型中的一樣。
以上三個(gè)練習(xí)在一份示例中完成。
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();//每個(gè)子類(lèi)都調(diào)用了一次添加的新方法,
// 因?yàn)樽宇?lèi)繼承父類(lèi)自然把所有方法都繼承過(guò)去了,只不過(guò)沒(méi)有顯示出來(lái),
// 其實(shí)是隱式的存在的,子類(lèi)調(diào)用的其實(shí)是繼承自父類(lèi)的沒(méi)有重寫(xiě)的方法,
// 看起來(lái)像是調(diào)用了父類(lèi)的方法
}
Shape s = new Recf();//這里是聲明了一個(gè)Shape類(lèi)型的引用,但實(shí)際的對(duì)象還是Recf.
s.draw();//輸出的是Recf類(lèi)重寫(xiě)的方法,證明多態(tài)對(duì)新類(lèi)的作用于在舊類(lèi)中是一樣的
}
}
class Shape{//基類(lèi)
public void draw(){}
public void erase(){}
public void newMethod(){
System.out.println("new method");//添加的新方法
}
}
class Circle extends Shape{
@Override//添加注解,一般IDE可以自動(dòng)添加
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");
//重寫(xiě)后該類(lèi)輸出內(nèi)容就發(fā)生改變,沒(méi)有重寫(xiě)時(shí)該類(lèi)的該方法與父類(lèi)運(yùn)行結(jié)果相同
//無(wú)論重寫(xiě)與否,其實(shí)調(diào)用的都是自身的方法
//只是沒(méi)有重寫(xiě)時(shí)方法調(diào)用結(jié)果與父類(lèi)的相同
}
}
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{//是一種工廠,用以隨機(jī)獲取一種Shape的子類(lèi)
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();
}
}
}
練習(xí)(5):以練習(xí)1為基礎(chǔ),才Cycle中添加wheels()方法,它將返回輪子的數(shù)量。修改ride()方法,讓它調(diào)用wheels()方法,并驗(yàn)證多態(tài)起作用了。
在練習(xí)(1)的代碼中給基類(lèi)添加
public void wheels(){
System.out.println("輪子數(shù)量是" + num);
}
然后在main中:
unicycle.wheels(); bicycle.wheels(); tricycle.wheels();
最后輸出結(jié)果都順利輸出了方法中的語(yǔ)句,證明多態(tài)確實(shí)起作用了。
練習(xí)(6):修改Music3.java,是what()方法成為根Object的toString()方法。試用System.out.pringtln()方法打印Instrument對(duì)象(不用向上轉(zhuǎn)型).
練習(xí)(7):向Music3.java添加一個(gè)新的類(lèi)型Instrument,并驗(yàn)證多態(tài)性是否作用于所添加的新類(lèi)型.
練習(xí)(8):修改Music3.java,使其可以向Shapes.java中的方式那樣隨機(jī)創(chuàng)建Instrument對(duì)象。
三個(gè)練習(xí)將在一份代碼完成。
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);//驗(yàn)證多態(tài)性是否適用于所添加的新類(lèi)型,答案是確實(shí)適用。
}
public static void tune(Instrument instrument){
instrument.play(Note.MIDDLE_C);//無(wú)論傳進(jìn)聲明子類(lèi),都播放MIDDLE_C
}
public static void tuneAll(Instrument[] e){
for (Instrument i :
e) {
tune(i);
System.out.println(i.toString());
}
}
}
class RandomInsFactory{//工廠類(lèi),用于隨機(jī)生成一個(gè)Instrument的子類(lèi)
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{//枚舉類(lèi),存放了哪些音樂(lè)
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() {
// /添加一個(gè)toString方法,調(diào)用當(dāng)前what方法,
// 子類(lèi)會(huì)自動(dòng)繼承該方法并分別返回給自what()里的內(nèi)容
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{//新添加的類(lèi)型
@Override
void play(Note note) {
System.out.println("newIns.play()" + note);
}
}
練習(xí)(9):創(chuàng)建Rodent(嚙齒動(dòng)物):Mouse(老鼠),Gerbil(鼴鼠),Hamster(大頰鼠),等等這樣一個(gè)的繼承層次結(jié)構(gòu)。在基類(lèi)中,提供對(duì)所有的Rodent都通用的方法,在導(dǎo)出類(lèi)中,根據(jù)特定的Rodent類(lèi)型覆蓋這些方法,以便觀察它們執(zhí)行不同的行為。創(chuàng)建一個(gè)Rodent數(shù)組,填充不同的Rodent類(lèi)型,然后調(diào)用基類(lèi)方法,觀察發(fā)生了什么情況。
這跟前面Instrument的例子相似,在Instrument中有what()這個(gè)對(duì)所有Instrument都通用的方法,而在每個(gè)子類(lèi)中我們都覆蓋了這個(gè)方法并賦予了不同的行為,最終在main中創(chuàng)建了Instrument數(shù)組,調(diào)用了基類(lèi)方法,最后得到的結(jié)果是不同類(lèi)調(diào)用基類(lèi)方法得到的輸出是該類(lèi)重寫(xiě)后的結(jié)果。不再重復(fù)。
練習(xí)(10):創(chuàng)建一個(gè)包含兩個(gè)方法的基類(lèi)。在第一個(gè)方法中可以調(diào)用第二個(gè)方法。然后產(chǎn)生一個(gè)繼承自該基類(lèi)的導(dǎo)出類(lèi),且覆蓋基類(lèi)中的第二個(gè)方法。為該導(dǎo)出類(lèi)創(chuàng)建一個(gè)對(duì)象,將它向上轉(zhuǎn)型為基類(lèi)并調(diào)用第一個(gè)方法,解釋發(fā)生的情況。
public class Test10 {
public static void main(String[] args){
jilei j = new daochulei();//創(chuàng)建導(dǎo)出類(lèi)的對(duì)象并轉(zhuǎn)型為基類(lèi)
j.first();//調(diào)用第一個(gè)方法
//結(jié)果輸出daochulei is running
//原因,就像前面提過(guò)的,導(dǎo)出類(lèi)繼承了基類(lèi)的所有東西,沒(méi)有重寫(xiě)的方法隱藏了起來(lái)
//其實(shí)在daochulei中還隱士的有void first()這個(gè)方法里調(diào)用了自身重寫(xiě)的second()
//當(dāng)daochulei調(diào)用first()方法后,它就調(diào)用了自身重寫(xiě)的second()方法。
//導(dǎo)出類(lèi)調(diào)用基類(lèi)方法其實(shí)不是真的調(diào)用,而是調(diào)用自身繼承自基類(lèi)的方法,
// 只不過(guò)這個(gè)方法沒(méi)重寫(xiě)時(shí),內(nèi)部形式與基類(lèi)相同
}
}
class jilei{
void first(){//調(diào)用第二個(gè)方法
second();
}
void second(){
System.out.println("first is running");
}
}
class daochulei extends jilei{
@Override
void second() {
System.out.println("daochulei is running");
}
}
練習(xí)(11)跳過(guò)
練習(xí)(12):修改練習(xí)(9),使其能夠演示基類(lèi)和導(dǎo)出類(lèi)的初始化順序。然后向基類(lèi)和導(dǎo)出類(lèi)中添加成員對(duì)象,并說(shuō)明構(gòu)建期間初始化發(fā)生的順序。
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 + "點(diǎn)" );
}
}
class Mouse extends Rodent{
private int sh;
public Mouse(){
sh = 1000;
System.out.println("Mouse");
}
@Override
public void bite() {
System.out.println("造成傷害" +sh + "點(diǎn)" );
}
}
class Gerbil extends Mouse{
private int shang;
public Gerbil(){
shang = 2000;
System.out.println("Gerbil");
}
@Override
public void bite() {
System.out.println("造成傷害" +shang + "點(diǎn)" );
}
}
class Hamster extends Gerbil{
private Mouse mouse = new Mouse();//成員對(duì)象
//該類(lèi)初始化輸出結(jié)果
//Rodent
// Mouse
// Gerbil
// Rodent
// Mouse
// Hamster
//可以分析出,初始化時(shí)先調(diào)用基類(lèi)的構(gòu)造方法,
// 然后初始化成員變量,因?yàn)槠渲杏蠱ouse這個(gè)成員對(duì)象,所有對(duì)Mouse進(jìn)行初始化,
// 完成后再調(diào)用自身的構(gòu)造方法
private int hai;
public Hamster(){
hai = 3000;
System.out.println("Hamster");
}
@Override
public void bite() {
System.out.println("造成傷害" + hai + "點(diǎn)" );
}
}
總結(jié)
以上就是本文關(guān)于Java編程—在測(cè)試中考慮多態(tài)的全部?jī)?nèi)容,希望對(duì)大家有所幫助。歡迎參閱:Java面向?qū)ο缶幊蹋ǚ庋b/繼承/多態(tài))實(shí)例解析、Java實(shí)現(xiàn)微信公眾平臺(tái)朋友圈分享功能詳細(xì)代碼、Java編程BigDecimal用法實(shí)例分享等,如有不足之處,歡迎留言指出。感謝朋友們對(duì)本站的支持!
相關(guān)文章
深入淺析SPI機(jī)制在JDK與Spring?Boot中的應(yīng)用
SPI是一種使軟件框架或庫(kù)更加模塊化、可擴(kuò)展和可維護(hù)的有效方法。通過(guò)遵循“開(kāi)閉原則”,?SPI?確保了系統(tǒng)的穩(wěn)定性和靈活性,從而滿(mǎn)足了不斷變化的業(yè)務(wù)需求,這篇文章主要介紹了SPI機(jī)制在JDK與Spring?Boot中的應(yīng)用,需要的朋友可以參考下2023-09-09
使用@Valid+BindingResult進(jìn)行controller參數(shù)校驗(yàn)方式
這篇文章主要介紹了使用@Valid+BindingResult進(jìn)行controller參數(shù)校驗(yàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
SpringBoot整合flyway實(shí)現(xiàn)自動(dòng)創(chuàng)建表的方法
這篇文章主要介紹了SpringBoot整合flyway實(shí)現(xiàn)自動(dòng)創(chuàng)建表的方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03
mybatis嵌套循環(huán)map方式(高級(jí)用法)
這篇文章主要介紹了mybatis嵌套循環(huán)map方式(高級(jí)用法),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
詳解mybatis 批量更新數(shù)據(jù)兩種方法效率對(duì)比
這篇文章主要介紹了詳解mybatis 批量更新數(shù)據(jù)兩種方法效率對(duì)比,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02

