Java核心技术第5章(1)

第5章   继承

    本章将学习面向对象程序设计的一个基本概念:继承. 继承已存在的类就是复用这些类的方法和域.在此基础上,可以添加一些新的方法和域,以满足新的需求.
    此外,本章还阐述反射的概念.反射是指在程序运行期间发现更多的类及其属性的能力.

5.1 类,超类和子类

    Manager可以重用Employee类中已经编写的部分代码,并将其中的所有域保留下来.
    下面是由继承Employee类来定义Manager类的格式, 关键字 extends 表示继承.
class Manager extends Employee
{
    添加方法和域
}
    注释:Java与C++定义继承类的方式十分相似.Java用关键字 extends 代替了C++中的冒号(:). 在Java中,所有的继承都是公有继承,而没有C++中的私有继承和保护继承.
    关键字 extends 表明正在构造的新类派生于一个已存在的类.
已存在的类称为超类(superclass),基类(base class)或父类(parent class);新类称为子类(subclass),派生类(derived class)或孩子类(child class).超类和子类是Java程序员最常用的两个术语.
    注释:前缀"超"和"子"来源于计算机科学和数学理论中的集合语言的术语.所有雇员组成集合包含所有经理组成的集合.可以这样说,雇员集合是经理集合的超集,也可以说,经理集合是雇员集合的子集.
    在Manager类中,增加了一个用于存储奖金信息的域,以及一个用于设置这个域的方法:
class Manager extends Employee
{
    private double bonus;
    ...
    public void setBonus(double d)
    {
        bonus = d;
    }
}
    如果有一个Manager类就可以使用setBonus方法.
    在通过扩展超类定义子类的时候,仅需要指出子类与超类的不同之处.因此在设计类的时候,应该将通用的方法放在超类,而将具有特殊用途的方法放在子类中.
    然而,超类中的有些方法对子类Manager并不一定适用.为此需要提供一个新的方法来覆盖超类中的方法.例如:
class Manager extends Employee
{
    ...
    public double getSalary()
    {
        ...
    }
}
    应该如何实现这个方法呢?咋看起来很简单,只要返回salary和bonus域的总和就可以了:
public double getSalary()
{
    return salary + bonus;
}
    然而,这个方法并不能运行.这是因为Manager类的getSalary方法并不能够直接访问超类的私有域.也就是说,尽管每一个Manager对象都拥有一个名为salary的域,但在Manager类的 getSalary 方法中并不能够直接访问 salary域.只有Employee类的方法才能访问私有部分.如果Manager类的方法一定要访问私有域,就必须借助于公有的接口,Employee类中的公有部分getSalary正是这样一个接口.
public double getSalary()
{
    double baseSalary = getSalary();
    return baseSalary + bonus;
}
    上述代码仍然不能运行,问题出现在调用getSalary的语句上,这是因为Manager类也有一个 getSalary方法.
    这里需要指出: 如果希望调用超类Employee的getSalary方法而不是当前类的这个方法的,可以使用特定的关键字 super 解决这个问题:
super.getSalary();
    上述语句调用的是Employee类的getSalary方法. 下面是Manager类中getSalary方法的正确书写格式:
public double getSalary()
{
    double baseSalary = super.getSalary();
    return baseSalary + bonus;
}
    注释:有些人认为 super 与 this 引用是类似的概念,实际上,这样比较并不太恰当.这是因为 super 不是一个对象的引用,不能将 super 赋给另一个对象变量,它只是一个只是编译器调用超类方法的特殊关键字.
    注释:在Java中使用关键字 super 调用超类的方法,而在C++中则采用超类名加上::操作符的形式.例如,在Manager类的getSalary方法中,应该将 super.getSalary替换为
Employee::getSalary.
    最后,看一下 super 在构造器中的应用.
public Manager(String n, double s, int year, int month, int day)
{
    super(n, s, year, month, day);
    bonus = 0;
}
    这里的关键字 super 具有不同的含义.语句
super(n, s, year, month, day);
    是"调用超类Employee中含有n,s,year,month,day参数的构造器"的简写形式.
    由于Manager类的构造器不能访问Employee类的私有域,所以必须利用Employee类的构造器对这部分私有域进行初始化,可以通过 super 实现对超类构造器的调用.使用 super 调用构造器的语句必须是子类构造器的第一条语句.
    如果子类的构造器没有显式调用超类的构造器,则将自动地调用超类默认的构造器.如果超类没有不带参数的构造器,并且在子类的构造器又没有显式地调用超类的其他构造器,则Java编译器将报告错误.
    注释:关键字 this 有两个用途:一是引用隐式参数,二是调用该类其他的构造器.同样, super 关键字也有两个用途:一是调用超类的方法,二是调用超类的构造器.在调用构造器的时候,这两个关键字的使用方式很类似.调用构造器的语句只能作为另一个构造器的第一条语句出现,构造参数既可以传递给本类(this)的其他构造器,也可以传递给超类(super)的构造器.
    注释:在C++的构造函数中,使用初始化列表语法调用超类的构造函数,而不调用 super .在C++中,Manager的构造函数如下所示:
Manager::Manager(String n, double s, int year, int month, int day)
    : Employee(n, s, year, month, day)
{
    bonus = 0;
}
    重定义Manager对象的getSalary方法后,建立一个Manager对象
Manager boss = new Manager("Carl Cracker", 8000, 1999, 1, 20);
boss.setBonus(5000);
    下面定义一个包含2个雇员的数组
Employee[] staff = new Employee[2];
staff[0] = boss;
staff[1] = new Employee("harry", 200, 1999, 1, 10);
for(Employee e : staff)
    System.out.printlne(e.getName() + " " + e.getSalary());
    这里staff[1]输出基本的薪水,它对应的是Employee对象,而staff[0]对应的是Manager对象,它的getSalary方法将奖金与基本薪水加在一起了.
    注意,e.getSalary()调用能够确定应该执行哪个getSalary方法, 尽管这里将e声明为Employee类型,但实际上e既可以引用Employee类型的对象,也可以引用Manager类型的对象.
    虚拟机知道e实际引用的对象类型,因此能够正确地调用相应的方法.
    一个对象变量(例如,变量e)可以指示多种实际类型的现象被称为多态.在运行时能够自动地选择调用哪个方法的现象称为动态绑定(dynamic binding).
    注释:在Java中不需要将方法声明为虚拟方法. 动态绑定是默认的处理方式.如果不希望让一个方法具有虚拟特征,可以将它标记为 final .
    程序ManagerTest.java展示了Employee对象与Manager对象在薪水计算上的区别.
    inheritance/ManagerTest.java如下所示:
package inheritance;

/**
 * This program denonstrates inheritance
 */
public class ManagerTest
{
    public static void main(String[] args)
    {
        // construct a Manager object
        Manager boss = new Manager("Harry", 8000, 1999, 1, 29);
        boss.setBonus(5000);

        Employee[] staff = new Employee[3];
        staff[0] = boss;
        staff[1] = new Employee("Hacker", 5000, 1999, 1, 2);
        staff[2] = new Employee("Tommy", 5000, 1999, 2, 23);

        for (Employee e : staff)
            System.out.println("name = " + e.getName() + ", salary = " + e.getSalary());
    }
}
    inheritance/Employee.java如下所示:
package inheritance;

import java.util.Date;
import java.util.GregorianCalendar;

public class Employee
{
    private String name;
    private double salary;
    private Date hireDay;

    public Employee(String n, double s, int year, int month, int day)
    {
        name = n;
        salary = s;
        GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
        hireDay = calendar.getTime();
    }
    public String getName()
    {
        return name;
    }
    public double getSalary()
    {
        return salary;
    }
    public Date getHireDay()
    {
        return hireDay;
    }
    public void raiseSalary(double byPercent)
    {
        double raise = salary * byPercent / 100;
        salary += raise;
    }
}    
    inheritance/Manager.java如下所示:
package inheritance;

public class Manager extends Employee
{
    private double bonus;
    /**
     * @param n the employee's name
     * @param s the salary
     * @param year the hire year
     * @param month the hire month
     * @param day the hire day
     */
    public Manager(String n, double s, int year, int month, int day)
    {
        super(n, s, year, month, day);
        bonus = 0;
    }
    public double getSalary()
    {
        double baseSalary = super.getSalary();
        return baseSalary + bonus;
    }
    public void setBonus(double b)
    {
        bonus = b;
    }
}
    注意,在包inheritance所在的目录下编译,并且使用javac inheritance\ManagerTest.java进行编译,使用java inheritance.ManagerTest 执行结果,结果如下所示:
   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值