多态概述
面向对象的三大特性
多态(父类引用变量可以指向子类对象。让程序提前不知道自己到底调用的是哪个方法,只有当他运行的时候才确定,这样不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现)让程序可以选择多个运行状态
封装(隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法)
继承(为了重用父类代码。两个类若存在IS-A的关系就可以使用继承)
多态的使用条件和继承一样,必须有父类和子类,在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法
方法调用的先后顺序
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法,
动态绑定
方法可以沿着继承链的多个类中实现,在父类中定义,在子类中重写。
一个对象调用方法是由该对象属于哪个子类来决定的,这就是动态绑定。
动态绑定的工作机制
动态绑定工作机制如下:假设对象
o
o
o 是类
C
1
,
C
2
,
…
C
n
C_1, C_2,… C_n
C1,C2,…Cn的实例,其中
C
1
C_1
C1是
C
2
C_2
C2的子类,
C
2
C_2
C2 是
C
3
C_3
C3 的子类,… ,
C
n
−
1
C_{n-1}
Cn−1是
C
n
C_n
Cn 的子类.。也就是说,
C
n
C_n
Cn 是最通用的类,
C
1
C_1
C1是最特殊的类。在 Java 中,
C
n
C_n
Cn 是 Object 类。如果对象
o
o
o 调用一个方法
p
p
p, 那么
JVM 会依次在类
C
1
,
C
2
,
…
,
C
n
−
1
,
C
n
C_1,C_2, … ,C_{n -1},C_n
C1,C2,…,Cn−1,Cn 中查找方法 p 的实现,直到找到为止。一旦找到一个实现,就停止査找,然后调用这个首先找到的实现。
多态的好处
可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。让具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容(功能)的函数。
举个例子,我们平常有父类和子类的时候,如果只用继承,子类可以用父类的函数,但是调用的时候输出的结果是同样的。你需要不同的子类调用相同的函数产生不同的结果,当然,不同的函数名也不失为一种方法,但是,当他量达到一定数量级之后,修改函数名就会让你的代码过多的冗余,多态就因此诞生了。他让多个子类可以用相同的函数名,但是具体的结果输出只有运行的时候程序看他是从哪个类来的才会输出相对应的结果
多态的定义与使用格式和两种转型
public static void main(String[] args) {
show(new Cat()); // 以 Cat 对象调用 show 方法
show(new Dog()); // 以 Dog 对象调用 show 方法
Animal a = new Cat(); // 向上转型
a.eat(); // 调用的是 Cat 的 eat
Cat c = (Cat)a; // 向下转型
c.work(); // 调用的是 Cat 的 work
}
这里有两个新颖的词:
向下转型: 将子类c赋值父类a,因为类型不同,所以强制类型转换(大到小)
向上转型: 将父类a赋值一个子类的类型(或者开辟这样的一个空间),此后调用该父类的函数时遇到同名函数就会执行子类为cat子类的函数效果
父类的函数
//show函数
public static void show(Animal a) {
a.eat();
// 类型判断
if (a instanceof Cat) { // 猫做的事情
Cat c = (Cat)a;
c.work();
} else if (a instanceof Dog) { // 狗做的事情
Dog c = (Dog)a;
c.work();
}
}
//父类函数
abstract class Animal {
abstract void eat();
}
//设定子类
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void work() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
public void work() {
System.out.println("看家");
}
}
像上面的代码,我们让父类a为子类cat的上升转型,那么输出的a.eat就是等价于cat.eat
说的粗俗一点:a是b爹,b是c的爹,b是d的爹,那么c和d就可以是b或者a,调用a和b的内部方法,就得看他们被new成了c还是d,输出的结果也就是c或者d函数调用的结果
虚函数和多态
这东西是我看c++的博客看来的。但我发现虚函数是c++所特有的,因为java有了动态绑定之后,写在java内的普通函数在使用上就和c++的虚函数一样。如果 Java 中不希望某个函数具有虚函数特性,可以加上 final 关键字变成非虚函数
c++多态的构成条件
多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。
那么在继承中要构成多态还有两个条件:
1.必须通过基类的指针或者引用调用虚函数
2.被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
虚函数是加了virtual关键词之后的类成员函数。
虚函数定义:即被virtual修饰的类成员函数称为虚函数。
那我们就可以等价的理解了
在java里面,创建一个父类后,再建几个子类和该父类关联,形成继承关系,在父类的函数里将每个子类调用父类函数时所应当执行的程序,这就是多态。(感觉还挺好用的,不用特意去定义一个虚函数)