摘要:
- 上溯造型中方法的绑定
- 抽象类和方法
- 接口
方法的绑定(Binding)
- 早期绑定:由编译期和链接程序,在程序运行以前执行绑定
- 后期绑定:使对象在上溯造型后依然能调用正确的方法(衍生类的方法)
若一种语言实现了后期绑定(动态绑定、运行期绑定),同时必须提供一些机制,可在运行期间判断对象的类型,并分别调用适当的方法。也就是说,编译器此时依然不知道对象的类型,但方法调用机制能自己去调查,找到正确的方法主体。不同的语言对后期绑定的实现方法是有所区别的。但我们至少可以这样认为: 它们都要在对象中安插某些特殊类型的信息。
java中所有的方法声明都是后期绑定,除非一个方法被声明为final。final能防止其他人覆盖那个方法,可有效的“关闭”动态绑定,或者告诉编译期不需要进行动态绑定。(这样编译期就可为final方法调用生成更高效的代码)
1 | Shape s = new Circle(); |
抽象类和方法
- abstract 关键字;
- 包含抽象方法的类叫抽象类。如果一个类里包含了一个或多个抽象方法,类就必须声明为abstract;
- 抽象类不能创建对象。(抽象类的目的是为从它衍生出的所有类都创建一个通用接口,而不是创建抽象类的对象,编译期会保证抽象类的“纯洁性”,我们不必担心误用它);
- 在衍生类中必须提供方法定义。如果从一个抽象类继承,而且想生成新类型的一个对象,就必须为基础类中的所有抽象方法提供方法定义。如果不这样做(可以选择不这样做),则衍生类也会是抽象的,而且编译期会强迫我们用abstract关键字标注那个类的“抽象”本质;
- 如果一个类没有任何抽象方法,而我们想禁止那个类的所有实例,也可以将一个类声明为抽象类;
接口 interface
- implements关键字;
- “纯”抽象类。常把接口用于建立类与类之间的一个协议(“protocol”);
- 接口也包含了基本数据类型的数据成员,但它们都默认static final(compile-time constant)。它们不能是“空白final”,但可初始化成非常数表达式:
1
int rint = (int)Math.random();
- 可在interface 关键字钱添加public 关键字(但只有接口定义在同名的一个文件内),或者将其省略,默认friendly;
- 接口中的方法默认为public,也可明确定义为public。所以在实现一个接口时,来自接口的方法必须定义为public(否则编译期会报错);
接口的多重继承
1 | interface CanFight{ |
P.S.
- 合并具体类和接口时,必须是具体类在前,接口在后(否则编译期会报错),基础类只能有一个,接口可以有多个(用逗号隔开),例:
1
2class Hero extends ActionCharacter
implements CanFight, CanSwim, CanFly {} - 尽管Hero 没有为 fight()明确地提供 一个定义,但定义是随同ActionCharacter 来的,所以这个定义会自动提供,我们可以创建Hero 的对象;
- 使用接口最重要的一个原因:能上溯造型至多个基础类;
- 使用接口的第二个原因:防止客户程序员创建这个类的一个对象(与使用抽象类相同);
- 选择接口还是抽象类:
如果事先知道某个东西会成为基础类,那么优先选择接口。只有在必须使用方法定义或成员变量的时候,才考虑抽象类;
通过集成扩展接口
通过继承技术,可方便地为一个接口添加新的方法声明,也可以将几个接口合并成一个新接口(可用extends引用多个基础接口,用逗号隔开)。
1 | interface Hero |
常数分组
由于置入接口的所有字段都自动具有static final属性,所以接口是对常数值进行分组的一个好工具,它具有与C的enum相似的效果。(JDK1.5引入了新类型——枚举 enum)
1 | public interface Number { |
P.S.
- 根据java命名规则,static final的基本数据类型(即编译期常量)全部大写,用下划线分隔多个单词;
- 可以使用Number.ONE 来对值进行引用。但得到的只是一个int,所以并不安全(不像c++的enum那样拥有额外的类型安全性),(只提供了int值,所以本来想用来代表一个月份的int变量可能实际获得一个整数值)。疑惑???
如果不想放弃安全性,可构建像下面这样的类:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class Numbers {
private String name;
private Numbers(String s) { name = s; }
public String toString() { return name; }
public static final Numbers
ONE = new Numbers("One"),
TWO = new Numbers("Two");
public static final Numbers[] numbers = { ONE, ONE, TWO };//提供一个多余的ONE,是偏移量加一
public static void main(String[] args){
Numbers num = Numbers.ONE;
System.out.println(num);
num = Numbers.numbers[1];
System.out.println(num);
System.out.println(num == Numbers.TWO);
System.out.println(num.equals(Numbers.TWO));
}
}