11.3 使用super关键字
子类继承它的父类所有可访问的数据域和方法。它能继承构造方法吗?父类的构造方法能够从子类调用吗?
关键字super是指这个super出现的类的父类。关键字super可以用于两种途径:
1)调用父类的构造方法。
2)调用父类的方法。
11.3.1 调用父类的构造方法
调用父类构造方法的语法是:
super(), or super(parameters);
语句super()调用它的父类的无参构造方法,而语句super(参数)调用于参数匹配的父类的构造方法。语句super()和super(参数)必须出现在子类构造方法的第一行,这是显式调用父类构造方法的唯一方式。
警告 要调用父类构造方法就必须使用关键字super,而且这个调用必须是构造方法的第一条语句。在子类中调用父类构造方法的名字会引起一个语法错误。
注意 构造方法可用来构造一个类的实例。不像属性和方法,父类的构造方法是不被子类继承的。它们只能从子类的构造方法中用关键字super调用。
11.3.2 构造方法链
在任何情况下,构造一个类的实例时,将会调用沿用继承链的所有父类的构造方法。当构造一个子类的对象时,子类构造方法会在完成自己的任务之前,首先调用它的父类的构造方法。如果父类继承自其它类,那么父类构造方法又会在完成自己的任务之前,调用它自己的父类的构造方法。这个过程持续到沿着这个继承体系结构的最后一个构造方法被调用为止。这就是构造方法链(constructor chaining)。
public class Faculty extends Employee{
public static void main(String[] args){
new Faculty();
}
public Faculty(){
System.out.println("(4) Performs Faculty's tasks");
}
}
class Employee extends Person{
public Employee(){
this("(2) Invoke Employee's overloaded constructor");
System.out.println("(3) Performs Employee's tasks");
}
public Employee(String s){
System.out.println(s);
}
}
class Person{
public Person(){
System.out.println("(1) Performs Person's tasks");
}
}
(1) Performs Person's tasks
(2) Invoke Employee's overloaded constructor
(3) Performs Employee's tasks
(4) Performs Faculty's tasks
11.4 覆盖方法
子类从父类中继承方法。有时,子类需要修改父类中定义的方法的实现,这称做方法覆盖(method overriding)。
11.5 覆盖和重载
重载方法意味着可以定义多个同名的方法,但这些方法具有不同的签名。覆盖方法意味着为子类中的方法提供一个全新的实现。该方法已经在父类中定义。
为了覆盖一个方法,这个方法必须使用相同的签名以及相同的返回值类型在子类中进行定义。
用一个例子来显示覆盖和重载的不同。在a)中,类A中的方法p(double i)覆盖了在类B中定义的同一个方法。但是,在b)中,类B中有两个重载的方法p(double i)和p(int i)。类B的p(double i)方法被继承。
a)
public class Test{
public static void main(String[] args){
A a = new A();
a.p(10);
a.p(10.0);
}
}
class B{
public void p(double i){
System.out.println(i * 2);
}
}
class A extends B{
//This method overrides the method in B
public void p(double i){
System.out.println(i);
}
}
10.0
10.0
b)
public class Test{
public static void main(String[] args){
A a = new A();
a.p(10);
a.p(10.0);
}
}
class B{
public void p(double i){
System.out.println(i * 2);
}
}
class A extends B{
//This method overrides the method in B
public void p(int i){
System.out.println(i);
}
}
10
20.0
a)中的Test类时,a.p(10)和a.p(10.0)调用的都是定义在类A中的p(double i)方法,所以程序都显示10.0。运行b)中的Test类时,a.p(10)调用类A中定义的p(int i)方法,显示输出为10,而a.p(10.0)调用定义在类B中的p(double i)方法,显示输出为20.0。
11.6 对象类Object和它的toString()方法
Java中的每个类都源于java.lang.Object类。如果在定义一个类时没有指定继承性,那么这个类的父类就被默认是Object。例如,下面两个类的定义是一样的:
public class ClassName{
...
}
public class ClassName extends Object{
...
}
toString()方法的签名是:
public String toString()
调用一个对象的toString()会返回一个描述该对象的字符串。
11.7多态
首先,定义两个有用的术语:子类型和父类型。一个类实际上定义了一种类型。子类定义的类型成为子类型(subtype),而父类定义的类型成为父类型(supertype)。
继承关系使一个子类继承父类的特征,并且附加一些新特征。子类是它的父类的特殊化,每个子类的实例都是其父类的实例,但是反过来就不成立。
11.8 动态绑定
一个方法可以在父类中定义而在它的子类中覆盖。
toString()方法是在Object类中定义的,而该方法在GeometricObject1类中覆盖。
Object o = new GeometricObject();
System.out.println(o.toString());
这里的o调用哪个toString()呢?为了回答这个问题,我们首先介绍两个术语:声明类型和实际类型。一个变量必须被声明为某种类型。变量的这个类型成为它的声明类型(declared type)。这里,o的声明类型是Object。一个引用类型变量可以是一个null值或者是一个对声明类型实例的引用。实例可以使用声明类型或它的子类型的构造方法创建。变量的实际类型(actual type)是被变量引用的对象的实际类。这里,o的实际类型是GrometricObject,因为o指向使用new GeometricObject()创建的对象。o调用的到底是哪个toString()方法是由o的实际类型所决定的。这成为动态绑定(dynamic binding)。
11.11 数组线性表ArrayList类
java提供ArrayList类来存储不限定个数的对象
+ArrayList() | 创建一个空的线性表 |
+add(o:Object):void | 在这个线性表的末尾追加一个新元素0 |
+add(index:int,o:Object):void | 在这个线性表的特定下标处增加一个新元素0 |
+clear():void | 从这个线性表中删除所有的元素 |
+contains(o:Object):boolean | 如果这个线性表包含元素0则返回true |
+get(index:int):Object | 返回这个线性表在特定下标处的元素 |
+indexOf(o:Object):int | 返回这个线性表中第一个匹配元素的下标 |
+isEmpty():boolean | 如果这个线性表不包含元素则返回true |
+lastIndexOf(o:Object):int | 返回这个线性表中最后一个匹配元素的下标 |
+remove(o:Object):boolean | 从这个线性表中删除元素0 |
+size():int | 返回这个线性表中元素的个数 |
+remove(index:int):boolean | 删除指定下标处的元素 |
+set(index:int,o:Object):Object | 设置在特定下标处的元素 |
11.13 protected数据和方法
经常需要允许子类访问定义在父类中的数据域或方法,但不允许非子类访问这些数据域和方法。可以使用关键字protected完成该功能。父类中被保护的数据域或方法可以在它的子类中访问。
类中成员的修饰符 | 在同一类内可访问 | 在同一包内可访问 | 在子类内可访问 | 在不同包可访问 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | - |
(default) | √ | √ | - | - |
private | √ | - | - | - |
11.14 防止扩展和覆盖
有时候,可能希望防止类扩展。在这种情况下,使用final修饰符表明一个类是终极的,是不能作为父类的。Math类就是一个终极类。String、StringBuilder和StringBuffer类也可以是终极类。例如,下面的类就是终极的,是不能扩展的:
public final class C{
//Data fields, constructors, and methods omitted
}
也可以定义一个方式为终极的,一个终极方法不能被它的子类覆盖。
例如,下面的方法是终极的,是不能覆盖的:
public class Test{
//Data fields, constructors, and methods omitted
public final void m(){
//Do something
}
}
注意 修饰符可以用在类和类的成员(数据和方法)上,只有final修饰符还可以用在方法中的局部变量上。方法内的终极局部变量就是常量。