上一部分内容在这里:Inheritance and Interfaces 继承和接口1
当保留字 super 后跟括号()时,它表示对超类构造函数的调用。以这种方式使用时,构造函数调用必须是子类构造函数的第一条语句。另一方面,如果 super 后跟一个句点和一个 method 名称,则它表示对超类方法的调用,这样的调用可以在任何子类方法的任何地方进行。
-
Chapter 9.4 Polymorphism
learn how to use inheritance for processing objects of different types in the same program.
先解释一下 Polymorphism 是什么:Polymorphism 多态性(“具有多种形状”)允许我们操作共享一组任务的对象,即使这些任务以不同的方式执行。
虽然 method 需要传入超类对象,但我们可以用子类对象来替换,传入 method 中。在 Java 中,方法调用始终由实际对象的类型决定,而不是包含对象引用的变量的类型。 这称为动态方法查找。动态方法查找允许我们以统一的方式处理不同类的对象。此功能称为多态性。 我们要求多个对象执行一项任务,每个对象都以自己的方式执行任务。
(单看文字很抽象的,建议找个例子自己理解一下,这里我就不打代码了)
Abstract Classes
当扩展现有类时,可以选择是否覆盖 override 超类的方法。有时,需要强制程序员 override 一个方法。当超类没有好的默认 default 值时会发生这种情况,只有子类程序员才能知道如何正确实现该方法。
一个例子:假设 Java 第一国家银行决定每个账户类型都必须有一定的月费。 因此,应在 Account 类中添加一个 deductFees 方法:
public class Account
{
public void deductFees() { . . . }
. . .
}
但是这个 method 应该怎么做呢? 当然,可以让 method 什么都不做。 但是随后实现新子类的程序员可能会忘记实现 deductFees 方法。有一个更好的方法——将 deductFees 方法声明为抽象方法:
public abstract void deductFees();
抽象方法没有implementation。这会迫使子类的实现者指定此 method 的具体实现。一旦 Account 类具有抽象方法,编译器就会将创建新 Account() 的尝试标记为错误。An abstract method is a method whose implementation is not specified. An abstract class is a class that cannot be instantiated.
public abstract class Account
{
public abstract void deductFees();
. . .
}
public class SavingsAccount extends Account // Not abstract
{
. . .
public void deductFees() // Provides an implementation
{
. . .
}
}
虽然不能构造抽象类的对象,但是可以拥有类型为抽象类的对象引用。 当然,它所引用的实际对象必须是具体子类的实例:
Account anAccount; // OK
anAccount = new Account(); // Error—Account is abstract
anAccount = new SavingsAccount(); // OK
anAccount = null; // OK
使用抽象类的原因是强迫程序员创建子类。通过将某些方法指定为抽象方法,可以避免其他人可能意外继承的无用默认方法的麻烦。
-
Chapter 9.5 Object: The Cosmic Superclass
The equals Method
和等号的区别:
if (stamp1.equals(stamp2)) . . . // Contents are the same—see Figure 9
if (stamp1 == stamp2) . . . // Objects are the same—see Figure 10
The instanceof Operator
在超类变量中存储子类引用是合法的,下面是个例子:
ChoiceQuestion cq = new ChoiceQuestion();
Question q = cq; // OK
Object obj = cq; // OK
偶尔,需要执行相反的转换,从超类引用到子类引用。 例如,有一个 Object 类型的变量,并且您碰巧知道它实际上hold一个 Question 引用。 在这种情况下,您可以使用强制转换来转换类型:
Question q = (Question) obj;
不过,这样转换有点危险。如果obj 实际上引用了一个不相关类型的对象,那么就会抛出一个“类转换”异常。为了防止错误的强制转换,可以使用 instanceof 运算符,它测试对象是否属于特定类型。 例如,obj instanceof Question。如果 obj 的类型可转换为 Question,则返回 true。 如果 obj 引用一个实际问题或一个子类,例如 ChoiceQuestion,就会发生这种情况。
-
Chapter 9.6 Interface Types
通过专注于算法所需的基本操作,通常可以设计出一个通用的、可重复使用的处理对象的机制。使用接口类型来表达这些操作。
例子:考虑以下方法,该方法计算一个 BankAccount 对象组成的数组中的平均余额。
public static double average(BankAccount[] objects)
{
if (objects.length == 0) { return 0; }
double sum = 0;
for (BankAccount obj : objects)
{
sum = sum + obj.getBalance();
}
return sum / objects.length;
}
现在假设有一个 Country 对象数组并想要确定这些区域的平均值:
public static double average(Country[] objects)
{
if (objects.length == 0) { return 0; }
double sum = 0;
for (Country obj : objects)
{
sum = sum + obj.getArea();
}
return sum / objects.length;
}
显然,两种情况下计算结果的算法是相同的,但计算过程的细节不同。 如何编写一个通用方法来计算银行账户和国家的平均值?
假设这些类能够使用单一方法 getMeasure 来获取要在数据分析中使用的数据。对于银行账户,getMeasure 返回余额。 对于国家/地区,getMeasure 返回面积。然后我们可以实现一个单一的方法来计算:sum = sum + obj.getMeasure();
变量 obj 的类型是什么? 在 Java 中,接口类型用于指定所需的操作。我们将声明一个我们称之为 Measurable 的接口类型:
public interface Measurable
{
double getMeasure();
}
Java 接口类型包含一组方法的返回类型、名称和参数变量。接口声明列出了接口类型需要的所有方法。 Measurable 接口类型需要一个方法,但一般来说,一个接口类型可以需要多个方法。 Unlike a class, an interface type provides no implementation.
接口类型类似于类,但有几个重要的区别:1)接口类型中的方法必须是抽象的。2)接口类型中的所有方法都是自动公开的。3)接口类型不能有实例变量。4)接口类型不能有静态方法。
我们可以使用接口类型 Measurable 来实现计算平均值的“通用”方法:
public static double average(Measurable[] objects)
{
if (objects.length == 0) { return 0; }
double sum = 0;
for (Measurable obj : objects)
{
sum = sum + obj.getMeasure();
}
return sum / objects.length;
}
average 方法可用于实现 Measurable 接口的任何类的对象。如果类在 implements clause 中声明了接口,则该类实现了接口类型。 类应该实现接口所需的一个或多个抽象方法。 让我们修改 BankAccount 类来实现 Measurable 接口。
public class BankAccount implements Measurable
{
public double getMeasure()
{
return balance;
}
. . .
}