目录
多态的是什么
什么是多态 。多就是[多种]态[状态],我们成为多态,可能这样看起来比较抽象,但是没事我们继续往下看
基本介绍
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上。一讲到多态,肯定会有运行类型和编译类型,那么什么是编译类型什么是运行呢,有这么一句话=编译类型看左边运行类型看右边
来看看具体的案例吧
Dog dog = new Animal();
上面的是一个非常简单的代码,那么根据上的的这样一句话,编译类型看等号的左边,运行类型看等号的右边,因此上面的这一句代码的
编译类型是:Dog
运行类型是:Animal
那么这个有什么作用呢,非常的重要,接下来马上就会讲到
多态的注意事项和使用细节
多态的向上转型
1)本质:父类的引用指向了子类的对象
2)语法:父类类型 引用名 = new 子类类型);
3)特点:编译类型看左边,运行类型看右边。 可以调用父类中的所有成员(需遵守访问权限),不能调用子类中特有成员; 最终运行效果看子类的具体实现!
向上转型调用方法的规则如下:
(1)可以调用父类中的所有成员(需遵守访问权限)
(2)但是不能调用子类的特有的成员
(#)因为在编译阶段,能调用哪些成员,是由编译类型来决定的
animal.catchMouse();错误
(4)最终运行效果看子类(运行类型)的具体实现, 即调用方法时,按照从子类(运行类型)开始查找方法,然后调用,规则我前面我们讲的方法调用规则一致。
多态的向下转型
1)语法:子类类型 引用名= (子类类型)父类引用;
2)只能强转父类的引用,不能强转父类的对象
3)要求父类的引用必须指向的是当前目标类型的对象
4)当向下转型后,可以调用子类类型中所有的成员(也要遵守访问权限)
//多态的向下转型
//(1)语法:子类类型 引用名 =(子类类型)父类引用;
向上转型和向下转型的代码演示:
package idea.polymorphic.detail;
/**
* 演示向上转型
*/
public class PolyDetail01 {
public static void main(String[] args) {
//向上转型: 父类的引用指向了子类的对象
//语法:父类类型 引用名 = new 子类类型();
/*
这就是一个向上转型
此时我们看,Animal animal = new Cat(); 这句话的编译类型和运行类型是什么
还记得我们的话嘛,编译类型看=左边 运行类型看=右边
编译类型是 Animal
运行类型是 Cat
语法:父类类型 引用名 = new 子类类型();
*/
Animal animal = new Cat();
Object obj = new Cat();//这样也是可以的,因为Cat是Animal的子类,而Animal是Object的子类,因此这样也是满足向上转型的语法
//向上转型调用方法的规则如下:
//(1)可以调用父类中的所有成员(需遵守访问权限)
//可以调用父类中的属性,但是私有的属性不能够访问
System.out.println(animal.name);
System.out.println(animal.age);
//System.out.println(animal.id);因为id属性是private的,因此不能直接访问会报错
//看看方法 都可以调用父类中的方法(需要遵守访问权限,如果是私有的方法也不能访问,和调用属性的规则一样)
animal.eat();
animal.run();
animal.show();
animal.sleep();
//(2)但是不能调用子类的特有的成员
//(#)因为在编译阶段,能调用哪些成员,是由编译类型来决定的
//因为这是子类特有的方法,所以不能够去访问
//animal.catchMouse();//错误的
//(4)最终运行效果看子类(运行类型)的具体实现, 即调用方法时,按照从子类(运行类型)开始查找方法,然后调用,规则我前面我们讲的方法调用规则一致。
//我们知道,在子类中也有一个eat()方法,该方法重写了父类的eat()方法,那么在使用animal去调用eat方法的时候,到底输出的是父类的方法呢,还是子类的方法
//结果是子类的方法,因为最终运行起来,最后看的还是运行类型,而我们此时的运行类型是cat,那么在当程序运行起来
animal.eat();
//多态的向下转型
//1)语法:子类类型 引用名= (子类类型)父类引用;
//2)只能强转父类的引用,不能强转父类的对象
//3)要求父类的引用必须指向的是当前目标类型的对象
//4)当向下转型后,可以调用子类类型中所有的成员
//只能强转父类的引用,不能强转父类的对象 这句话怎么理解我们看上面的这个代码 Animal animal = new Cat();
//animal就是父类的引用,因为Animal是父类
//所以向下转型就是 把animal强装成一个Cat
Cat cat = (Cat) animal;
//此时我们再来看编译类型和运行类型
//编译类型是 Cat
//运行类型是 Cat
//向下转型后就可以去访问访问子类中的所以属性,但是也要遵守访问权限
cat.catchMouse();//这样就不会报错了
}
}
属性没有重写之说!属性的值看编译类型
代码演示:
package idea.polymorphic.detail;
public class PolyDetail02 {
public static void main(String[] args) {
//属性没有重写之说!属性的值看编译类型
//向上转型
Base base = new son();
/*
这里输出的就是10,因为属性没有重写之说
属性的值看编译类型 此时的编译类型是 Base 运行类型是 son
所以此时输出的count就是10
*/
System.out.println(base.count);
son son = new son();
/*
这里输出的就是20,因为属性没有重写之说
属性的值编译类型 此时的编译类型是 Base 运行类型是 son
所以此时输出的count就是20
*/
System.out.println(son.count);
}
}
class Base {//父类
int count = 10;
}
class son extends Base {//子类
int count = 20;
}
instanceOf比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型
代码演示:
package idea.polymorphic.detail;
public class PolyDetail03 {
public static void main(String[] args) {
BB bb = new BB();
System.out.println(bb instanceof BB);// true
System.out.println(bb instanceof AA);// true
//aa 编译类型 AA, 运行类型是BB
//BB是AA子类
AA aa = new BB();
//看的是运行类型 此时aa的运行类型是BB 而BB是AA的子类,所以他们是同一个对象
System.out.println(aa instanceof AA);
//看的是运行类型 此时aa的运行类型是BB 而BB就是BB,所以他们是同一个对象
System.out.println(aa instanceof BB);
//obj 编译类型 Object , 运行类型是 Object
Object obj = new Object();
//看的是运行类型 此时obj的运行类型是Object 而obj不是AA的子类,所以他们不是同一个对象
System.out.println(obj instanceof AA);//false
String str = "hello";
//System.out.println(str instanceof AA);
//因为String是Object的子类所以返回true
System.out.println(str instanceof Object);//true
}
}
class AA {
}
class BB extends AA {
}
多态例题
思路分析:
我们要知道对于属性来说没有重写的概念,因此在多态中属性看的是。编译类型,而方法看的是运行类型,因此我们只需要分清楚一个对象实例的编译类型和运行类型,那么就不容易出错,在本例题中我们可以清楚的感受到编译类型和运行类型,仔细看代码中的注释,就可以清楚的知道为什么输出这样值
package idea.polymorphic;
/**
* 讲解多态的例题
*/
public class polyExercise01 {
public static void main(String[] args) {
Sub s = new Sub();
//s的编译类型是 Sub 运行类型是 Sub 因为属性看的是编译类型所以s.count输出的就是20
System.out.println(s.count);//20
//方法看的是运行类型 所以调用的是Sub中的display方法 因为是this.count 所以访问的就是本类中的属性
s.display();//20
Base b = s;
//因为把s赋给了 b 此时 编译类型变成了 Base 运行类型变成了Sub
System.out.println(b == s);//T 把s赋给了b 相当于 b 和 s 都指向同一块空见,所以返回true
//属性看的是编译类型,此时编译b的编译类型是Base 所以b.count 输出的就是10
System.out.println(b.count);//10
//方法看的是运行类型 因为b的运行类型是 Sub 所以b.display()调用的还是Sub中的方法,因此输出的是20
b.display();//20
}
}
class Base {//父类
int count = 10;
public void display() {
System.out.println(this.count);
}
}
class Sub extends Base {//子类
int count = 20;
public void display() {
System.out.println(this.count);
}
}
动态绑定机制
1.当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
2.当调用对象属性时 ,没有动态绑定机制,哪里声明哪里使用
System.out.println(a.sum());//?40 -> 30
/*
思路分析
1.首先我们知道方法看的是运行类型,而a的运行类型是B 所以会去B类中去找sum()方法,但是B类中没有sum()方法,因为B类继承了A类,所以
会去A类中查找,在A类中我们确实查找到了sum()方法,
2.在A类的sum()方法中,有这么一句话return getI()+10; ,当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定,因此
调用这个方法的是a,但是a的编译类型是B,因此会进行动态绑定,会先去B类中查找getI()方法,如果在B类中查找不到,才回去A类查找
3.我们在B类中找到了getI()方法,里面有一句return i; 因为在调用属性时,没有动态绑定机制,所以返回的就是B类中的i,因此返回的就是20
如果在B类中,没有i,那么就会到A类也就是父类中去查找
4.因此最后a.sum(); 输出的就是30
*/System.out.println(a.sum1());//?30-> 20
/*
思路分析
1.首先我们知道方法看的是运行类型,而a的运行类型是B 所以会去B类中去找sum1()方法,但是B类中没有sum1()方法,因为B类继承了A类,所以
会去A类中查找,在A类中我们确实查找到了sum1()方法,
2.在A类的sum1()方法中,有这么一句话return i + 10; ,因为在调用对象属性的时候,没有动态绑定机制,哪里声明哪里使用,所以这里的i
时父类中的i 也就是A类中的i 因此a.sum1()方法输出的就是20
3.如果一直找不到i,最后会报错
*/
package idea.polymorphic.DynamicBindingMechanism;
/**
* 讲解动态绑定机制
*/
public class DynamicBinding {
public static void main(String[] args) {
//a 的编译类型 A, 运行类型 B
A a = new B();//向上转型
//结论
//1.当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
//2.当调用对象属性时 ,没有动态绑定机制,哪里声明哪里使用
System.out.println(a.sum());//?40 -> 30
/*
思路分析
1.首先我们知道方法看的是运行类型,而a的运行类型是B 所以会去B类中去找sum()方法,但是B类中没有sum()方法,因为B类继承了A类,所以
会去A类中查找,在A类中我们确实查找到了sum()方法,
2.在A类的sum()方法中,有这么一句话return getI()+10; ,当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定,因此
调用这个方法的是a,但是a的编译类型是B,因此会进行动态绑定,会先去B类中查找getI()方法,如果在B类中查找不到,才回去A类查找
3.我们在B类中找到了getI()方法,里面有一句return i; 因为在调用属性时,没有动态绑定机制,所以返回的就是B类中的i,因此返回的就是20
如果在B类中,没有i,那么就会到A类也就是父类中去查找
4.因此最后a.sum(); 输出的就是30
*/
System.out.println(a.sum1());//?30-> 20
/*
思路分析
1.首先我们知道方法看的是运行类型,而a的运行类型是B 所以会去B类中去找sum1()方法,但是B类中没有sum1()方法,因为B类继承了A类,所以
会去A类中查找,在A类中我们确实查找到了sum1()方法,
2.在A类的sum1()方法中,有这么一句话return i + 10; ,因为在调用对象属性的时候,没有动态绑定机制,哪里声明哪里使用,所以这里的i
时父类中的i 也就是A类中的i 因此a.sum1()方法输出的就是20
3.如果一直找不到i,最后会报错
*/
}
}
class A {//父类
public int i = 10;
//动态绑定机制:
public int sum() {//父类sum()
return getI() + 10;//20 + 10
}
public int sum1() {//父类sum1()
return i + 10;//10 + 10
}
public int getI() {//父类getI
return i;
}
}
class B extends A {//子类
public int i = 20;
// public int sum() {
// return i + 20;
// }
public int getI() {//子类getI()
return i;
}
// public int sum1() {
// return i + 10;
// }
}
多态数组
数组体现多态,在下面的代码中我们创建了一个Person数组,因为数组也可以体现多态,因为Teacher类和Student都继承了Person类,所以Student和Teacher都可以存在在Person数组中
代码演示:
package idea.polymorphic.polyarr;
/**
* 演示数组体现多态
*/
public class PolyArray {
public static void main(String[] args) {
//创建了一个Person数组,因为数组也可以体现多态,因为Teacher类和Student都继承了Person类,所以Student和Teacher都可以存在在Person数组中
Person[] p = new Person[5];
//初始化数组的值,数组中的类型各不相同,有Person类型的,Student类型的,Teacher类型的
p[0] = new Person("jack", 10);
p[1] = new Student("tom", 10, 100);
p[2] = new Student("many", 10, 90);
p[3] = new Teacher("A", 10, 10000);
p[4] = new Teacher("B", 10, 20000);
//遍历这个数组
for (int i = 0; i < p.length; i++) {
//调用数组中每一个元素的say()方法,因为有动态绑定机制的存在,在调用方法时,看的是运行类型,因此会自动的找到,对应类中的方法去调用
System.out.println(p[i].say());
if (p[i] instanceof Student) {//判断数组中的每个元素的运行类型是声明,如果是Student 那么就强转p[i]然后去调用Student类的方法,相当于向下转型
((Student) (p[i])).study();
} else if (p[i] instanceof Teacher) {//判断数组中的每个元素的运行类型是声明,如果是Teacher 那么就强转p[i]然后去调用Teacher对应类的方法,相当于向下转型
((Teacher) p[i]).teach();
} else {
System.out.println("不做处理");
}
}
}
}
Person类
package idea.polymorphic.polyarr;
public class Person {//父类
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String say() {//返回名字和年龄
return name + "\t" + age;
}
}
Student类
package idea.polymorphic.polyarr;
public class Student extends Person {
private double score;
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
//重写父类say
@Override
public String say() {
return "学生 " + super.say() + " score=" + score;
}
//特有的方法
public void study() {
System.out.println("学生 " + getName() + " 正在学java...");
}
}
Teacher类
package idea.polymorphic.polyarr;
public class Teacher extends Person {
private double salary;
public Teacher(String name, int age, double salary) {
super(name, age);
this.salary = salary;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
//写重写父类的say方法
@Override
public String say() {
return "老师 " + super.say() + " salary=" + salary;
}
//特有方法
public void teach() {
System.out.println("老师 " + getName() + " 正在讲java课程...");
}
}
多态参数
思路分析:
test(jack); test(tom);1.test()方法可以接受一个Employee对象,因为Worker和Manage都是Employee的子类,所以可以接受参数
2.因为在调用方法的时候,看的是运行类型,因此我们传入一个worker对象,那么就会动态绑定到Worker类中的getAnnual()方法
如果传入的是,Manager,那么就会动态绑定到Manager中
思路分析:
testWork(jack); testWork(tom);1.testWork()方法可以接受一个Employee对象,因为Worker和Manage都是Employee的子类,所以可以接受参数
2.这次我们要调用的是子类中的特有属性,因此要对传入的参数进行判断,如果e的类型是Worker,那么就强转,相当于向下转型,并且会去调用对应的方法
package idea.polymorphic.polyparameter_;
/**
* 演示参数体现多态
*/
public class PloyParameter {
public static void main(String[] args) {
//这里我们创建了一个Worker对象
Worker jack = new Worker("jack", 1000);
//这里我们创建了一个Manager对象
Manager tom = new Manager("tom", 2000, 1000);
/*
思路分析
1.test()方法可以接受一个Employee对象,因为Worker和Manage都是Employee的子类,所以可以接受参数
2.因为在调用方法的时候,看的是运行类型,因此我们传入一个worker对象,那么就会动态绑定到Worker类中的getAnnual()方法
如果传入的是,Manager,那么就会动态绑定到Manager中
*/
test(jack);
test(tom);
/*
思路分析
1.testWork()方法可以接受一个Employee对象,因为Worker和Manage都是Employee的子类,所以可以接受参数
2.这次我们要调用的是子类中的特有属性,因此要对传入的参数进行判断,如果e的类型是Worker,那么就强转,相当于向下转型,并且会去调用对应的方法
*/
testWork(jack);
testWork(tom);
}
//该方法可以接受一个Employee 对象
public static void test(Employee e) {
System.out.println(e.getAnnual());
}
//该方法可以接受一个Employee 对象
public static void testWork(Employee e) {
if (e instanceof Worker) {
((Worker) e).work();
} else if (e instanceof Manager) {
((Manager) e).manage();
} else {
System.out.println("不做任何处理 ");
}
}
}
employee类
package idea.polymorphic.polyparameter_;
public class Employee {
private String name;
private double salary;
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
//得到年工资的方法
public double getAnnual() {
return 12 * salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
Manager类
package idea.polymorphic.polyparameter_;
public class Manager extends Employee {
private double bonus;
public Manager(String name, double salary, double bonus) {
super(name, salary);
this.bonus = bonus;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
public void manage() {
System.out.println("经理 " + getName() + " is managing");
}
//重写获取年薪方法
@Override
public double getAnnual() {
return super.getAnnual() + bonus;
}
}
Worker类
package idea.polymorphic.polyparameter_;
public class Worker extends Employee {
public Worker(String name, double salary) {
super(name, salary);
}
public void work() {
System.out.println("普通员工 " + getName() + " is working");
}
@Override
public double getAnnual() { //因为普通员工没有其它收入,则直接调用父类方法
return super.getAnnual();
}
}