一、什麼是多態?
Polymorphism means “different forms.” In object-oriented programming, you have the same interface from the base class, and different forms using that interface: the different versions of the dynamically bound methods. (摘自thinking in java, Page217) polymorphism (also called dynamic bindingor late bindingor run-time binding)
例子:多態,子類-->父類 向上轉型
public class PolyTest
{
public static void main(String[] args)
{
//Parent parent = new Parent();
//parent.sing();
//Child child = new Child();
//child.sing();
Parent p = new Child(); //子類即父類
p.sing();
}
}
class Parent
{
public void sing()
{
System.out.println("parent is singing");
}
}
class Child extends Parent
{
//public void sing(int i) //重載
public void sing()
{
System.out.println("child is singing");
}
}
//Note:當申明父類型的引用去指向字類型的對象時,父類調用方法原則:第一,會去
//父類中查看是否有此方法,如果有繼續查找此方法是否有被子類繼承,如果繼承
//則繼續查找此方法是否覆寫,如果覆寫則調用覆寫的方法,如果沒有被覆寫則直接
//調用父類中的方法。第二,如果父類型的引用調用方法時此方法在父類中沒有定義,則直接會報錯,因爲什麼類型的引用就能調用什麼類型的方法。
//(與重載無關,如果子類重載了父類的方法,調用時任然是調用父類的方法,如果有
//被覆寫則調用覆寫的方法)
爲什麼要向上轉型?
a) 可以保持父類不需要修改,只需要子類繼承父類,然後覆寫父類的方法。
b)可以用父類型引用,接受子類型引用參數,這樣父類方法不需要改變,只需要增加子類即可。(詳見第二十講最後大約10分鐘)
public class PolyTest5
{
/*
public void runCar(BMW bmw) //不使用多態:子類添加,方法也必須添加。
{
bmw.run();
}
public void runCar(QQ qq) //傳的都是具體的車的類型。QQ不能接受BMW
{
qq.run();
}
*/
//使用多態:無論子類增加多少類,父類方法不需要修改
public void runCar(Car car) //可接受car類型和car的子類類型
{
car.run();
}
public static void main(String[] args)
{
/*
PolyTest5 test = new PolyTest5();
BMW bmw = new BMW();
test.runCar(bmw);
QQ qq = new QQ();
test.runCar(qq);
*/
PolyTest5 test = new PolyTest5();
Car car = new BMW(); //通過多態自動向上轉換實現
test.runCar(car);
QQ qq = new QQ();
test.runCar(qq); //子類引用傳遞給父類方法接受。向上類型轉換
}
}
class Car
{
public void run()
{
System.out.println("car is running");
}
}
class BMW extends Car
{
public void run()
{
System.out.println("BMW is running");
}
}
class QQ extends Car
{
public void run()
{
System.out.println("QQ is running");
}
}
//總結:多態屏蔽掉了子類之間的差異性,用一個公共父類方法標識我的接口(方法的參數)
//不管子類怎麼改變,只要增加的子類都繼承了這個父類,都可以作爲參數傳遞到我的這個
//方法裏。
多態的晚綁定:
//驗證晚綁定
public class PolyTest4
{
public static void main(String[] args)
{
A a = null;
if(args[0].equals("1"))
{
a = new B();
}
else if(args[0].equals("2"))
{
a = new C();
}
else if(args[0].equals("3"))
{
a = new D();
}
a.method();
}
}
class A
{
public void method()
{
System.out.println("A");
}
}
class B extends A
{
public void method()
{
System.out.println("B");
}
}
class C extends A
{
public void method()
{
System.out.println("C");
}
}
class D extends A
{
public void method()
{
System.out.println("D");
}
}
向下轉型:
例子:多態,父類-->子類, 向下轉型
public class PolyTest2
{
public static void main(String[] args)
{
/*
Animal animal = new Cat();
Animal animal2 = new Animal();
animal2 = animal;
animal2.sing();
*/
/*
Animal animal = new Cat();
Animal animal2 = new Animal();
animal = animal2;
animal.sing();
*/
/*
Cat cat = new Cat();
Animal animal = cat;
animal.sing();
*/
Animal animal = new Animal();
Cat cat =new Animal();
/* //向上類型轉換
Cat cat = new Cat();
Animal animal = cat;
animal.sing();
//向下類型轉換
Animal a = new Cat();
Cat c = (Cat)a; //向下轉換的原則是:a實際指向的是什麼,就可以把它轉換成什麼類型的引用
c.sing();
*/
}
}
class Animal
{
public void sing()
{
System.out.println("animal is singing");
}
}
class Dog extends Animal
{
public void sing()
{
System.out.println("dog is singing");
}
}
class Cat extends Animal
{
public void sing()
{
System.out.println("cat is singing");
}
}
爲什麼要使用向下轉換?
例子:
public class PolyTest3
{
public static void main(String[] args)
{
//Fruit f = new Pear();
//f.run();
//Pear p = (Pear)f;
//p.run();
//Fruit f = new Pear();
//f.grow();
Fruit f = new Pear();
Pear p = (Pear)f;
p.grow();
}
}
class Fruit
{
public void run()
{
System.out.println("fruit is running");
}
}
class Pear extends Fruit
{
public void run()
{
System.out.println("pear is running");
}
public void grow()
{
System.out.println("pear is growing");
}
}
總結:子類裏面新增了自己的方法時,父類引用需要調用子類方法此時就需要向下轉型。將父類引用類型轉型爲子類引用類型,因爲什麼類型的引用就能指向什麼類型的對象。
1. JAVA中面向對象主要有兩種提現:
(1) 方法的重載與覆寫。
(2) 對象的多態性。
對象的多態性主要分爲以下兩種類型:
(1) 向上轉型:子類對象——>父類對象。
(2) 向下轉型: 父類對象——>子類對象。
對於向上轉型,程序會自動完成,而對於向下轉型時,必須明確地指明要轉型的子類類型。
【格式 對象轉型】
對象向上轉型: 父類 父類對象 = 子類實例;
對象向下轉型: 子類 子類對象 = (子類)父類實例;
範例:對象的向上轉型
//對象的向上轉型
class A{ //定義類A
public void fun1(){
System.out.println("A --> public void fun1(){}");
}
public void fun2(){
this.fun1();
}
};
class B extends A{ //子類通過extends繼承父類
public void fun1(){ //覆寫父類中的fun1()方法
System.out.println("B --> public void fun1(){}");
}
public void fun3(){ //子類自己定義的方法
System.out.println("B --> public void fun3()");
}
};
public class PolDemo01{
public static void main(String []args){
B b = new B(); //定義子類實例化對象
A a = b; //發生了向上轉型的關係,子類 --> 父類
a.fun1(); //此方法被子類覆寫過
}
}
//總結:如果對象發生了向上轉型關係後,所調用的方法一定是被子類覆寫過的方法。
程序運行結果:
B --> public void fun1(){}
此時的對象a是無法調用B類中的fun3()方法的,因爲此方法只在子類定義,而沒有在父類中定義,如果想要調用子類自己的方法,則肯定要使用子類實例,所以此時可以將對象進行向下轉型。
//對象的向下轉型
class A{ //定義類A
public void fun1(){
System.out.println("A --> public void fun1(){}");
}
public void fun2(){
this.fun1();
}
};
class B extends A{ //子類通過extends繼承父類
public void fun1(){ //覆寫父類中的fun1()方法
System.out.println("B --> public void fun1(){}");
}
public void fun3(){ //子類自己定義的方法
System.out.println("B --> public void fun3(){}");
}
};
public class PolDemo02{
public static void main(String []args){
A a = new B(); //發生了向上轉型的關係,子類-->父類
B b = (B)a; //此時發生了向下轉型關係
b.fun1(); //調用方法被覆寫的方法
b.fun2(); //調用父類的方法
b.fun3(); //調用子類自己定義的方法
}
}
程序運行結果:
B --> public void fun1(){}
B --> public void fun1(){}
B --> public void fun3(){}
注意: 對象向下轉型的要求
在進行對象的向下轉型前,必須首先發生對象向上轉型,否則將出現對象轉換異常
//錯誤的轉型
class A{ //定義類A
public void fun1(){
System.out.println("A --> public void fun1(){}");
}
public void fun2(){
this.fun1();
}
};
class B extends A{ //子類通過extends繼承父類
public void fun1(){ //覆寫父類中的fun1()方法
System.out.println("B --> public void fun1(){}");
}
public void fun3(){ //子類自己定義的方法
System.out.println("B --> public void fun3(){}");
}
};
public class PolDemo03{
public static void main(String []args){
A a = new A(); //此時聲明的是父類對象。父對象並不知道誰是子對象
B b = (B)a; //此時發生了向下轉型關係
b.fun1(); //調用方法被覆寫的方法
b.fun2(); //調用父類的方法
b.fun3(); //調用子類自己定義的方法
}
}
由以上程序可以發現,此時的A類對象是由A類本身進行實例化的,然後將A類的實例化對象強制轉換爲子對象,這樣寫在語法上是沒有任何錯誤的,但是在運行時出現了異常,因爲此時父類用其本身類實例化自己的對象,但它並不知道誰是自己的子類,那肯定在轉換時會出現錯誤。只需要將連個對象建立好關係即可解決此我呢提,在聲明父類對象時發生向上轉型關係“A a = new B();”,這時相當於是由子類去實例化父類對象,也就是說這時父類知道有這麼一個子類,所以下面在進行轉換時就不會再有問題。2. instanceof 關鍵字:判斷某個對象是否是某個類的實例。
語法形式: 引用名 instanceof 類名(接口名),返回 boolean 值。
//Instanceof 關鍵字作用
public class InstanceofTest{
public static void main(String args[]){
People people = new Man();
System.out.println(people instanceof People);
//因爲 Man 是 People 的子類,根據繼承,子類就是父類,
//因此 Man 也可以看作是 People 的實例。
}
}
class People{};
class Man extends People{};
//使用instanceof判斷一個對象屬於那個類的實例
class A{ //定義類A
public void fun1(){
System.out.println("A --> public void fun1(){}");
}
public void fun2(){
this.fun1();
}
};
class B extends A{ //子類通過extends繼承父類
public void fun1(){ //覆寫父類中的fun1()方法
System.out.println("B --> public void fun1(){}");
}
public void fun3(){ //子類自己定義的方法
System.out.println("B --> public void fun3(){}");
}
};
public class InstanceofDemo01{
public static void main(String []args){
A a1 = new B(); //通過向上轉型實例化A類對象
System.out.println("A a1 = new B():" + (a1 instanceof A));
System.out.println("A a1 = new B():" + (a1 instanceof B));
A a2 = new A(); //通過A類的構造實例化本類對象
System.out.println("A a2 = new A():" + (a2 instanceof A));
System.out.println("A a2 = new A():" + (a2 instanceof B));
}
}
//通過子類實例化的對象同時是子類和父類的實例
程序運行結果:
A a1 = new B():true
A a1 = new B():true
A a2 = new A():true
A a2 = new A():false
//在向下轉型前進行驗證
class A{ //定義類A
public void fun1(){
System.out.println("A --> public void fun1(){}");
}
public void fun2(){
this.fun1();
}
};
class B extends A{ //子類通過extends繼承父類
public void fun1(){ //覆寫父類中的fun1()方法
System.out.println("B --> public void fun1(){}");
}
public void fun3(){ //子類自己定義的方法
System.out.println("B --> public void fun3(){}");
}
};
class C extends A{ //子類通過extends繼承父類
public void fun1(){ //覆寫父類中的fun1()方法
System.out.println("C --> public void fun1(){}");
}
public void fun5(){ //子類自己定義的方法
System.out.println("C --> public void fun3(){}");
}
};
public class InstanceofDemo02{
public static void main(String []args){
fun(new B()); //傳遞B類實例,產生向上轉型
fun(new C()); //傳遞C類實例,產生向上轉型
}
public static void fun(A a){ //此方法可以分別調用各自子類單獨定義的方法
a.fun1();
if(a instanceof B){ //判斷是否是B類實例
B b = (B)a; //進行向下轉型
b.fun3(); //調用子類自己定義的方法
}
if(a instanceof C){ //判斷是否是C類實例
C c = (C)a; //進行向下轉型
c.fun5(); //調用子類自己定義的方法
}
}
}
程序運行結果:
B --> public void fun1(){}
B --> public void fun3(){}
C --> public void fun1(){}
C --> public void fun3(){}