【MC开发日记】

【MC开发日记】,第1张

MC开发日记–Java基础(一):多态

文章目录
  • MC开发日记--Java基础(一):多态
  • 前言
  • 一、什么叫多态?
  • 二、调用顺序:
    • 1."is a"原则:
    • 2.函数表
  • 补充


前言

本系列的内容大都是源自《Java核心技术》第10版(以下称《核心技术》)以及菜鸟教程中的内容


以下是本篇文章正文内容,下面案例可供参考

一、什么叫多态?

  多态发生在父类对象指向子类对象时,调用共有接口时,此时编译器会决定哪一个接口会被真正调用:换言之,当且仅当:
  继承(inheritance)
  覆写(override)
  父对象指向子对象时会出现

  用《核心技术》中的例子来进行说明:

//父类:Employee类
class Employee{
    // 静态域
    private static int nextId = 0;

    // 实例域
    private String name = "Employee";
    private double salary = 0;
    private Date hireDay = new Date(2020, 1, 1);
    private int id = 0;

    public Employee(String str, double sal, int year, int month, int day){
        name = str;
        salary = sal;
        hireDay = new Date(year, month, day);
    }
    //返回雇员的薪水
    public double getSalary(){return salary;}
}

//子类:Manager类
public class Manager extends Employee{			//A extends B表示A类继承了B类
    private double bonus = 0;					//子类中独有的属性
    public void setBonus(double aBonus){		//子类中独有的方法
        bonus = aBonus;
    }

    @Override
    public double getSalary(){					//覆写父类中的getsalary方法
        double baseSalary = super.getSalary();
        System.out.println("Manager's salary");
        return bonus + baseSalary;
    }

    public Manager(String aName, double aSal, int aYear, int aMonth, int aDay){
        super(aName, aSal, aYear, aMonth, aDay);
    }
}

public class EmployeeTest {
    public static void main(String[] args){
    	//在这种情况下,Manager类只能算是“继承”了Employee,多态还没有发生
    	Manager manager = new Manager("Alice", 15000, 1999, 2, 21);
    	Employee employee = new Manager("Alice", 15000, 1999, 2, 21);
    	employee.getSalary();
    }
}

  可以看到,在Employee类和Manager类中都存在*getSalary()*方法,当我们以父类对象变量调用该方法时:

  最终调用的结果为子类(Manager类)的对应方法,这里就可以看到,尽管允许将子类对象赋值给父类对象,但调用方法时,以该对象本身的类型为基准

二、调用顺序: 1."is a"原则:

  这一节开始深入理解上面的现象,这也有助于更好地理解和运用多态。在《核心技术》中指出,继承关系可以用"is a"进行描述,Manager一定是一个Employee,这种想法能够迅速理解继承的关系。根据"is a"原则,很容易就能说明为什么能用Employee对象变量指向Manager
  同时,由继承关系知,从抽象层次来看,显然Employee包含了Manager,但从属性、方法的数量上看,又可以看成Manager扩充了Employee

2.函数表

  从第一部分的结果来看,可以得到两个信息:
  1. 对象本身的类型与对象变量的类型不一定非得相同(Manager类对象可以被Employee对象变量引用
  2.上一条成立的条件是对象变量的类型是父类,而对象本身是子类
  那么编译器如何知道父类对象变量调用有重载的成员函数时,到底该调用哪一个呢?
  事实上,Java编译器维护被称为方法表的结构,该结构从当前对象本身的类型所拥有的成员函数开始构造,一直构造到基类。当函数调用时,编译器自底向上地从当前类开始查找,一旦在当前类中找到符合要求的函数(符合要求指:与调用函数有相同的函数签名并且被标记为public),直接调用,否则到父类中继续该过程,如果始终没有找到则抛出异常
  例如例子中的两个类在方法表中可以看作有这样的结构:

class Employee:
	getSalary() -> Employee.getSalary(),
	...
class Manager:
	getSalary() -> Manager.getSalary(),
	...

  然后自底向上地进行查找,就能够得到刚才的结果。


补充

函数签名

  根据《核心概念》,在Java中,签名被定义为由方法名参数列表组成的,方法的唯一标识符。注意,返回类型并不算在函数签名中。如果在子类中定义了父类中的函数,那么该签名会覆写父类中的签名。
@Override注解
  @Override并不是必要的,然而它可以帮助编译器检查错误。当方法前面用@Override注解后,父类中必须有该方法以供覆写否则会报错。而没有@Override时,显然即使父类中没有该方法,编译器也仅仅会认为这是一个普通函数,尽管程序正常运行,但却丢失了重载的性质。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/729425.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-04-27
下一篇2022-04-27

发表评论

登录后才能评论

评论列表(0条)

    保存