JAVA 关于多态

本文详细介绍了Java中的多态概念,包括向上转型、动态绑定、方法重写及其应用。多态允许一个引用对应多种类型实例,降低了类调用者的使用成本并提高了代码的可扩展性。此外,还讨论了向下转型的注意事项,强调了避免在构造方法中调用可能被重写的方法。
摘要由CSDN通过智能技术生成

一、向上转型

    用一个 父类的引用去指向一个子类的实例 写法就称为向上转型。(来源自用  UML 图来表示类之间的关系的表示方式,因为父类通常画在子类的上方, 所以我们就称为“ 向上转型” 表示 往父类的方向转 )转成父类的引用后就只能访问到父类的属性和方法了。也是 is - a 语义 向上转型有三种方式:① 直接赋值;② 方法传参;③ 方法返回。下面我就对这三种方法分别举个例子讲述:
1)直接赋值:
//对于一个继承自 Animal 类的 Dog 类,可以写成:
Dog dog = new Dog("小汪");
Animal animal = dog;

//或写成:
Animal animal = new Dog("小汪");

2)方法传参:

public class Test1_21{
    public static void main(String[] args) {
        Dog dog = new Dog("小汪");
        feed(dog);
    }

    public static void feed(Animal animal) {
        animal.eat("狗粮");
    }
}

//执行结果
小汪正在吃狗粮

3)方法返回:

public class Test1_21 {
    public static void main(String[] args) {
        Animal animal = findMyAnimal();
   }

    public static Animal findMyAnimal() {
        Dog dog = new Dog("小汪");
        return dog;
   }
}

 

二、动态绑定

    在 Java 调用某个类的方法 究竟执行了哪段代码( 是父类方法的代码还是子类方法的代码) 要看究竟这个 引用指向的是父类对象还是子类对象 这个过程是程序运行时 (运行期)决定的 而不是 编译期——静态 的), 因此称为 动态绑定
    如果一个方法只在父类中存在,那么可以调用这个方法且不涉及动态绑定问题;如果一个方法只在子类中,那么调用这个方法就会报错但也不涉及动态绑定问题;如果一个方法在子类和父类中均存在但参数的类型和个数不同,同前两个结果一样但也不涉及动态绑定。 只有一个方法在父类和子类中均存在且参数的类型和个数也相同才涉及动态绑定
 
 

三、方法重写

    子类实现父类的同名方法并且参数的类型和个数完全相同,这种情况称为覆写/重写/覆盖(Override)。因为对于已经投入使用的类,尽量不要对其进行修改。因此我们可以重新定义一个新的类,来重复利用其中共性的内容,并且可以根据要求添加或者修改其中的内容。事实上,方法重写是 Java 语法层次上的规则,而动态绑定是方法重写这个语法规则的底层实现,两者本质上描述的是相同的事情只是侧重点不同。

• 注意:普通方法可以重写,static 修饰的静态方法不能重写;重写中子类的方法的访问权限不能低于父类的方法访问权限;重写的方法返回值类型不一定和父类的方法相同。并且推荐在代码中进行重写方法时给子类的方法前显式加上 @Override 注解,有了这个注解能帮我们进行一些合法性校验

※区别重载和重写:重载是在同一个作用域(一个类)中,方法名称相同且参数的类型及个数不同重写是在继承关系中,方法名称、(返回值类型如果和父类的返回值类型也是父子关系也可以在重写方法时不与父类的返回值类型完全相同)、参数的类型及个数完全相同,并且被覆写的方法不能拥有比父类更严格的访问控制权限。

 

四、理解多态

    有了面的向上转型、动态绑定、方法重写之后,我们就可以使用 多态(polypeptide) 的形式来设计程序了。多态就是一个引用能对应到多种形态(即不同类型的实例)。

※使用多态的好处:

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

2. 可扩展能力更强。如果要新增一种新的类,使用多态的方式代码改动成本也比较低。因为对于类的调用者来说只需要创建一个新类的实例就可以了,改动成本很低。

3.  能够降低代码的 " 圈复杂度 ", 避免使用大量的 if - else。举个例子:
//不基于多态实现打印多个形状:
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.draw();
        } else if (shape.equals("rect")) {
            rect.draw();
        } else if (shape.equals("flower")) {
            flower.draw();
        }
    }
}

//使用多态实现:
public static void drawShapes() {
    Shape[] shapes = {new Cycle(), new Rect(), new Cycle(), 
                      new Rect(), new Flower()};
    for (Shape shape : shapes) {
        shape.draw();
    }
}

 

五、向下转型

    向上转型是子类对象转成父类对象, 向下转型就是 父类对象转成子类对象 ,可以通过向下转型还原回原来的类型。
//向上转型:
Animal animal = new Dog("小汪");
//向下转型:
Dog dog = (Dog)animal;

向下转型是向上转型的可逆操作,如果当前需要调用的方法只在子类中而不在父类中存在,那么就可以先向下转型成子类对象再调用该方法。但要注意对象之间的类型一定要匹配才可以,不然运行时就会报错。可以用 instanceof 判定一个引用是否是某个类的实例。如果是则返回 true,这时再进行向下转型就比较安全了。

注意:尽量不要在构造方法中调用方法(如果这个方法被子类重写过就会触发动态绑定但是此时子类对象还没构造完成),可能就会出现一些隐藏的但是又极难发现的问题。

 

六、执行顺序

1. 无继承关系时:静态代码块先执行,且 只执行一次 ;实例代码块接着执行;最后构造方法执行 。
2. 有继承关系时:
1) 父类静态代码块优先于子类静态代码块执行,且均是 最早执行
2) 父类实例代码块和父类构造方法紧接着执行;
3) 子类的实例代码块和子类构造方法紧接着再执行;
4) 第二次实例化子类对象时,父类和子类的静态代码块 都将不会再执行
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值