轉:多型,向上向下造型(https://blog.csdn.net/lingang1991/article/details/69905944)
一、多型是繼封裝、繼承之後,面向物件的第三大特性。現實事物經常會體現出多種形態,如學生,學生是人的一種,則一個具體的同學張三既是學生也是人,即出現兩種形態。 Java作為面向物件的語言,同樣可以描述一個事物的多種形態。如Student類繼承了Person類,一個Student的物件便既是Student,又是Person。一個Student物件既可以賦值給一個Student型別的引用,也可以賦值給一個Person型別的引用。 最終多型體現為父類引用變數可以指向子類物件:父類型別 變數名 = new 子類型別(); 1、多型的前提是必須有子父類關係或者類實現介面關係,否則無法完成多型。 2、在使用多型後的父類引用變數呼叫方法時,會呼叫子類重寫後的方法。 二、多型的三種形式: 1、 普通類多型定義的格式 父類 變數名 = new 子類();
class Fu {}
class Zi extends Fu {}
//類的多型使用
Fu f = new Zi();
2、 抽象類多型定義的格式
abstract class Fu {
public abstract void method();
}
class Zi extends Fu {
public void method(){
System.out.println(“重寫父類抽象方法”);
}
}
//類的多型使用
Fu fu= new Zi();
3、介面多型定義的格式
interface Fu { public abstract void method(); } class Zi implements Fu { public void method(){ System.out.println(“重寫介面抽象方法”); } } //介面的多型使用 Fu fu = new Zi();
注意:同一個父類的方法會被不同的子類重寫。在呼叫方法時,呼叫的為各個子類重寫後的方法。
Person p1 = new Student();
Person p2 = new Teacher();
p1.work(); //p1會呼叫Student類中重寫的work方法
p2.work(); //p2會呼叫Teacher類中重寫的work方法
三、掌握了多型的基本使用後,那麼多型出現後類的成員有啥變化呢?前面學習繼承時,我們知道子父類之間成員變數有了自己的特定變化,那麼當多型出現後,成員變數在使用上有沒有變化呢? 多型出現後會導致子父類中的成員變數有微弱的變化。看如下程式碼:
class Fu {
int num = 4;//沒有這句會編譯失敗
}
class Zi extends Fu {
int num = 5;
}
class Demo {
public static void main(String[] args) {
Fu f = new Zi();
System.out.println(f.num);
Zi z = new Zi();
System.out.println(z.num);
}
}
列印結果:4 5 總結:當子父類中出現同名的成員變數時,多型呼叫該變數時: 1、編譯時期:參考的是引用型變數所屬的類中是否有被呼叫的成員變數。沒有,編譯失敗。 2、執行時期:也是呼叫引用型變數所屬的類中的成員變數。 簡單記:編譯和執行都參考等號的左邊。編譯執行看左邊。
多型出現後會導致子父類中的成員方法有微弱的變化,看程式碼:
class Fu {
int num = 4;
//沒有這個方法,編譯失敗
void show() {
System.out.println("Fu show num");
}
}
class Zi extends Fu {
int num = 5;
//重寫父類方法
void show() {
System.out.println("Zi show num");
}
void show_1{
System.out.println("Zi show show_1");
}
}
class Demo {
public static void main(String[] args) {
Fu f = new Zi();
f.show();
//f.show_1();
}
}
列印結果:Zi show num 總結:多型成員方法 1、編譯時期:參考引用變數所屬的類,如果沒有類中沒有呼叫的方法,編譯失敗(如果把f.show_1()前面的註釋開啟,則編譯失敗)。 2、執行時期:參考引用變數所指的物件所屬的類,並執行物件所屬類中的成員方法(如果把子類重寫的show()方法註釋掉,那麼列印的結果是Fu show num)。 簡而言之:編譯看左邊,執行看右邊。
四、多型的轉型分為向上轉型與向下轉型兩種
1、向上轉型:當有子類物件賦值給一個父類引用時,便是向上轉型,多型本身就是向上轉型的過程。
使用格式:父類型別 變數名 = new 子類型別();
如:Person p = new Student();
2、向下轉型:一個已經向上轉型的子類物件可以使用強制型別轉換的格式,將父類引用轉為子類引用,這個過程是向下轉型。如果是直接建立父類物件,是無法向下轉型的!
使用格式:
子類型別 變數名 = (子類型別) 父類型別的變數;
如:Person p = new Student();
Student stu = (Student) p
五、多型的好處與弊端
當父類的引用指向子類物件時,就發生了向上轉型,即把子類型別物件轉成了父類型別。向上轉型的好處是隱藏了子類型別,提高了程式碼的擴充套件性。但向上轉型也有弊端,只能使用父類共性的內容,而無法使用子類特有功能,功能有限制。看如下程式碼:
//描述動物類,並抽取共性eat方法
abstract class Animal {
abstract void eat();
}
// 描述狗類,繼承動物類,重寫eat方法,增加lookHome方法
class Dog extends Animal {
void eat() {
System.out.println("啃骨頭");
}
void lookHome() {
System.out.println("看家");
}
}
// 描述貓類,繼承動物類,重寫eat方法,增加catchMouse方法
class Cat extends Animal {
void eat() {
System.out.println("吃魚");
}
void catchMouse() {
System.out.println("抓老鼠");
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Dog(); //多型形式,建立一個狗物件
a.eat(); // 呼叫物件中的方法,會執行狗類中的eat方法
// a.lookHome();//使用Dog類特有的方法,需要向下轉型,不能直接使用
// 為了使用狗類的lookHome方法,需要向下轉型
// 向下轉型過程中,可能會發生型別轉換的錯誤,即ClassCastException異常
// 那麼,在轉之前需要做健壯性判斷
if( !a instanceof Dog){ // 判斷當前物件是否是Dog型別
System.out.println("型別不匹配,不能轉換");
return;
}
Dog d = (Dog) a; //向下轉型
d.lookHome();//呼叫狗類的lookHome方法
}
}
我們來總結一下: 1、什麼時候使用向上轉型: 當不需要面對子類型別時,通過提高擴充套件性,或者使用父類的功能就能完成相應的操作,這時就可以使用向上轉型。 如:
Animal a = new Dog();
a.eat();
2、什麼時候使用向下轉型 當要使用子類特有功能時,就需要使用向下轉型。 如:
Dog d = (Dog) a; //向下轉型
d.lookHome();//呼叫狗類的lookHome方法
3、向下轉型的好處:可以使用子類特有功能。
4、向下轉型的弊端:需要面對具體的子類物件;在向下轉型時容易發生ClassCastException型別轉換異常。在轉換之前必須做型別判斷。
如:if( !a instanceof Dog){…}