10. 继承的语法


post10

继承的语法

  • 使用extends关键字

举例如下:

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package com.isflee.c06_2_1_detergent;

//: Detergent.java
// Inheritance syntax & properties
//继承的语法
class Cleanser {
private String s = new String("Cleanser");

public void append(String a) {
s += a;
}

public void dilute() {
append(" dilute()");
}

public void apply() {
append(" apply()");
}

public void scrub() {
append(" scrub()");
}

public void print() {
System.out.println(s);
}

public static void main(String[] args) {
Cleanser x = new Cleanser();
x.dilute();
x.apply();
x.scrub();
x.print();
}
}

public class Detergent extends Cleanser { // Change a method:
public void scrub() {
append(" Detergent.scrub()");
//调用父类方法的时候,方法的定义(方法名和参数)相同时,子类中需要使用super关键字,防止与递归调用混淆
super.scrub();
// Call base-class version
}

// Add methods to the interface:
//进行继承时,我们并不限于只能使用基础类的方法。亦可在衍生出来的类里加入自己的新方法。
public void foam() {
append(" foam()");
} // Test the new class:

public static void main(String[] args) {
Detergent x = new Detergent();
x.dilute();
x.apply();
x.scrub();
x.foam();
x.print();
System.out.println("Testing base class:");
Cleanser.main(args);
}
} ///:~

关于继承的注意点:

1.在计划继承的时候,一个比较好的规则是将所有字段都设为private,并将 所有方法都设为public(protected 成员也允许衍生出来的类访问它;以后还会深入探讨这一问题)。

2.调用父类方法的时候,方法的定义(方法名和参数)相同时,子类中需要使用super关键字,防止与递归调用混淆。

3.进行继承时,我们并不限于只能使用基础类的方法。亦可在衍生出来的类里加入自己的新方法。这时采取的 做法与在普通类里添加其他任何方法是完全一样的:只需简单地定义它即可。

  • 继承语法中的初始化问题

1.在衍生类的构建器中,Java会自动插入对基础类构建器的调用。

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
package com.isflee.c06_2_2_cartoon;

//: Cartoon.java
// Constructor calls during inheritance
//在衍生类的构建器中,Java会自动插入对基础类构建器的调用。
class Art {
Art() {
System.out.println("Art constructor");
}
}

class Drawing extends Art {
Drawing() {
System.out.println("Drawing constructor");
}
}

public class Cartoon extends Drawing {
Cartoon() {
System.out.println("Cartoon constructor");
}

public static void main(String[] args) {
Cartoon x = new Cartoon();
}
} ///:~

2.含有自变量的构建器的初始化

编译器会强迫我们在衍生类构建器的主体中首先设置对基础类构建器的调用

如果类没有默认的自变量,或者想调用含有一个自变量的某个基础类构建器,必须明确地编写对基础类的调用代码。

用super关键字以及适当的自变量列表实现

使用代码如下:

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
package com.isflee.c06_2_3_chess;

//: Chess.java
// Inheritance, constructors and arguments
//含有自变量的构建器
class Game {
Game(int i) {
System.out.println("Game constructor");
}
}

class BoardGame extends Game {
BoardGame(int i) {
super(i);
System.out.println("BoardGame constructor");
}
}

public class Chess extends BoardGame {
Chess() {
super(11);
System.out.println("Chess constructor");
}

public static void main(String[] args) {
Chess x = new Chess();
}
} ///:~
  • 合成与继承的结合

示例代码:

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package com.isflee.c06_3_1_place_setting;

//: PlaceSetting.java
// Combining composition & inheritance
//合成与继承的结合
class Plate {
Plate(int i) {
System.out.println("Plate constructor");
}
}

class DinnerPlate extends Plate {
DinnerPlate(int i) {
super(i);
System.out.println(
"DinnerPlate constructor");
}
}

class Utensil {
Utensil(int i) {
System.out.println("Utensil constructor");
}
}

class Spoon extends Utensil {
Spoon(int i) {
super(i);
System.out.println("Spoon constructor");
}
}

class Fork extends Utensil {
Fork(int i) {
super(i);
System.out.println("Fork constructor");
}
}

class Knife extends Utensil {
Knife(int i) {
super(i);
System.out.println("Knife constructor");
}

}

// A cultural way of doing something:
class Custom {
Custom(int i) {
System.out.println("Custom constructor");
}
}

public class PlaceSetting extends Custom {
Spoon sp;
Fork frk;
Knife kn;
DinnerPlate pl;

PlaceSetting(int i) {
super(i + 1);
sp = new Spoon(i + 2);
frk = new Fork(i + 3);
kn = new Knife(i + 4);
pl = new DinnerPlate(i + 5);
System.out.println(
"PlaceSetting constructor");
}

public static void main(String[] args) {
PlaceSetting x = new PlaceSetting(9);
}
} ///:~
  • 确保正确的清除

在自己的清除方法中,必须注意对基础类以及成员对象清除方法的调用顺序——假若一个子对象要以另一个为基础。

通常,应采取与C++编译器对它的“破坏器”采取的同样的形式: 首先完成与类有关的所有特殊工作(可能要求基础类元素仍然可见),然后调用基础类清除方法

举例:

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package com.isflee.c06_3_2_cad_system;

//: reusing/CADSystem.java
// Ensuring proper cleanup.
//确保正确的清除
class Shape {
Shape(int i) {
System.out.println("Shape constructor");
}

void dispose() {
System.out.println("Shape dispose");
}
}

class Circle extends Shape {
Circle(int i) {
super(i);
System.out.println("Drawing Circle");
}

void dispose() {
System.out.println("Erasing Circle");
super.dispose();
}
}

class Triangle extends Shape {
Triangle(int i) {
super(i);
System.out.println("Drawing Triangle");
}

void dispose() {
System.out.println("Erasing Triangle");
super.dispose();
}
}

class Line extends Shape {
private int start, end;

Line(int start, int end) {
super(start);
this.start = start;
this.end = end;
System.out.println("Drawing Line: " + start + ", " + end);
}

void dispose() {
System.out.println("Erasing Line: " + start + ", " + end);
super.dispose();
}
}

public class CADSystem extends Shape {
private Circle c;
private Triangle t;
private Line[] lines = new Line[3];

public CADSystem(int i) {
super(i + 1);
for (int j = 0; j < lines.length; j++)
lines[j] = new Line(j, j * j);
c = new Circle(1);
t = new Triangle(1);
System.out.println("Combined constructor");
}

public void dispose() {
System.out.println("CADSystem.dispose()");
// The order of cleanup is the reverse
// of the order of initialization:
t.dispose();
c.dispose();
for (int i = lines.length - 1; i >= 0; i--)
lines[i].dispose();
super.dispose();
}

public static void main(String[] args) {
CADSystem x = new CADSystem(47);
try {
// Code and exception handling...
} finally {
x.dispose();
}
}
}
  • 名字的隐藏

原文

如果 Java 基础类 有一个方法名被“过载”使用多次,在衍生类里对那个方法名的重新定义就不会隐藏任何基础类的版本。

也就是说,

对于多级继承的情况,父类中的某个方法名被“过载”使用多次, 最后一级的子类的对象仍然能访问到父类的这个方法

代码如下:

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
package com.isflee.c06_3_3_hide;

//: Hide.java
// Overloading a base-class method name // in a derived class does not hide the // base-class versions
class Homer {
char doh(char c) {
System.out.println("doh(char)");
return 'd';
}

float doh(float f) {
System.out.println("doh(float)");
return 1.0f;
}
}

class Milhouse {
}

class Bart extends Homer {
void doh(Milhouse m) {
}
}

class Hide {
public static void main(String[] args) {
Bart b = new Bart();
b.doh(1); // doh(float) used
b.doh('x');
b.doh(1.0f);
b.doh(new Milhouse());
}
} ///:~