Java核心技术第5章(1)
2015-10-08 10:19
721 查看
第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;注意,在包inheritance所在的目录下编译,并且使用javac inheritance\ManagerTest.java进行编译,使用java inheritance.ManagerTest 执行结果,结果如下所示:
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;
}
}
相关文章推荐
- java 反射机制
- Java学习 9.25
- [java学习笔记]java语言核心----面向对象基础
- Java学习 2015.9.24
- 我理解的IOC技术在Java和C#中比较分析
- 二叉排序树转化为顺序双链表 JAVA实现
- JavaCore视频的一些读书心得
- java.lang.UnsupportedClassVersionError: com/wxm/framework/listener/SessionLi
- MyEclipse使用总结——MyEclipse10安装SVN插件
- Java深入理解文章(转载)
- 第二章 Spring MVC入门 —— 跟开涛学SpringMVC
- 用eclipse maven 创建多模块 maven web 项目
- spring ioc
- Hibernate 入门教程(纯Eclipse版)
- eclipse 指定使用jdk
- 将带图片的Java工程用eclipse打包成jar,然后用exe4j将jar打包成exe,并附带jre库
- jdk1.7 windows
- 实用的java代码片段
- Android studio 工程迁移到Eclipse
- Java生成和导出Excel文件