面向对象编程-5-多态

多态

向上转型

Bird  bird=new Bird (“小鸟”);
//这个代码可以写成这样:
Bird bird =new Bird("小鸟");
Animal  bird2=bird;
//或者写成下面的形式
Animal bied2=new Bird("小鸟")

此时bird2是一个父类(Animal)的引用,指向一个子类(Bird)的实例。这种写法称为向上转型。

为啥叫 “向上转型”?
在面向对象程序设计中, 针对一些复杂的场景(很多类, 很复杂的继承关系), 程序猿会画一种 UML 图的方式来表 示类之间的关系. 此时父类通常画在子类的上方. 所以我们就称为 “向上转型” , 表示往父类的方向转.

向上转型发生的时机:
直接赋值
方法传参
方法返回

方法传参

public class Test{
	public static void main(String [] name){
	Bird bird =new Bird("小鸟";
	feed(bird);
}
public static void feed(Animal animal){
	animal.eat("谷子");
	}
}
//此时形参animal的类型是Animal(基类),实际上对应到Bird(父类)的实例

方法返回

public class Test{
	public static void main(String [] args){
	Animal animal =FindMyAnimal();
	}
	public static Animal FindMyAnimal(){
	Bird bird =new Bird("小鸟");
	return bird;
	}
}
//此时方法findMyAnimal返回的 是一个Animal类型的引用,但是实际上对应到Bird的实例。

动态绑定
当子类和父类中出现同名方法的时候,再去调用会出现啥情况??

package packageAnimal;
public class Animal {
    protected String name;

    public Animal(String name) {
        this.name = name;
    }
    public void eat(String food){
    	System.out.println("我是一只小动物";
        System.out.println(this.name+"正在吃"+food);
    }
}
//Bird.java
public class Bird extends Animal{
	public Bird(String name){
	super(name);
	}
	public void eat(String food){
	System.out.println("我是一只小鸟");
	System.out.println(this.name+"正在吃"+food);
	}va
}
//Test.java
public class Test{
	public static void main(String [] args){
	Animal animal1=new Animal("小鸟");
	animal1.eat("谷子");
	Animal animal=new Bird("小鸟蛋");
	animal2.eat("谷子");
	}
}
//执行结果
我是一只小动物
小鸟正在吃谷子
我是一只小鸟
小鸟蛋正在吃谷子

此时发现:
animal1和animal2虽然都是Animal类型的引用,但是animal1执行Animal类型的实例,animal2指向bird类型的实例。
针对animal1和animal2分别调用eat方法,发现animal1.eat()实际调用父类的方法,二animal2.eat()实际调用了子类的方法。

因此,在java中,调用某个类的方法,究竟执行了那段代码(是父类方法的代码还是子类的代码),要看究竟这个引用指向的是父类对象还是子类对象
指向子类的对象就调用子类中的方法
指向父类的对象就调用父类中的方法

这个过程是程序运行时决定的而不是编译期,因此称为 动态绑定。

方法重写
针对刚才的eat方法来说:

子类实现父类的同名方法,并且参数的类型和个数完全相同,这种情况称为重写/覆写/覆盖
关于重写的注意事项:
1、重写和重载完全不一样,不要混淆
2、普通方法可以重写,static修饰的方法不能重写
3、重写中子类的访问权限不能低于父类的访问权限

方法权限示例:

//Animal.java
public calss Animal{
	public void eat (String food){
	......
	}
}
//Bird.java
public class Bird extends Animal{
//将子类的 eat改成 private
private void eat(String food){
	......
	}
}
//编译出错
Error:(8, 10) java: com.bit.Bird中的eat(java.lang.String)无法覆盖com.bit.Animal中的 eat(java.lang.String) 
  正在尝试分配更低的访问权限; 以前为public

另外,针对重写的方法,可以使用@Override注解来显示指定

//Bird.java
	public class Bird extends Animal{
@Override
private void eat(String food){
.....
	}
}

有了这个注解能帮我们进行一些合法性校验. 例如不小心将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发 现父类中没有 aet 方法, 就会编译报错, 提示无法构成重写.
推荐在代码中进行重写方法时显式加上 @Override 注解.

重写和重载的区别
重载的规则是:方法名称相同,参数类型或者参数个数不同。
重写的规则是:方法名称相同,参数个数和类型也完全相同。(子类实现父类的同名方法)
普通方法可以重写,static修饰的方法不能重写
重写中子类的访问权限不能低于父类的访问权限
在这里插入图片描述
多态就体现在三个方面上:
1、向上转型
2、动态绑定
3、方法重写

代码示例:打印多种形状
用多态先来设计一个程序
可以写一些只关注父类的代码,就能够同时兼容各种子类的情况
新建一个packagePloypeptide包
里面放如下类和代码

 //父类
 package packagePolypeptide;
public class Shape {
    public void show(){
        //先定义一个函数,什么也不实现
    }
}
//子类1
package packagePolypeptide;

public class Cycle extends Shape{
    @Override
    public void show() {
        System.out.println("○");
    }
}
//○□♣
//子类2
package packagePolypeptide;

public class Rect extends Shape{
    @Override
    public void show() {
        System.out.println("□");
    }
}
//子类3
package packagePolypeptide;

public class Flower extends Shape{
    @Override
    public void show() {
        System.out.println("♣");
    }
}
//Test测试类
package packagePolypeptide;

public class Test {
    public static void main(String[] args) {
        Shape s1=new Flower();
        Shape s2=new Cycle();
        Shape s3=new Rect();
        drawMap(s1);
        drawMap(s2);
        drawMap(s3);
    }
    //打印单个图形
    public static void drawMap(Shape shape){
        shape.show();
    }
}
/*打印结果
* ♣
* ○
* □
*/

这个代码分为类的实现者(父类和子类)和调用者(Test)
当类调用者在编写drawMap这个方法的时候,参数类型为Shape(父类),此时该方法内部并不知道,也不关注当前的shape引用指向的是哪个类型(哪个子类)的实例,此时shape这个引用调用draw方法可能会有多种不同的表现(这个表现和shape对应的 实例相关),这种行为就是多态。

多态的真正意义:
就是一个引用能够表现出多种不同的形态

就比如shape.show()这个引用,引用的是啥,要看上面函数具体说明的是哪个对象,才能调用对应的方法。

多态的好处是啥?
1)类调用者对类的使用成本进一步降低
封装是让类的调用者不必知道类的具体实现细节
多态能让类的调用者连这个类的类型是什么都不必知道,只需要知道这个对象具有什么方法就行。
因此,多态可以理解成是封装的更近一步,让类的调用者对类的使用成本进一步降低

2)能够降低代码的“圈复杂度” 避免使用大量的 if-else 语句
比如说要打印的不是一个形状,打印多个形状,多个形状就不基于多态
代码:

package packagePolypeptide;

public class main {
    public static void main(String[] args) {
        Shape s1=new Flower();
        Shape s2=new Cycle();
        Shape s3=new Rect();
        drawShapes();
    }
    public static void drawShapes(){
        Rect rect=new Rect();
        Cycle cycle=new Cycle();
        Flower flower=new Flower();
        String [] shapes={
                "cycle",
                "rect",
                "cycle",
                "rect",
                "flower"
        };
        for(String shape:shapes){
            if(shape.equals("cycle")){
                cycle.show();
            }else if(shape.equals("rect")){
                rect.show();
            }else if(shape.equals("flower")){
                flower.show();
            }
        }
    }
}

如果使用多态
创建一个 shape对象的数组

package packagePolypeptide;
//如果使用多态,不用写很多的if-else语句
//代码更简单
public class SZ {
    public static void main(String[] args) {
        Shape s1=new Flower();
        Shape s2=new Cycle();
        Shape s3=new Rect();
        drawShapes();
    }
    public static void drawShapes(){
        //创建一个数组
        Shape [] shapes= {
                new Cycle(),
                new Rect(),
                new Cycle(),
                new Rect(),
                new Flower()
        };
        for(Shape shape:shapes){
            shape.show();
        }
    }
}

(圈复杂度)
描述一段代码的复杂程度,可以理解为循环语句和条件语句出现的次数,一般不要超过10;
降低圈复杂度的方法
1、拆分函数
2、使用多态
3、使用转移表

3)、使用多态可以使代码的可扩展能力变强
如果要新增一种新的形状,使用多态代码改动成本也比较低

package packagePolypeptide;

public class Triange extends Shape{
    @Override
    public void show() {
        System.out.println("△");
    }
}

对于类的调用者Test来说,只要创建一个新的类的实例就可以了,改动成本很低
而对于不使用多态的情况,就要把drawShapes中的if-else进行修改,成本很高

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值