多态
先看一个问题
问题描述 : com.edu.poly_ : Poly01.java (polymorphic: 多态的)
请编写一个程序 ,Master类 中有一个feed(喂食)方法 ,可以完成 主人给动物喂食物的信息
分析 :首先要有三个类 ,分别是食物类 主人类 动物类 ,其次 食物类分支有 : 鱼肉类 骨头类 米饭类 , 动物类分支 的有 猫猫类 狗狗类 猪猪类
-
首先用传统的思维方式解决 (private属性)
编写代码之后我们可以发现创建一个feed方法 ,里面传入的是对象 ,如果是小狗吃骨头, 就可以把骨头类和小狗类传入
-
传统的方法带来的问题是什么? 如何解决?
代码的问题在于 ,以上的feed方法 ,如果出现了多种动物和食物 ,同时每种动物吃的食物不同 ,想要喂食所有的动物的话, 就会不断地重复编写很多遍feed方法(代码复用性不高 ,不利于代码的维护)
提出的解决方法 : 多态
●多态基本介绍 (多态:多种状态)
方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承基础 的。
●多态的具体体现
-
方法的多态 PloyMethod.java 重写和重载就体现多态
-
对象的多态(核心)
重要的几句话:
(1) 一个对象的编译类型和运行类型可以不一致
(2) 编译类型在定义对象时,就确定了,不能改变
(3) 运行类型是可以变化的.
(4) 编译类型看定义时 = 号 的左边,运行类型看 = 号的 右边
案例 : com.edu.poly.objpoly_ : PolyObject.java
Animal animal = new Dog(); [animal 的编译类型是Animal, 运行类型是Dog]
Animal = new Cat(); [animal的运行类型变成了Cat ,编译类型仍然是Animal]
●多态注意事项和细节讨论
com.edu.poly_.detail 包:PolyDetail.java
-
多态的前提是:两个对象(类)存在继承关系
多态的向上转型
-
本质:父类的引用指向了子类的对象
-
语法: 父类类型 引用名 = new 子类类型();
-
特点:编译类型看左边,运行类型看右边。
可以调用父类中的所有成员(需遵守访问权限)
不能调用子类中特有成员
最终运行效果看子类的具体实现!
//可以调用父类中的所有成员(需遵守访问权限)
//animal.catchMouse();调用这个方法就会报错
//但是不能调用子类的特有成员,例如私有方法
//因为编译阶段,能够调用哪些成员 是由编译类型决定的
//最终运行效果看子类的具体实现!
animal.eat();//调用方法的时候从子类开始查找方法,然后调用规则和前面讲的方法调用规则一致
//也就是运行类型
animal.run();
//先从子类找没有run方法,然后到父类找找到了就使用父类的
animal.show();
animal.sleep();
多态的向下转型
-
语法:子类类型 引用名 =(子类类型)父类引用;
-
只能强转父类的引用,不能强转父类的对象
-
要求父类的引用必须指向的是当前目标类型的对象
-
当向下转型之后 ,就可以调用子类类型中所有的成员
//希望可以调用Cat的抓猫的方法
//多态的向下转型
//此时如果是向上转型的animal的话就无法调用该方法
//子类类型 引用名 =(子类类型)父类引用;
Cat cat = (Cat) animal;
//将父类的引用强制转换为子类的引用
//此时编译类型是Cat引用类型也是Cat
cat.catchMouse();
//要求父类的引用必须指向的是当前目标类型的对象
//Dog dog = (Dog) animal;
//这样的写法是错误的,因为animal指向的堆空间为Cat对象(目标类型的对象为Cat)
●多态注意事项和细节讨论
-
属性没有重写之说!属性的值看编译类型 PolyDetail02.java
-
instanceOf 比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型;[举例说明] PolyDetail03.java
package com.edu.poly_.detail;
public class PolyDetail02 {
public static void main(String[] args) {
//属性没有重写之说!属性的值看编译类型
Base base = new Sub();//向上转型
System.out.println(base.count);// 这里和方法不一样, 直接看编译类型
//编译类型为Base因此就是Base类, count打印的值就是10
Sub sub = new Sub();
System.out.println(sub.count);//编译类型为Sub,因此输出结果 20
}
}
class Base { //父类
int count = 10;//属性
}
class Sub extends Base {//子类
int count = 20;//属性
}
package com.edu.poly_.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();
System.out.println(aa instanceof AA);
System.out.println(aa instanceof BB);
Object obj = new Object();
System.out.println(obj instanceof AA);//false
String str = "hello";
//System.out.println(str instanceof AA);
System.out.println(str instanceof Object);//true
}
}
class AA {}//父类
class BB extends AA {}//子类
多态的练习
package com.edu.poly_.exercise_;
public class PolyExercise01 {
public static void main(String[] args) {
double d = 13.4;
long l = (long)d;
System.out.println(l);
int in = 5;
//boolean b = (boolean)in;
//Boolean类型不能转换为int
Object obj = "Hello";
//向上转型
String objStr = (String)obj;
//再把obj转换为字符串也是可以的,这属于向下转型
System.out.println(objStr);
Object objPri = new Integer(5);
//可以,这是向上转型
String str = (String)objPri;
//错误 ,objPri指向Integer父类引用,如果强制进行转换指向String的话就会报错
Integer str1 = (Integer)objPri;
//向下转型将objPri转为Integer类型,因为objPri指向Integer所以是正确的
}}
package com.edu.poly_.exercise_;
public class PolyExercise02{
public static void main(String[] args){
Sub s = new Sub();
//s指向对象Sub(其中包含父类count10和子类count20)
System.out.println(s.count);
//访问属性看编译类型属性为s因此输出子类的count 20
s.display();
//执行Sub的运行类型方法输出10
Base b = s;
//向上转型 子类引用指向父类的引用
System.out.println(b == s);
//由于父类引用子类引用都指向同一块空间,因此输出true
System.out.println(b.count);
//b的编译类型为Base因此输出Base的count 10
b.display();
//方法的调用要看b的运行类型 ,b的运行空间为Sub
//因此输出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);
}
}
java的动态绑定机制
Java 重要特性: 动态绑定机制 DynamicBinding.java com.edu.poly_.dynamic_
Dynamic(动态的)Binding(捆绑,绑定)
package com.edu.poly_.dynamic_;
public class DynamicBinding {
public static void main(String[] args) {
A a = new B();//向上转型
System.out.println(a.sum());
System.out.println(a.sum1());
//执行方法看运行类型,运行类型为B()
//因此输出的结果为40 30
}
}
class A{//父类
public int i = 10;
public int sum(){
return getI()+ 10;
}
public int sum1(){
return i+10;
}
public int getI(){
return i;
}
}
class B extends A{
public int i = 20;
public int sum(){
return i + 20;
}
public int getI(){
return i;
}
public int sum1(){
return i + 10;
}
}
假设我们将子类的B类中的sum方法去除掉 ,再去执行主类中的 System.out.println(a.sum()); 先找运行方法即子类的B 如果找不到,就会开始执行父类A的同名方法 ,而父类的sum方法中使用了 geti方法 ,而geti方法父类和子类都有 ,因此就需要使用到动态绑定了 ,由于和运行类型进行绑定了 ,因此使用的还是运行类型的方法 ,也就是getI ,因此最终的结果就是返回20 ,输出为30
同样的去除掉B类的sum1方法去除掉 ,执行主类 的System.out.println(a.sum1()); 此时就会执行到父类的A中去 ,A的sum1方法中为i + 10 ,这个i作为属性 ,就会依照哪里声明哪里使用的原则 ,因此使用的是A类的i ,因此执行的结果就是20
java的动态绑定机制
-
当调用对象方法的时候,该方法会和该对象的 内存地址/运行类型 进行绑定
-
当调用对象属性时没有动态绑定机制,哪里声明,那里使用
多态的应用
-
多态数组
com.edu.poly_.polyarr_ 包 PloyArray.java数组的定义类型为父类类型,里面保存的实际元素类型为子类类型应用实例:现有一个继承结构如下:要求创建 1 个 Person 对象、2 个 Student 对象和 2 个 Teacher 对象, 统一放在数组 中,并调用每个对象的 say 方法.
代码:
package com.edu.poly_.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;
}
}
package com.edu.poly_.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...");
}
}
package com.edu.poly_.polyarr_;
public class PloyArray {
public static void main(String[] args) {
//应用实例:现有一个继承结构如下:要求创建 1 个 Person 对象、
// 2 个 Student 对象和 2 个 Teacher 对象, 统一放在数组中,并调用每个对象 say 方法
Person[] persons = new Person[5];
//需要使用父类类型来去接收,父类的引用可以指向对象
persons[0] = new Person("jack",20);
persons[1] = new Student("jack",18,100);
persons[2] = new Student("smith",19,30.1);
persons[3] = new Teacher("scott",30,20000);
persons[4] = new Teacher("king",50,25000);
//循环遍历多态数组 ,调用say方法
for (int i = 0; i < persons.length; i++) {
System.out.println(persons[i].say());
//这里会有一个动态绑定机制
//persons[i]编译类型是Person运行类型根据实际情况由JVM进行判断
}
//循环遍历多态数组 ,调用say方法
for (int i = 0; i < persons.length; i++) {
System.out.println(persons[i].say());
//这里会有一个动态绑定机制
//persons[i]编译类型是Person运行类型根据实际情况由JVM进行判断
//由于编译类型是Person因此无法调用子类的方法
//使用类型判断+向下转型的方式解决
if (persons[i] instanceof Student){
//可以通过instanceof判断person[i]运行类型是否是Student
((Student) persons[i]).study();
//等同于Student student = (Student) person;
//也就是一种向下转型
}else if (persons[i] instanceof Teacher){
//同样的使用instanceof进行判断
((Teacher) persons[i]).teach();
}else if (persons[i] instanceof Person){
//如果类型是Person不做任何处置
}else {
System.out.println("类型有误,请自己检查");
}
}
}
}
输出结果 :
jack 20
学生 jack 18 score=100.0
学生 smith 19 score=30.1
老师 scott 30 salary=20000.0
老师 king 50 salary=25000.0
应用实例升级:如何调用子类特有的方法,比如 Teacher 有一个 teach , Student 有一个 study 怎么调用?
package com.edu.poly_.polyarr_;
public class PloyArray {
public static void main(String[] args) {
//应用实例:现有一个继承结构如下:要求创建 1 个 Person 对象、
// 2 个 Student 对象和 2 个 Teacher 对象, 统一放在数组中,并调用每个对象 say 方法
Person[] persons = new Person[5];
//需要使用父类类型来去接收,父类的引用可以指向对象
persons[0] = new Person("jack",20);
persons[1] = new Student("jack",18,100);
persons[2] = new Student("smith",19,30.1);
persons[3] = new Teacher("scott",30,20000);
persons[4] = new Teacher("king",50,25000);
//循环遍历多态数组 ,调用say方法
for (int i = 0; i < persons.length; i++) {
System.out.println(persons[i].say());
//这里会有一个动态绑定机制
//persons[i]编译类型是Person运行类型根据实际情况由JVM进行判断
//由于编译类型是Person因此无法调用子类的方法
//使用类型判断+向下转型的方式解决
if (persons[i] instanceof Student){
//可以通过instanceof判断person[i]运行类型是否是Student
((Student) persons[i]).study();
//等同于Student student = (Student) person;
//也就是一种向下转型
}else if (persons[i] instanceof Teacher){
//同样的使用instanceof进行判断
((Teacher) persons[i]).teach();
}else if (persons[i] instanceof Person){
//如果类型是Person不做任何处置
}else {
System.out.println("类型有误,请自己检查");
}
}
}
}
多态参数
方法定义的形参为父类类型 ,实参类型允许为子类类型
应用实例1:前面的主人喂食物
应用实例2:com.edu.poly_.polyparameter_包(参数) Polyparameter.java
定义员工类Employee ,包含姓名和月工资[private] ,以及计算年工资 getAnnual的方法 ,普通员工和经理继承了员工 ,经理类多了奖金bonus属性和管理manage的方法 ,普通员工类多了work方法 ,普通员工和经理类分别要求重写getAnnual方法
测试类中添加一个方法showEmAnnual(Employee e) ,实现获取任何员工对象的年工资 ,并在main方法中调用该方法 [e.getAnnual()]
测试类中添加一个方法 ,testWork ,如果是普通的员工 ,则调用work方法 , 如果是经理 ,则调用manage方法
package com.edu.poly_.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 salary * 12;
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public void setName(String name) {
this.name = name;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
package com.edu.poly_.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;
}
}
package com.edu.poly_.polyparameter_;
public class PloyParameter {
public static void main(String[] args) {
Worker tom = new Worker("tom", 2500);
Manager milan = new Manager("milan", 5000, 200000);
PloyParameter ployParameter = new PloyParameter();
ployParameter.showEmAnnual(tom);
ployParameter.showEmAnnual(milan);
ployParameter.testWork(tom);
ployParameter.testWork(milan);
}
//添加一个方法showEmAnnual(Employee e) ,实现获取任何员工对象的年工资
public void showEmAnnual(Employee e){
System.out.println(e.getAnnual());
}
//添加一个方法 ,testWork ,如果是普通的员工 ,则调用work方法 , 如果是经理 ,则调用manage方法
public void testWork(Employee employee){
if (employee instanceof Worker){
((Worker) employee).work();//向下转型的操作
}else if (employee instanceof Manager){
((Manager) employee).manage();
}else {
System.out.println("传参的类不是员工或者经理");
}
}
}
package com.edu.poly_.polyparameter_;
public class Worker extends Employee{
public Worker(String name, double salary) {
super(name, salary);
}
public void work(){
System.out.println("员工:"+getName() + "正在工作....");
}
@Override
public double getAnnual() {
//因为普通员工没有其他收入,因此可以直接调用父类的方法
return super.getAnnual();
}
}

浙公网安备 33010602011771号