第八章 多态

什么是多态

多态是对方法而言,同一种方法,但其实现的功能却不同(执行结果不同)

  • 多态的作用是清除类型间的耦合关系
  • 对同一种方法,传入参数类型不同,运行结果不同
  • 多态的实现依靠继承(向上转型)的特性
  • 多态又称动态绑定,后期绑定,运行时绑定

方法调用绑定

将一个方法调用同方法对象关联起来,根据关联时间,可分为两种:
动态绑定:程序运行时,根据对象类型进行绑定—-重点
‘静态绑定’:在程序执行前绑定

静态绑定的特点是:在程序运行前,就进行绑定
动态绑定特点是:编译器一直不知道对象类型,但是方法调用机制,能根据对象类型找到正确的方法体,即最终要调用哪种方法要看对象类型

向上转型+动态绑定 == 多态

一个多态的例子

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
package package1;
interface Shape{
abstract void draw();
void erase();
int i=1;

}
//抽象方法可以是public,protected,默认包权限
//抽象类中的普通方法与其他普通方法相同
//接口类的方法,必须都是纯抽象方法,与抽象类不同的是:
//在接口类中无需显式的使用abstract定义抽象类,也可以显式定义
//接口类中方法默认权限都是public,可以设为static,abstract,其他的权限都不被允许
//接口类只能是public和abstract(可以从安全方面进行记忆)
//接口类的变量是常量,不可修改,故在定义常量时,必须给他赋值
abstract class Test{
void draw(){};
abstract void erase();
}

//对于继承接口的类而言,要重写接口类的所有方法,
//需要注意的是,这些接口类的方法是public类型
//因此,在重写的时候,也只能是public类型,不能降低权限

class Circle implements Shape{
public void draw(){
System.out.println("Circle`s draw");
}
public void erase(){
System.out.println("Circle`s erase");
}
}
class Square implements Shape{
public void draw(){
System.out.println("Square`s draw");
}
public void erase(){
System.out.println("Square`s erase");
}
}
public class demo1 implements Shape{
public void draw(){
System.out.println("demo`s draw");
}
public void erase(){
System.out.println("demo`s erase");
}
public static void main(String args[]){
Shape cirle = new Circle();
Shape square = new Square();
cirle.draw();
cirle.erase();
square.draw();
square.erase();
}

}

运行结果

1
2
3
4
Circle`s draw
Circle`s erase
Square`s draw
Square`s erase

多态的应用

利用多态机制,可以隐藏接口的实现–由于向上转型,所以无法确定是有哪种子类向上转型而来。
其次:可以将改变的事物与不变的事物分离开来,降低程序的耦合性,即,可以在不改变接口调用方法的情况下,修改接口的实现,而接口使用者无需了解接口如何实现,

域、静态与多态

域(field):A field is an attribute. A field may be a class’s variable, an object’s variable, an object’s method’s variable, or a parameter of a function.详细解析

域,静态方法是不具有多态性的。只有普通方法具有多态性

下面的例子是重点,需要重点理解!

接口类的多态性

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
package Chapter9;

interface Shape{
abstract void draw();
void erase();
void getField();
String field="i am Shape`s field";
}

class Circle implements Shape{
String field =" i am Circle`s field";
public void getField(){
System.out.println("Cirle`s getField(): "+field);
}
public String getSupField(){
//return super.field;会报错
//获得接口中的域
return Shape.field;
}
public void draw(){
System.out.println("Circle`s draw");
}
public void erase(){
System.out.println("Circle`s erase");
}
}
class Square implements Shape{
String field =" i am Square`s field";
public void getField(){
System.out.println("Squsre`s getField(): "+field);
}
public void draw(){
System.out.println("Square`s draw");
}
public void erase(){
System.out.println("Square`s erase");
}
}
public class demo1 implements Shape{
String field =" i am Demo1`s field";
public void getField(){
System.out.println("demo`s getField(): "+field);
}
public void draw(){
System.out.println("demo`s draw");
}
public void erase(){
System.out.println("demo`s erase");
}
public static void main(String args[]){
Shape cirle = new Circle();
Shape square = new Square();
Shape demo = new demo1();
Circle c = new Circle();
cirle.draw();
cirle.erase();
cirle.getField();
System.out.println("获得接口中的域(getSupField) :"+c.getSupField());
System.out.println("Circle.field= "+cirle.field);
square.draw();
square.erase();
square.getField();
System.out.println("Square.field= "+cirle.field);
demo.getField();
System.out.println("Demo1.field= "+cirle.field);
}

}

运行结果

1
2
3
4
5
6
7
8
9
10
11
Circle`s draw
Circle`s erase
Cirle`s getField(): i am Circle`s field
获得接口中的域(getSupField) :i am Shape`s field
Circle.field= i am Shape`s field
Square`s draw
Square`s erase
Squsre`s getField(): i am Square`s field
Square.field= i am Shape`s field
demo`s getField(): i am Demo1`s field
Demo1.field= i am Shape`s field

分析

1
2
3
Shape cirle = new Circle();  //向上转型
cirle.getField(); // 结果为 i am Circle`s field
circle.field; // 结果为 i am Shape`s field

Circle类实现Shape接口,Circle和Shape都有field这个(变量),
当Circle向上转型成Shape时,获得field会出现何种情况?

  • 如果以调用接口类普通方法的形式,由于普通方法具有多态性,因此,field取决于对象中内部field的值
  • 如果不以方法形式获取,则就没有多态性,取决于引用的类型

继承形式的多态性

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
package Chapter9;

//interface Shape{
// abstract void draw();
// void erase();
// void getField();
// String field="i am Shape`s field";
//}

class Shape{
String field="i am Shape`s field";
void draw(){};
void erase(){};
void getField(){};

}

class Circle extends Shape{
String field =" i am Circle`s field";
public void getField(){
System.out.println("Cirle`s getField(): "+field);
}
public String getSupField(){
//return super.field;若是实现接口,则会报错
return super.field;
}
public void draw(){
System.out.println("Circle`s draw");
}
public void erase(){
System.out.println("Circle`s erase");
}
}
class Square extends Shape{
String field =" i am Square`s field";
public void getField(){
System.out.println("Squsre`s getField(): "+field);
}
public void draw(){
System.out.println("Square`s draw");
}
public void erase(){
System.out.println("Square`s erase");
}
}
public class demo1 extends Shape{
String field =" i am Demo1`s field";
public void getField(){
System.out.println("demo`s getField(): "+field);
}
public void draw(){
System.out.println("demo`s draw");
}
public void erase(){
System.out.println("demo`s erase");
}
public static void main(String args[]){
Shape cirle = new Circle();
Shape square = new Square();
Shape demo = new demo1();
Circle c = new Circle();

cirle.getField();
System.out.println("获得基类中的域(getSupField) :"+c.getSupField());
System.out.println("Circle.field= "+cirle.field);

square.getField();
System.out.println("Square.field= "+cirle.field);
demo.getField();
System.out.println("Demo1.field= "+cirle.field);
}

}

运行结果

1
2
3
4
5
6
7
Cirle`s getField():      i am Circle`s field
获得基类中的域(getSupField) :i am Shape`s field
Circle.field= i am Shape`s field
Squsre`s getField(): i am Square`s field
Square.field= i am Shape`s field
demo`s getField(): i am Demo1`s field
Demo1.field= i am Shape`s field

在分别用接口和继承来实现时,发现super关键字一般只适用与继承


域不好判断,但是只要记住,只有普通方法才具有多态性,当要访问某变量,静态方法,静态属性等,他们都不具有多态性

任何域访问操作都是由编译器解析的,因此没有多态性,所以为了避免混淆,最好子类中的域名字不要与基类相同.

构造器与多态

由于构造器是静态方法,故没有多态性
基类的构造器总是在导出类的构造过程前被调用,而且按照继承层次,依次向上链接.

这样做的目的在于:确保对象被正确构建,导出类的构造器只能构造他自己的成员,因此需要调用基类的构造器类对基类的成员进行初始化(基类的成员可能是private).

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
package Chapter9;

class Food{
public Food() {
// TODO Auto-generated constructor stub
System.out.println("Food");
}
}

class Rice extends Food{
public Rice() {
// TODO Auto-generated constructor stub
System.out.println("Rice");
}
}
class Bread extends Rice{
public Bread() {
// TODO Auto-generated constructor stub
System.out.println("Bread");
}
}

class Lunch extends Food{
public Lunch() {
// TODO Auto-generated constructor stub
System.out.println("Lunch");
}
}
class PortableLunch extends Lunch{
public PortableLunch() {
// TODO Auto-generated constructor stub
System.out.println("PortableLunch 内部 构建 Lunch");
new Lunch();
System.out.println("PortableLunch");
}
}
public class Sandwich extends PortableLunch{
public Sandwich() {
// TODO Auto-generated constructor stub
System.out.println("Sandwich");
}
public static void main(String args[]){
System.out.println("构建Bread()");
new Bread();
System.out.println("构建Rice()");
new Rice();
System.out.println("构建Lunch()");
new Lunch();
System.out.println("构建PortableLunch()");
new PortableLunch();
System.out.println("构建Sandwich()");
new Sandwich();

}
}

运行结果

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
构建Bread()
Food
Rice
Bread
构建Rice()
Food
Rice
构建Lunch()
Food
Lunch
构建PortableLunch()
Food
Lunch
PortableLunch 内部 构建 Lunch
Food
Lunch
PortableLunch
构建Sandwich()
Food
Lunch
PortableLunch 内部 构建 Lunch
Food
Lunch
PortableLunch
Sandwich

构建顺序:

  • 递归调用所有可能要用到的基类,按序执行构造器内部代码
    比如: new PortableLunch()
    执行 Food 构造器->Lunch 构造器->PortableLunch 构造器-> 按序执行PortableLunch 构造器内部的语句

构造器内部的多态

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
package Chapter9;

import java.lang.reflect.Field;

class Shape{
void draw(){
System.out.println("Shape`s draw()");
}
public Shape() {
// TODO Auto-generated constructor stub
System.out.println("Shape`s constructor begin");
draw();
System.out.println("Shape`s constructor end");
}
}
public class Square extends Shape{
private int Field = 1;
@Override
void draw(){
System.out.println("Square`s draw,Field= "+Field);
}
public Square() {
// TODO Auto-generated constructor stub
System.out.println("Square`s constuctor");
}
public static void main(String args[]){
new Square();
}

}

运行结果

1
2
3
4
Shape`s constructor begin
Square`s draw,Field= 0
Shape`s constructor end
Square`s constuctor

由结果可以看出,new Square()调用基类构造器,由于基类构造器中调用draw()方法,由多态性可以理解为:基类构造器调用Square类的draw()方法,但是这里出现了一个问题:Field的值是0,而正常来说应该是1,可以理解Java没有正确执行,最重要的是没有报错信息.

由于java在其他任何事物发生之前,将分配给对象的存储空间初始化
Field=0
在调用基类构造器时,由于子类构造器还未调用,故Field的值仍然为0,
但是基类调用的draw()方法以被子类方法覆盖(多态性),所以就有了上述结果.

本文标题:第八章 多态

文章作者:定。

发布时间:2017年5月18日 - 01时05分

本文字数:8,725字

原始链接:http://cocofe.cn/2017/05/18/第八章 多态/

许可协议: Attribution-NonCommercial 4.0

转载请保留以上信息。