Java核心技术第5章(1)

来源:互联网 发布:北大青鸟java专业学费 编辑:程序博客网 时间:2024/05/17 01:16

第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
原创粉丝点击