Java核心技术(四) —— 继承(1)

来源:互联网 发布:android编程实战 pdf 编辑:程序博客网 时间:2024/06/07 20:19

继前面我们对类和对象进行系统学习之后,我们来继续学习OOP的另一个基本概念:继承。利用继承,可以基于已存在的类构造一个新类。继承已存在的类就是复用(继承)这些类的方法和域,在此基础上,可以添加一些新的方法和域,以满足新的需求。
此外,我们也将介绍反射的概念。反射即在程序运行期间发现更多的类及其属性的能力,不过这个强大的特性更吸引开发软件工具人员的关注,编写应用程序的人员则不太关注,所以我们粗略介绍下。


1、类、超类和子类

Java中使用关键字extends表示继承,下例即表示由继承Employee类来定义Manager类的格式

class Manager extends Employee
{
添加方法和域
}

关键字extends表明正在构造的新类Manager派生于一个已存在的类Employee。已存在的类称为超类、基类或者父类,新类称为子类、派生类或者孩子类。

好玩的是,虽然Employee类是一个超类,但并不表示其位于子类以上或者拥有比子类更多的功能,恰恰相反,子类比超类拥有的功能更丰富,即封装了更多的数据拥有更多的方法。

(1)超类Employee的对象是没法使用子类Manager的方法的。反之,尽管子类Manager中没有显示定义某些超类Employee已有的方法,但前者对象仍可使用这些方法。同样,子类Manager的对象还包含超类Employee拥有的所有域。

(2)通过扩展超类定义子类时,仅需要指出子类与超类的不同之处,即设计类的时候,将通用的方法放在超类中,仅将具有特殊用途的方法放在子类中。

(3)注意前面说子类Manager的对象还包含超类Employee拥有的所有域,但前者的方法不能够直接访问后者的私有域,如要访问,必须借助与公有的接口。
举个例子,有时超类中的有些方法对子类并不一定适用,这时需要在子类中定义一个新的方法来覆盖超类中的这个方法,例如getSalary方法

class Manager extends Employee{    ···    public double getSalary()    {        double baseSalary = super.getSalary();        return baseSalary + bonus;    }    ···}

上述代码段有个值得注意的点:需要使用特定的关键字super来调用超类Employee中的getSalary方法。

(4)可见在子类中可以增加域、增加方法或者覆盖超类中的方法,但绝不能删除继承的任何域和方法。

(5)此外,关键字super在子类的构造器中也有用处。
由于子类的构造器不能访问超类的私有域,所以必须使用超类的构造器对这部分私有域进行初始化,可以通过super实现对超类构造器的调用,且使用super调用构造器的语句必须是子类构造器的第一条语句。如

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

上述代码段中的super(n,s,year,month,day);是调用超类Employee中含有参数n,s,year,month和day的构造器的简写形式。
如果子类的构造器没有显式调用超类的构造器,则将自动调用超类的默认构造器。

1.1 继承层次

继承并不仅限于一个层次,由一个公共超类派生出来的所有类的集合称为继承层次,从某个特定的类到其祖先的路径称为该类的继承链。
注意Java不支持类似C++的多继承。

1.2 多态

一个对象变量可以指示多种实际类型的现象称为多态,在运行时能够自动地选择调用哪个方法的现象称为动态绑定。
(1)通常使用“is-a”规则来判断是否应该设计为继承关系,其表明子类的每个对象也是超类的对象。“is-a”规则的另一种表述法是置换法则,它表明程序中程序中出现超类对象的任何地方的都可以使用子类对象予以置换。
(2)Java中的对象变量是多态的,即一个对象变量不仅可以引用原定义类的对象,还可以引用该类的任何一个子类的对象。如
这里写图片描述
当然反之不成立,不能将一个超类的引用赋予子类变量。

1.3 动态绑定

关于对象方法的调用过程:
(1)编译器查看对象的声明类型和方法名
如调用x.f(para),且隐式参数x声明为C类的对象,编译器会一一列举所有C类中名为f的方法和其超类中访问属性为public且名为f的方法。
(2)编译即将查看调用方法时提供的参数类型
(3)如果是private方法、static方法、final方法或者后早期,编译器将可以准确知道调用哪个方法。这种调用称为静态绑定。
与之对应的是,调用的方法依赖于隐式参数的实际类型,即动态绑定。
(4)当程序运行且采用动态绑定时,虚拟机一定调用与x所引用对象的实际类型最合适的那个类的方法

1.4 阻止继承:final类和方法

可以使用final修饰符来声明某个类,从而限制人们扩展这个类,生成子类。
可以适用final声明类中的方法,从而限制子类覆盖这个方法。
当然,可见final类中的所有方法自动地成为了final方法。
但注意,final类中的域没有被限制为final域。

1.5 强制类型转换

这里要讲的是将某个类的对象引用强制转换为另一个类的对象引用。同样也是用圆括号,如将Employee类对象staff[0]强制转换为Manager类型

Manager boss = (Manager) staff[0];

进行类型转换的唯一原因在于:在暂时忽视对象的实际类型之后,使用对象的全部功能。
联系前面所讲,可见将一个超类的引用赋予一个子类变量时,必须进行类型转换。
注意:
(1)只能在继承层次内进行类型转换
(2)在将超类转换成子类之前,应该使用instanceof进行检查

1.6 抽象类

通常来说,在继承层次中,祖先类更加通用,一般作为派生其他类的基类,而不作为想使用的特定的实例类。
对于高层次的抽象超类来说,不仅可以包含具体数据、方法,还可以添加抽象方法,使用abstract关键字声明,当然包含一个或多个抽象方法的类本身需要被声明为抽象的。注意抽象类不能被实例化,但可以定义一个抽象类的对象变量,它只能引用非抽象子类的对象。

这里写图片描述

1.7 受保护访问

可以将超类中的某些方法或者域声明为protected的,从而允许被子类访问。
不过一般要谨慎使用protected属性。

2、Object

Object类是Java中所有类的超类,每个类都由其扩展而来,但并不需要写成

class Employee extends Object

因此,可以适用Object类型的变量引用任何类型的对象。
Java中,只有基本类型不是对象,如数值、字符、布尔值都不是对象。所有的数组类型,不管是对象数组还是基本类型的数组都扩展于Object类。

2.1 equals方法

Object类中的equals方法用于检测两个对象是否具有相同的引用。
在子类中定义equals方法时,首先调用超类的equals,如果检测失败,对象就不可能相等,如果超类中的域都相等,就需要比较子类中的实例域。

2.2 相等测试与继承

2.3 hashCode方法

2.4 toString方法

8 0