5.1 类,超类和子类
5.1.1 定义子类
- 关键字 extends 表明正在构造的新类派生自一个已经存在的类
- 已存在的类被称为 超类(superclass)、基类(base class) 或 父类(parent class)
- 新类被称为 子类(subclass)、派生类(derived class) 或 孩子类(child class)
5.1.2 覆盖方法
- 父类中有些方法对子类并不一定适用,为此需要提供一个新的方法来 覆盖(override)
- 在覆盖一个方法的时候,子类方法不能低于父类方法的可见性
5.1.3 子类构造器
- 子类构造器不能访问父类的私有域,所以必须利用父类构造器对这部分私有域进行初始化,
- 可以通过 super 实现对父类构造器的调用
- super 调用构造器的语句必须是子类构造器的第一个语句
- 如果子类没有显示调用父类构造器,则将自动调用父类默认的无参构造器
- 如果父类没有无参构造器,并且在子类的构造器中又没有显示调用父类其它构造器,将报错
多态(polymorphism)
:一个对象变量可以只是多种实际类型的现象
动态绑定(dynamic binding)
: 在运行时能够自动地选择调用方法的现象
- 调用的方法依赖于隐式参数的实际类型,并且在运行时实现动态绑定
- 无需对现存的代码进行修改,就可以对程序进行扩展
5.1.4 继承层次
继承层次(inheritance hierarchy)
:由一个公共基类派生出来的所有类的集合
- Java不允许多继承
5.1.5 多态
"is-a"规则
:又可称为 置换法则
,它表明程序中出现父类对象的任何地方都可以用子类置换
- Java中,对象变量是多态的
- 不能将一个父类引用赋给子类变量
- Java中,子类数组的引用可以转换成父类数组的引用而不需要采用强制类型转换
- 此时要保证所有数组创建他们的元素类型,并负责监督仅将类型兼容的引用存储到数组中
- 如果类型不兼容将引发ArrayStoreException异常
5.1.6 理解方法调用
- 方法调用过程
- 第一步,编译器查看对象的声明类型和方法名。
- 第二步,编译器将查看调用方法时提供的参数类型。
重载解析(overloading resolution)
:
如果在所有同名的某方法中存在一个与提供的参数类型完全匹配,就选用该方法的过程
- 至此,编译器已获取需要调用的方法名字和参数类型
- 第三步,
静态绑定(static binding)
:
如果是private方法、static方法、final方法或者构造器,那么编译器将可以准确地知道应该调用的方法
- 第四步,当程序运行,并且采用动态绑定调用方法时,虚拟机一定调用与所引用对象的实际类型最适合的那个类的方法
方法表(method table)
:为了避免每次调用方法都要进行搜索而产生的大量时间开销,
JVM为每个类创建了一个列出了所有方法的签名与实际调用的方法的表。
- JVM在调用方法时,只需要查找方法表就可以了
5.1.7 阻止继承:final类和方法
final类
: 不允许扩展的类
final方法
: 类中特定的方法如果被声明为final,子类就不能覆盖这个方法
final类中的所有方法自动地成为final方法
- 将方法或类声明为final主要目的:
确保它们不会再子类中改变语义
5.1.8 强制类型转换
- 进行强制类型转换的唯一原因: 在暂时忽视对象的实际类型之后,使用对象的全部功能
- instanceof操作符: 用于在进行类型转换之前,先查看一下是否能成功地转换
- 只能在继承层次内进行类型转换
- 在将父类转换成子类之前,应该使用 instanceof 进行检查
- 在一般情况下,应该尽量少用类型转换和 instanceof 运算符
5.1.9 抽象类
- 如果自下而上在类的继承层次结构中上移,位于上层的类更具用通用性,甚至可能更加抽象。
从某种角度看,祖先类更加通用,人们只将它作为派生其它类的基类,而不作为想使用的特定的实例类
- 为了提高程序的清晰度,包含一个或多个抽象方法的类本身必须被声明为抽象的
- 除了抽象方法之外,抽象类还可以包含具体数据和具体方法
- 抽象方法充当着占位的角色,它们的具体实现在子类中
- 扩展抽象类的方法
- 在抽象类中定义部分抽象类方法或不定义抽象类方法,这样必须将子类也标记为抽象类
- 定义全部的抽象方法,这样一来,子类就不是抽象的了
- 类即使不含抽象方法,也可以将类声明为抽象类
- 抽象类不可实例化
5.1.10 受保护访问
- 4个访问修饰符:
private
: 仅对本类可见
public
: 对所有类可见
protected
: 对本包和所有子类可见
- 不加修饰符:对本包可见
返回