多态
多态的主要优点是消除类型之间的耦合关系,可以将类型之间的公共部分放到基类,则所有导出类可以看成是基类,编程只需面向基类,只需一份代码;而且增加新的导出类,代码无需更改。
1 | class Shape { |
如上,对于drawShape(Shape shape)方法,如果没有多态的话,需要写drawShape(Circle circle), drawShape(Triangle triangle) 等方法,且每增加一个导出类,都需要增加一个对应方法。
但是通过多态,可以将这些公共的部分提取出来,直接面向基类Shape编程。而且对于新的导出类,drawShape的代码不需要改变。
动态绑定
1 | public static void drawShape(Shape shape){ |
上面的代码中,编译器是不知道shape引用是指向Circle对象还是Triangle对象的。所以编译器不知道要调用哪个方法。
解决这个问题的方法是动态绑定。因为要知道调用哪个方法,必须要区分Circle,Triangle等对象,所以需要在对象中存储某种类型信息。动态绑定的机制就是在运行时根据对象的类型绑定相应的方法。
java中除了static、final的方法之外,其他方法默认都是后期绑定的,所以要关闭动态绑定,只需将某个声明为final。
构造器
因为导出类由基类派生,所以要构造导出类,首先需要构造基类。
导出类的构造器调用之前,需要先调用基类的构造器,如果导出类没有通过super()显式调用,则会调用基类默认构造器,如果不存在,则会报错。
1 | class Meal{ |
可以加入super("haha")
显式调用基类的构造器。
构造器调用顺序
要构造出导出类对象,首先得构造出基类对象。
- 将分配给对象的存储空间初始化为二进制0
- 先调用基类构造器
- 按声明顺序调用成员初始化方法
- 调用导出类构造器实体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45class Meal {
private Bread bread = new Bread();
Meal() { System.out.println("Meal()"); }
}
class Bread {
Bread() { System.out.println("Bread()"); }
}
class Cheese {
Cheese() { System.out.println("Cheese()"); }
}
class Lettuce {
Lettuce() { System.out.println("Lettuce()"); }
}
class Lunch extends Meal {
Lunch() { System.out.println("Lunch()"); }
}
class PortableLunch extends Lunch {
PortableLunch() { System.out.println("PortableLunch()");}
}
public class Sandwich extends PortableLunch {
private Bread b = new Bread();
private Cheese c = new Cheese();
private Lettuce l = new Lettuce();
public Sandwich() { System.out.println("Sandwich()"); }
public static void main(String[] args) {
new Sandwich();
}
}
/*output:
Bread()
Meal()
Lunch()
PortableLunch()
Bread()
Cheese()
Lettuce()
Sandwich()
*/
构造器内调用多态方法
1 | import static net.mindview.util.Print.*; |
上面的代码中先调用了基类构造器Glyph()时,draw()由于动态绑定,会调用RoundGlyph中的draw方法,而此时radius尚未初始化,只是在一开始存储空间初始化为0。
所以在构造器中,最好不要调用非final的方法。
向下转型
向上转型时安全的,因为导出类有基类的所有接口。但是向下转型却未必正确。所以在Java语言中,所有转型都会得到检查,由于类型信息存在运行时的对象中,可以进行RTTI(运行时类型识别),检查向下转型是否正确,错误则抛出ClassCastException。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29class Useful {
public void f() {}
public void g() {}
}
class MoreUseful extends Useful {
public void f() {}
public void g() {}
public void u() {}
public void v() {}
public void w() {}
}
public class RTTI {
public static void main(String[] args) {
Useful[] x = {
new Useful(),
new MoreUseful()
};
x[0].f();
x[1].g();
// Compile time: method not found in Useful:
//! x[1].u();
((MoreUseful)x[1]).u(); // Downcast/RTTI
((MoreUseful)x[0]).u(); // Exception thrown
}
} ///:~
// ouput: java.lang.ClassCastException: class Useful cannot be cast to class MoreUseful