面向对象特征——多态





多态:Polymorphism

简单的理解就是:多种形态的意思,即某一事物的多种存在形态。
比如:函数的重载,即为多态性。

看下面的例子:

class Fruit

{}

class Apple extends Fruit

{}

class Pear extends Fruit

{}

以上ApplePear都继承于Fruit
然后:


Apple apple = new Apple();

Fruit apple = new Apple();    //一个对象,两种形态。

在这里apple不仅具备Apple的属性,还具备Fruit的属性,
因此创建两个对象都是正确的,只不过第一个是使用本类创建的本类对象,另一个使用父类创建的子类对象。
这就是Java中对象的多态性。
简单说:就是一个对象对应着不同类型。
多态在代码中的体现:父类或者接口的引用指向其子类的对象。

多态的好处:
提高了代码的扩展性,前期定义的代码可以使用后期的内容。
看下面的例子:

anstract class Animal //抽象类

{

        abstractvoid eat();  //抽象方法

}       

class Dog entends Animal

{

        voideat()

        {

              System.out.println("啃骨头");

        }

        voidHouseKeeping()

        {

              System.out.println("看家");

        }

}

class Cat extends Animal

{

        voideat()

        {

              System.out.println("吃鱼");

        }

        voidCatchMouse()

        {

              System.out.println("捉老鼠");

        }

}

class PolymorphismDemo

{

        publicstatic void main(String[] args)

        {

              Dogd = new Dog();

              d.eat();

              Catc = new Cat();

              c.eat();

        }

}

以往,我们可能会这样规划我们的程序:让对象各自去调用自己的方法,实现所需的功能。
也可以这样:

public static void main(String[]args)

{

        Dogd = new Dog();

        d.method();

        Catc = new Cat();

        c.method();

}

public static void method(Dog d)

{

        d.eat();

} 

public static void method(Cat c)

{

        c.eat();

}

像这样,定义新的方法,更直观的调用重载函数也可以实现程序所需。
但是以上的方法都过于繁杂,代码的复用性很差,不利于扩展。
因此,可以利用多态的性质,这样做:

public static void main(String[]args)

{

        method(newDog());

        method(newCat());

}

public static void method(Annimala) //Animal a = new Dog();

{

        a.eat();

}

上边的代码中:method()方法中参数是Animal类型,主函数调用该方法时,传入的却是Dog/Cat类型的参数。
这就是利用多态的性质实现的代码的可扩展。
加入以后需要加入新的类型,例如:

class Cock extends Animal

{

        publicvoid eat()

        {

              System.out.println("芝麻");

        }

        voidcrow()

        {

              System.out.println("打鸣");

        }

}

这里新添加了一个类,但是需要实例化并调用该类方法时,并不需要重写其方法:
可以:

         method(newCock());
即完成新类型事物的功能实现。

 

多态的弊端:
 
前期定义的内容不能使用(调用)后期子类的特有内容。
多态虽然有利于程序的扩展性,但是也有局限,比如:

 public static void method(Animal a)

 {

        a.catchMouse();

 }

在主函数中调用该方法时就会出错,因为在Animal类中没有catchMouse方法的定义。

 因此,使用多态时应注意多态的前提:
 1
,必须要有关系:继承,实现。
 2
,要有覆盖(子类中的特有功能覆盖父类方法)

 

由于多态的局限性,在使用多态时,前期定义的内容不能使用后期子类的特有内容,因此需要对相应的对象进行形式转换。
即,转型。

简单的类型转换有:
 byte b = 3;
 int i = b; //
b强制转换成int类型。

因此,在这里:
 Animal a = new Cat(); //
实际上是类型的自动提升,Cat对象提升成了Animal类型,但是该对象不能访问Cat类型的特有功能。
    //
其作用就是限制对特有功能的访问。
如果想访问Cat类型的特有功能,需要将该对象进行向下转型:
 Cat c = (Cat)a; //
向下转型是为了使用子类中的特有功能。
 c.eat();
 c.catchMouse(); //
此时调用该方法不会出现错误。
但是需要注意,类型转换只是同一个子类对象在做变化。
如果:
Animal a = new Animal();
Cat c = (Cat)a; //ClassCastException
此时会提示类型转换错误。

虽然这样,但是在转换的时候还是容易出现问题:

 public static void method(Animal a)

 {

        a.eat();

        //a.catchMouse();     //我们已经知道这样是行不通的

 

        //可以做一个类型转换

        Catc = (Cat)a;//向下转型,将Animal类型的a对象转换成Cat类型

        c.catchMouse();//调用子类方法

 }

 使用的时候:
 method(new Cat());
这样是没有问题的,但是如果传入的是:
 method(new Dog());
这时就出现类型转换的错误提示了。

想要解决上边的问题也不太难,可以使用类型判断(instanceof:用于判断对象的具体类型,只能用于引用数据类型)来完成:

 public static void method(Animal a)

 {

        a.eat();      //每种动物都有该方法,所以这个可以直接调用

 

        if(ainstanceof Cat)  //判断对象a是否为Cat类型

                              //instanceof通常用于在向下转型前健壮性的判断

        {

              Catc = (Cat)a;

              c.catchMouse();

        }

        elseif(a instanceof Dog)

        {

              Dogd = (Dog)a;

              d.houseKeeping();

        }

 }

 

 这样,有了以上的判断,method()方法不管传入的是何种参数,均可以。

 

多态时,成员的特点:
1
,成员变量(非静态)
编译时:参考引用型变量所属的类中的是否有调用的成员变量,有,编译通过,没有编译失败。
运行时:参考引用型变量所属的类中的是否有调用的成员变量,并运行该所属类中的成员变量。
简单说,编译和运行都参考等号的左边(=)
例如:

class Fu

{

        intnum = 4;

}

class Zi extends Fu

{

        intnum = 5;

}

主函数中:

        Fuf = new Zi();

        System.out.println(f.num);    //结果是4

如果在Fu类中没有定义num变量,则编译失败,因为f对象是Fu类型引用。
如果在Zi类中没有定义num变量,编译通过,输出num=4

2,成员函数
编译时:参考引用型变量所属的类中的是否有调用的函数,有,编译通过,没有编译失败。
运行时:参考对象所属的类中是否有调用的函数。
简单说,编译看左边(=),运行看右边(=)
例如:

class Fu

{

        voidshow()

        {

              System.out.println("fushow");

        }

}

class Zi extends Fu

{

        voidshow()

        {

              System.out.println("zishow");

        }

}

主函数中:

        Fuf = new Zi();

        f.show();     //zi show

 

如果父类中没有定义show()方法,则编译失败。因为fFu类的引用,当f找不到父类中的show()方法时,编译不能通过。
如果子类中没有定义show()方法,编译通过,因为编译时f首先在子类中搜索show()方法,如果没有则转向父类。

3,静态函数
编译时:参考引用型变量所属的类中的是否有调用的静态函数。
运行时:参考引用型变量所属的类中的是否有调用的静态函数。
简单说,编译和运行都看左边(=)
其实对于静态方法,是不需要对象的,直接使用类名调用即可。
例如:

class Fu

{

        staticvoid method()

        {

              System.out.println("fustatic method");

        }

}

class Zi extends Fu

{

        staticvoid method()

        {

              System.out.println("zistatic method");

        }

}

主函数中:

        Fuf = new Zi();

        f.method();   //fu static method

 

如果:

        Ziz = new Zi();

        z.method();   //zi static method

等同于:

        Fu.method();

        Zi.method();

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值