初步理解多态

本文详细解释了Java中方法的重写、重载、静态绑定与动态绑定的概念,以及向上转型、向下转型和多态的使用及其优缺点。强调了在构造方法中避免调用可能被重写的方法的重要性。
摘要由CSDN通过智能技术生成

重写(一定是发生在继承层次上)

1.被private修饰的方法是不能被重写的

2.被final修饰的方法(密封方法)也不能被重写

3.被static修饰的也不能重写

重写和重载的区别

区别点重写重载
方法名称方法名称相同相同
返回值返回值相同(不同的话构成父子类关系也可以)不做要求
参数列表参数列表相同(数据类型、顺序、个数)不同

重写中返回值构成父子类关系的示例代码:

静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代表函数重载。

动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体  调用那个类的方法。

class Animal {
    public  void eat() {
        System.out.println(name + "吃饭!");
    }
}


class Dog extends Animal{
    public void eat() {
        System.out.println(name+"正在吃狗粮!");
    }
}

在编译的时候还是Animal的eat(),但是在程序运行的时候就变成了Dog的eat(),这个过程我们就叫做:动态绑定。

向上转型和向下转型

1.向上转型

  1. 直接赋值
  2. 方法的参数
  3. 方法的返回值
public class TestAnimal {
    // 2. 方法传参:形参为父类型引用,可以接收任意子类的对象
    public static void eatFood(Animal a){ 
        a.eat();
    }

    // 3. 作返回值:返回任意子类对象
public static Animal buyAnimal(String var){ 
    if("狗".equls(var)){
        return new Dog("狗狗",1);
    }else if("猫"equls(var)){
        return new Cat("猫猫", 1);
    }else{
        return null;
    }
}

public static void main(String[] args) {
    Animal cat = new Cat("元宝",2); // 1. 直接赋值:子类对象赋值给父类对象
    Dog dog = new Dog("小七", 1);

    eatFood(cat);
    eatFood(dog);

    Animal animal = buyAnimal("狗"); 
    animal.eat();

    animal = buyAnimal("猫"); 
    animal.eat();
    }
}

向上转型的优点:让代码实现更简单灵活。

向上转型的缺陷:不能调用到子类特有的方法。

2.向下转型

向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。Java中为了提高向下转型的安全性,引入了instanceof ,如果该表达式为true,则可以安全转换。

class Animal { 
    String name;
    int age;

    public Animal(String name, int age){ 
        this.name = name;
        this.age = age;
    }
    public void eat(){ 
        System.out.println(name + "吃饭");
    }
}

class Cat extends Animal{ 
    public Cat(String name, int age){
    super(name, age);
}
    @Override
    public void eat(){
        System.out.println(name+"吃鱼~~~");
    }

    public void mew(){
        System.out.println(name + "喵喵喵叫");
    }
}

class Dog extends Animal { 
    public Dog(String name, int age){
    super(name, age);
}
    @Override
    public void eat(){
        System.out.println(name+"吃骨头~~~");
    }

    public void bark(){
        System.out.println(name + "汪汪汪汪叫");
    }
}

public class Home0120{
    public static void main(String[] args) {
        Cat cat = new Cat("元宝",2);
        Dog dog = new Dog("小七", 1);

        // 向上转型
        Animal animal = cat; 
        animal.eat();
        animal = dog; 
        animal.eat();

        if(animal instanceof Cat){
            cat = (Cat)animal; cat.mew();
        }

        if(animal instanceof Dog){
            dog = (Dog)animal; dog.bark();
        }
    }
}

多态

   实现条件

     在java中要实现多态,必须要满足如下几个条件,缺一不可:

  1. 必须在继承体系下
  2. 通过父类的引用调用重写的方法
  3. 子类必须要对父类中方法进行重写
class Shape {
    public void draw() {
        System.out.println("画图形!");
    }
}

class Rect extends Shape {
    @Override
    public void draw() {
        System.out.println("画矩形!");
    }
}

class Cycle extends Shape {
    @Override
    public void draw() {
        System.out.println("画圆!");
    }
}

class Triangle extends Shape {
    @Override
    public void draw() {
        System.out.println("画一个三角形!");
    }
}

public class Home0123 {

    public static void drawMap(Shape shape){
        shape.draw();
    }

    public static void main(String[] args) {
        drawMap(new Rect());
        drawMap(new Cycle());
        drawMap(new Triangle());
    }

}

在drawMap方法当中,Shape shape引用 引用的子类对象不一样,调用方法表现出来的行为不一样。我们把这种思想就叫做多态。

多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。

优缺点

使用多态的好处

  1. 能够降低代码的 "圈复杂度", 避免使用大量的 if - else
  2. 可扩展能力更强                                                                                                                                 如果要新增一种新的形状, 使用多态的方式代码改动成本也比较低.                                           对于类的调用者来说(drawShapes方法), 只要创建一个新类的实例就可以了, 改动成本很低.                                                                                                                                                     而对于不用多态的情况, 就要把 drawShapes 中的 if - else 进行一定的修改, 改动成本更高.

使用多态的缺陷

       代码的运行效率会变低

避免在构造方法中调用重写的方法

我们创建两个类, B 是父类, D 是子类. D 中重写 func 方法. 并且在 B 的构造方法中调用 func

class B {
    public B() {
        func();
    }
    public void func() {
        System.out.println("B.func()");
    }
}

class D extends B {
    private int num = 1;
    @Override
    public void func() {
        System.out.println("D.func() " + num);
    }
}

public class Test {
    public static void main(String[] args) {
        D d = new D();
    }


}

构造 D 对象的同时, 会调用 B 的构造方法.B 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 D 中的 func.此时 D 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0. 如果具备多态性,num的值应该是1. 所以在构造函数内,尽量避免使用实例方法,除了finalprivate方法。

结论: "用尽量简单的方式使对象进入可工作状态", 尽量不要在构造器中调用方法(如果这个方法被子类重写,  就会触发动态绑定, 但是此时子类对象还没构造完成), 可能会出现一些隐藏的但是又极难发现的问题.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值