类、超类和子类

定义子类

可以用如下继承Employee类来定义Manager类,这里使用关键字extend表示继承。

public class Manager extends Employee{
	*added methods an fields*
}

关键字extends表示正在构建的新类派生于一个已存在的类。这个已存在的类称为超类或者父类;新类称之为子类或者派生类。
尽管Employee类是一个超类,但并不是因为他优于子类或者拥有比子类更多的功能。实践上恰恰相反反而是子类比超类拥有的功能更多。
在Manager类中增加了一个存储信息的字段,以及一个用于设置这个字段的新方法:

public class Manager extend Employee{
	private double bonus;
	...
	public void setBonus(double bonus){
	this.bonus=bonus;
	}
}

这里定义的方法和字段并没有什么特别之处。如果有一个manager对象,就可以使用setBonus方法。

Manager boss=...;
boss.setBonus=bonus;

当然由于setBonus不是在Employee上定义的,所以属于Employee类的对象不能使用它。
然而尽管Manager类中没有显式的定义getName和getHireDay等方法,但是可以对Manager对象使用这些方法,这是因为Manager类自动继承了超类Employee类的这些方法。
类似的,从超类继承的name、salary、hireDay和bonus。
通过拓展超类定义子类的时候,只需要指出子类和超类的不同之处。因此在设计类的时候,应该将最一般的方法设置到超类中,从而将更特殊的方法放在子类中,这种将通用功能抽取到超类的做法在面向对象程序设计中十分普遍。

覆盖方法

超类中有一些方法对子类不一定适用。具体来说,对Manager类中的getSalary方法应该返回薪水和奖金的总和。为此,需要提供一个新的方法来覆盖超类中的这个方法:

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

应该如何实现这个方法呢?乍一看非常的简单,只需要返回salary和bonus的总和就行了:

public double getSalary(){
return salary+bonus; //无法执行
}

不过那样是不行的。回想一下,只有Employee方法能够直接访问Employee的私有变量。这意味着,Manager类无法调用那些私有变量,除非像所有其他方法一样使用公共接口,在这里就是要使用Employee类中的公共方法getSalary。
现在再试一下。我们需要调用getSalary方法而不是直接访问salary字段:

public duoble getSalary{
	double baseSalary = getSalary();//依然是无法执行
	return baseSalary+bonus;
}

上面这段代码会继续报错。问题出现在getSalary上,它只是在调用自身,这是因为Manager类也有一个getSalary方法(就是我们现在在实现的这个方法),所以这条语句将无限次调用自己,直到程序崩溃。
所以我们就需要关键字super来调用超类Employee的getSalary方法而不是子类Manager类的方法:

super.getSalary

下面是使用后正确调用getSalary的版本

public duoble getSalary{
	double baseSalary = super.getSalary();
	return baseSalary+bonus;
}

正像前面所看到的那样,在子类可以增加字段,增加方法或者覆盖超类的方法,不过,继承绝不会删除任何字段或者方法。

子类构造器

在例子的最后,我们来提供一个构造器

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

这里的关键字super具有不同的意义。语句super (name,salary,year,month,day);是“调用超类中带有n,s,year,month,day参数的构造器”的简写形式。
由于Manager类的构造器不能访问Employee类的私有字段,所以必须通过一个构造器来初始化这些私有字段。可以利用特殊的super语法调用这个构造器。使用super调用构造器的语句必须是子类构造器的第一条语句。
如果子类的构造器没有显式的调用超类的构造器,将自动调用超类的无参数构造器。如果超类没有的无参数构造器,子类的构造器也没有显式的调用超类的构造器,Java编译器就会报告一个错误。
重新定义Manager对象的getSalary方法之后,奖金就会自动地添加到经理的薪水中。
下面给出一个例子来说明这个类的使用。我们要创建一个新经理,并设置他的奖金:

Manager boss=new Manager("Corlone",80000,2001,08,04)
boss.setBonus(5000);

下面定义一个包含三个员工的数组:

var staff=new Employee[3];

在数组中混合填入经理和员工;

staff[0]=boss;
staff[1] = new Employee("Harry", 50000, 1990, 10, 1);
staff[2] = new Employee("Tom", 40000, 1997, 3, 15);

输出每个人的薪水:

for(Employee e:staff)
	 System.out.println("name:" + e.getName() + ",salary:" + e.getSalary());

运行这条循环语句将会输出以下数据:

name:Corlone,salary: 85000.0
name:Harry,salary: 50000.0
name:Tom ,salary:40000.0

这里的staff[1]和staff[2]只输出了基本薪水,这是因为它们是Employee对象,而staff[0]是一个Manager对象,它的getSalary方法会将奖金与基本薪水相加。 需要提醒的是,以下调用 e.getSalary()能够选出执行的正确getSalary方法;当e声明为Employee类型,但实际上e既能够引用Employee类型的对象,也可以引用Manager类型的对象。
当e引用Employee的对象时,e.getSalary调用的是Employee类的getSalary()方法;当e引用Manager的对象时,e.getSalary调用的是Manager类的getSalary()方法。虚拟机知道e实际上引用的对象类型,因此能够正确的调用相应的方法。
一个对象变量(例如e)可以指示多种实际类型的现象叫做多态。在运行时能够自动地选择适当的方法,称之为动态绑定。
程序清单1展示Employee对象(见程序清单2)与Manager对象(见程序清单3)在薪水上计算的区别。

程序运行清单1

public  class Manager
 {
  public static void main(String[] args)
   {
  	Manager boss=new Manager("Corlone",80000,2001,08,04)
	boss.setBonus(5000);
	var staff=new Employee[3];
	staff[0]=boss;
	staff[1] = new Employee("Harry", 50000, 1990, 10, 1);
	staff[2] = new Employee("Tom", 40000, 1997, 3, 15);
	//打印内容
	for(Employee e:staff)
	 	System.out.println("name:" + e.getName() + ",salary:" + e.getSalary());
   }
 }

程序运行清单2

import java.time.*;
public  class Employee {
        private String name;
        private double salary;
        private LocalDate hireDay;

        public Employee(String n,double s,int year,int month,int day){
            name = n;
            salary = s;
            hireDay = LocalDate.of(year,month,day);
        }

        public String getName()
        {
            return name;
        }
        public double getSalary()
        {
            return salary;
        }
        public LocalDate getHireDay()
        {
            return hireDay;
        }
        public void raiseSalary(double byPercent){
            double raise =salary*byPercent/100;
            salary+= raise;
        }
    }

程序运行清单3

public class Manager extend Employee
{
	private double bonus;
	public Manager(String name,double salary,int year,int month,int day)
	{
		super (name,salary,year,month,day);
		bonus=0;
	}
	public double getSalary()
	{
		double baseSalary = super.getSalary();
		return baseSalary+bonus;
	}
	public void setBonus(double bonus)
	{
		this.bonus=bonus;
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值