引子:同一件事交给不同的人去做造成不同的结果。--多态
向上转型
向上转型的三种形态:1.直接赋值;2.函数作形参接收;3.做返回值。
来看几个例子:eg1.直接赋值
给出代码可以自己感受一下:
//都是在一个包底下的不同类
package Testdemo1;
public class Animal {
public String name;
public int age;
public Animal(String Name,int Age){
this.name=Name;
this.age=Age;
}
public void eat(){
System.out.println("正在吃饭...");
}
}
package Testdemo1;
public class Bird extends Animal{
public Bird(String Name,int Age){
super(Name,Age);
}
public void eat(){
System.out.println("小鸟正在吃饭...");
}
public void fly(){
System.out.println("小鸟正在飞行...");
}
}
package Testdemo1;
public class Dog extends Animal{
public Dog(String Name,int Age){
super(Name,Age);
}
public void eat(){
System.out.println("小狗正在吃饭...");
}
public void bark(){
System.out.println("小狗正在吠叫...");
}
}
package Testdemo1;
public class Test01 {
public static void main(String[] args) {
Animal a=new Bird("多芬",5);
System.out.println(a.name +" "+a.age );
}
}
eg2.作为函数形参接收子类对象
可以看出,在Test类中又写了一个静态函数fun,其功能是打印对象中的各项数据。形参是Aniaml a,是父类的引用;而main函数中给fun函数传入的参数是子类对象。这就是第二种向上转型。
eg.3 作函数返回值
如图,在FUN函数中创建了一个子类Dog的对象dog,然后返回dog,而函数返回值类型用的是Animal父类。这就是第三种向上转型。
向上转型也有缺点,如不能调用子类中有 但 父类中没有的函数。如下:
怎么解决呢?这就引出了下面的内容:向下转型。
向下转型
为了解决父类对象不能访问子类特有函数or成员变量的问题,向下转型应运而生。
其形式类似于C语言中的强制类型转换。
step1:向上转型(三种形式)
step2:向下转型:子类对象=(父类)父类对象;
step3:访问子类特有函数
为了安全保险,我们一般还会引入instanceof来确认父类对象是否是由子类对象向上转型而来:
if(animal instanceof Bird){
Bird bird = (Bird)animal;
bird.bark();
}
重载与重写
想要实现动态绑定从而实现多态,就需要了解重写,区分重载和重写的区别。
重载:函数名字相同,但其返回值,形参参数类型或者个数 都可以不同。编译器也主要靠形参个数或类型+返回值类型 来区分调用哪一个函数。
而重写的要求更加严格:函数名,返回值类型,形参类型及个数,都必须一模一样,只有函数体(函数的定义部分)可以自行修改。
如下图:
动态绑定
这是实现多态的重要步骤。
先来看一个现象:
public class Animal {
public String name;
public int age;
public Animal(String Name,int Age){
this.name=Name;
this.age=Age;
}
public void eat(){
System.out.println("正在吃饭...");
}
}
public class Bird extends Animal{
public Bird(String Name,int Age){
super(Name,Age);
}
public void eat(){
System.out.println("小鸟正在吃饭...");
}
public void fly(){
System.out.println("小鸟正在飞行...");
}
}
public class Dog extends Animal{
public Dog(String Name,int Age){
super(Name,Age);
}
public void eat(){
System.out.println("小狗正在吃饭...");
}
public void bark(){
System.out.println("小狗正在吠叫...");
}
}
public class Test01 {
public static void fun(Animal animal){
animal.eat();
System.out.println("-----------");
}
public static void main(String[] args) {
Dog dog=new Dog("旺财",8);
Bird bird=new Bird("多芬",6);
fun(dog);
fun(bird);
}
运行结果:
分析一下以上代码:首先,两个子类里都重写了父类的eat()函数;
其次,main函数中创建了两个子类的对象,在调用fun()函数的时候发生了向上转型(第二类:函数形参接收子类对象)。
明明fun函数中是Animal的对象animal调用了eat()函数,却没有表现Animal类里的eat()方法,而是表现出了子类中eat()方法。--多态
动态绑定:等到程序运行阶段,要根据具体的对象才能确定是对应哪个方法。
对应的,静态绑定:在编译阶段就能确定使用哪个方法,看一下例子:
在调用eat()方法时编译器已经知道dog.eat()要调用Dog的eat()方法,同理bird。
总结:出现动态绑定的必要条件:1.有继承关系;2.子类重写父类方法;3.发生向上转型。
感谢大家观看!欢迎提出建议!