javaSE中多态
我们要了解多态就要先知道什么是向上转型
1、向上转型
向上转型发生的时机:
1、直接赋值
2、父类引用作为函数参数
3、作为函数的返回值
2、动态绑定
这是为什么呢???
父类引用调用的eat方法不应该是调用父类的eat方法吗??
这是因为这时候发生了动态绑定。
那什么是动态绑定呢???
动态绑定:
1、父类引用 引用子类对象
2、父类引用调用父类和子类同名的覆盖方法
同名的覆盖方法(重写)只发生在父子类关系中:
1、方法名相同
2、参数列表相同(参数个数,类型)
3、返回值相同(特殊情况:返回值不同(也叫协变类型))
重写的时候 需要注意的:
关于重写时需要注意
1. 重写和重载完全不一样. 不要混淆(思考一下, 重载的规则是啥?)
2. 普通方法可以重写, static 修饰的静态方法不能重写.
3. 重写中子类的方法的访问权限不能低于父类的方法访问权限.
4. 重写的方法返回值类型不一定和父类的方法相同(但是建议最好写成相同, 特殊情况除外).
动态绑定也叫运行时绑定
找到代码所在的路径www这个文件夹里
按住shift键+右键,点击在此处打开powershell窗口
输入 javap -c Test1 回车,找到mian函数
Animal.eat() 这时候编译的时候是调用父类的方法,为什么是运行是调用子类的方法呢?
因为这时在 编译的时候不能够确定调用的是谁的方法,只有在运行的时候才能够确定。
这就叫运行时绑定也叫动态绑定
动态绑定
重写当中的协变类型
重载会发生静态绑定 也叫 编译时绑定
2.1、通过父类引用只能访问自己的成员
2.2、重载和重写的区别
2.3、向下转型
这就发生了向下转型
但是这样不好
Animal是动物,bird是鸟
Bird bird = (Bird)animal; 所有的动物都是鸟吗?肯定不是嘛,这里也可以理解是大范围强制转换为 小范围
看下面:animal引用Dog
然而Bird bird = (Bird)animal;
通过bird就不能调用fly这个方法,
通过bird就不能调用fly这个方法,这里就要判断一下
instanceof 可以判定一个引用是否是某个类的实例
向下转型不安全,不建议使用
在父类的构造方法中调用重写方法的时候 是一个坑
2.4、理解多态
有了上面的向上转型, 动态绑定, 方法重写之后, 我们就可以使用 多态(polypeptide) 的形式来设计程序了
比如说:
通过shape同一个引用,可以表现出多种形态
实现多态的前提:动态绑定
当类的调用者在编写 drawMap 这个方法的时候, 参数类型为 Shape (父类), 此时在该方法内部并不知道, 也不关注当
前的 shape 引用指向的是哪个类型(哪个子类)的实例. 此时 shape 这个引用调用 draw 方法可能会有多种不同的表现
(和 shape 对应的实例相关), 这种行为就称为 多态
使用多态的好处是什么?
1) 类调用者对类的使用成本进一步降低.
封装 是让类的调用者不需要知道类的实现细节
多态 能让类的调用者连这个类的类型是什么都不必知道,,只需要知道这个对象具有某个方法即可,因此,,多态可以理解成是封装的更进一步, 让类调用者对类的使用成本进一步降低
作用1:能够降低代码的 “圈复杂度”, 避免使用大量的 if - else
例如我们现在需要打印的不是一个形状了, 而是多个形状. 如果不基于多态, 实现代码如下
class Shape {
public void draw() {
System.out.println("draw()::");
}
}
class Yuan extends Shape{
@Override
public void draw() {
System.out.println("●");
}
}
class Sanj extends Shape{
@Override
public void draw() {
System.out.println("△");
}
}
public class TestD {
public static void main(String[] args) {
Yuan yuan = new Yuan();
Sanj sanj = new Sanj();
String[] str = {"yuan","sanj","yuan","sanj","yuan"};
for (String a : str) {
if(a.equals("yuan")){
yuan.draw();
}else if (a.equals("sanj")){
sanj.draw();
}
}
}
}
那我们就可以简化代码:
class Shape {
public void draw() {
System.out.println("draw()::");
}
}
class Yuan extends Shape{
@Override
public void draw() {
System.out.println("●");
}
}
class Sanj extends Shape{
@Override
public void draw() {
System.out.println("△");
}
}
public class TestD {
public static void main(String[] args) {
Yuan yuan = new Yuan();
Sanj sanj = new Sanj();
Shape[] shapes = {yuan,yuan,sanj,sanj,yuan};
for (Shape shape : shapes) {
shape.draw();
}
}
}
总结
1、多态是面向对象程序设计中比较难理解的部分. 我们会在后面的抽象类和接口中进一步体会多态的使用. 重点是多态带来的编码上的好处.
另一方面, 如果抛开 Java, 多态其实是一个更广泛的概念, 和 “继承” 这样的语法并没有必然的联系.
2、C++ 中的 “动态多态” 和 Java 的多态类似. 但是 C++ 还有一种 “静态多态”(模板), 就和继承体系没有关系了.
3、Python 中的多态体现的是 “鸭子类型”, 也和继承体系没有关系./
4、Go 语言中没有 “继承” 这样的概念, 同样也能表示多态.
无论是哪种编程语言, 多态的核心都是让调用者不必关注对象的具体类型. 这是降低用户使用成本的一种重要方式