《Java核心技术 卷I》学习笔记17:继承


继承是面向对象程序设计的一个基本概念。继承的基本思想是,可以基于已有的类创建新的类。继承已存在的类就是复用(继承)这些类的方法,而且可以增加一些新的方法和字段,使新类能够适应新的情况。

1 定义子类

在定义新类时,使用关键字extends表示继承。例如:

public class Manager extends Employee
{
   
	private double bonus;

	public void setBonus(double bonus)
	{
   
		this.bonus = bonus;
	}
}

关键词extends表明正在构造的新类派生于一个已经存在的类。这个已存在的类称为超类基类父类,新类称为子类派生类。在上面的例子中,Manager类派生于Employee类,Employee类是超类,Manager类是子类。

Manager类中,增加了一个用于存储奖金信息的字段,以及一个用于设置这个字段的新方法。这里定义的方法和字段并没有什么特别之处,如果有一个Manager对象,就可以使用setBonus方法。但是,由于setBonus方法不是在Employee类中定义的,所以Employee类的对象不能使用它。

尽管在Manager类中没有显式地定义getNamegetHireDay等方法,但是可以对Manager对象使用这些方法,因为Manager类自动地继承了超类Employee中的这些方法。

类似地,从超类中还继承了namesalaryhireDay这3个字段。这样一来,每个Manager对象就包含了4个字段:namesalaryhireDaybonus

通过扩展超类定义子类的时候,只需要指出子类与超类的不同之处。因此在设计类的时候,应该将最一般的方法放在超类中,而将更特殊的方法放在子类中。

2 覆盖方法

超类中的有些方法对子类并不一定适用,为此需要提供一个新的方法来覆盖超类中的这个方法。只需要在子类的定义中重写超类中的方法,就可以完成覆盖。例如:

public class Manager extends Employee
{
   
	...
	public double getSalary()
	{
   
		...
	}
	...
}

Employee类中有getSalary方法,在Manager类中重写这个方法就可以覆盖它。

如果超类中的方法被覆盖,又想在子类的定义中调用这个方法,可以使用关键字super。例如Manager类中的getSalary方法可以定义如下:

public double getSalary()
{
   
	double baseSalary = super.getSalary(); // 调用 Employee 类中的 getSalary 方法
	return baseSalary + bonus;
}

super关键字也可以用于访问超类的实例字段,用法与调用超类方法类似。

3 子类构造器

由于子类的构造器不能访问超类的私有字段,所以必须通过超类的构造器来初始化这些私有字段。可以利用super语法调用这个构造器。使用super调用构造器的语句必须是子类构造器的第一条语句。例如Manager类的构造器可以定义如下:

public Manager(String name, double salary, int year, int month, int day)
{
   
	super(name, salary, year, month, day);
	bonus = 0;
}

如果子类的构造器没有显式地调用超类的构造器,将自动调用超类的无参数构造器。如果超类没有无参数构造器,并且在子类中又没有显式地调用超类的其他构造器,Java编译器就会报告一个错误。

4 继承层次

继承并不仅限于一个层次。由一个公共超类派生出来的所有类的集合称为继承层次。在继承层次中,从某个特定的类到其祖先的路径称为该类的继承链。例如:

继承层次
Employee作为公共超类,派生出ManagerSecretaryProgrammer三个子类,Manager类又派生出Executive类。ManagerSecretaryProgrammerExecutive这四个类的祖先都是Employee类,称为Employee类的继承层次。从Executive类到祖先的路径是Executive -> Manager -> Employee,这是Executive类的继承链。

5 多态

里氏替换原则(Liskov Substitution Principle,LSP):程序中出现超类对象的任何地方都可以使用子类对象替换。

在Java中,对象变量是多态的。一个类的变量既可以引用本类对象,也可以引用它的任何一个子类的对象。例如上面的Employee类变量既可以引用Employee类对象,也可以引用ManagerSecretaryProgrammerExecutive类的对象。

当子类覆盖了超类的方法时,虚拟机可以在运行时根据对象变量引用的对象类型正确地调用相应类的方法。

下面用一个例子展示多态性的应用。

/* ManagerTest.java */
package inheritance;

public class ManagerTest
{
   
	public static void main(String[] args)
	{
   
		Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
		boss.setBonus(5000);

		Employee[] staff = new Employee[3];
		staff[0] = boss;
		staff[1] = new Employee("Harry", 50000, 1989, 10
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
某公司雇员(Employee)包括经理(Manager),技术人员(Technician)和销售员(Salesman)。开发部经理(DeveloperManger),既是经理也是技术人员。销售部经理(SalesManager),既是经理也是销售员。 以Employee类为虚基类派生出Manager,Technician和Salesman类;再进一步派生出Developermanager和Salesmanager类。 Employee类的属性包括姓名、职工号、工资级别,月薪(实发基本工资加业绩工资)。操作包括月薪计算函数(pay()),该函数要求输入请假天数,扣去应扣工资后,得出实发基本工资。 Technician类派生的属性有每小时附加酬金和当月工作时数,及研究完成进度系数。业绩工资为三者之积。也包括同名的pay()函数,工资总额为基本工资加业绩工资。 Salesman类派生的属性有当月销售额和酬金提取百分比,业绩工资为两者之积。也包括同名的pay()函数,工资总额为基本工资加业绩工资。 Manager类派生属性有固定奖金额和业绩系数,业绩工资为两者之积。工资总额也为基本工资加业绩工资。 而DeveloperManager类,pay()函数是将作为经理和作为技术人员业绩工资之和的一半作为业绩工资。 SalesManager类,pay()函数则是经理的固定奖金额的一半,加上部门总销售额与提成比例之积,这是业绩工资。 编程实现工资管理。特别注意pay()的定义和调用方法:先用同名覆盖,再用运行时多态。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值