面向对象之多态
# 多态
Java引用变量有两个类型
- 编译时类型
- 编译时类型由声明该变量时使用的类型决定。(相当于
Object p = new Person();
中的Object
)
- 编译时类型由声明该变量时使用的类型决定。(相当于
- 运行时类型
- 运行时类型由实际赋给该变量的对象决定。(相当于
Object p = new Person();
中的new Person()
)
- 运行时类型由实际赋给该变量的对象决定。(相当于
如果编译时类型和运行时类型不一致,就可能出现所谓的多态(Polymorphism)。
# 代码示例
父类——BaseClass2
package com.abc.part4;
/**
* @author mi
*/
public class BaseClass2 {
public int age = 21;
public void base() {
System.out.println("父类中的普通方法。");
}
public void test() {
System.out.println("父类中的被覆盖的方法。");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
子类——SubClass2.java
package com.abc.part4;
/**
* @author mi
*/
public class SubClass2 extends BaseClass2 {
public int age = 25;
@Override
public void test() {
System.out.println("子类中覆写父类test()的方法。");
}
public void info() {
System.out.println("子类中的普通方法");
}
public static void main(String[] args) {
BaseClass2 baseClass2 = new BaseClass2();
//输出:baseClass2.age:21
System.out.println("baseClass2.age:" + baseClass2.age);
//输出:父类中的普通方法。
baseClass2.base();
//输出:父类中的被覆盖的方法。
baseClass2.test();
SubClass2 subClass2 = new SubClass2();
//输出:subClass2.age:25
System.out.println("subClass2.age:" + subClass2.age);
//输出:子类中覆写父类test()的方法。
subClass2.test();
//输出:子类中的普通方法
subClass2.info();
/*
SubClass2是BaseClass2的子类,属于继承关系,所以可以调用父类中的方法。
*/
//输出:父类中的普通方法。
subClass2.base();
BaseClass2 bs = new SubClass2();
//输出:子类中覆写父类test()的方法。
bs.test();
//输出:父类中的普通方法。
bs.base();
//输出:bs.age:21
System.out.println("bs.age:" + bs.age);
//bs.info();
}
}
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
上边代码中,第三个引用变量bs
则比较特殊,它的编译时类型是BaseClass2
,而运行时类型是SubClass2
,当调用该引用变量的test()
方法(BaseClass2
类中定义了该方法,子类SubClass2
覆盖了父类的该方法)时,实际执行的是SubClass2
类中覆盖后的test()
方法,这就是多态
的一种体现。
子类其实是一种特殊的父类,因此Java
允许把一个子类对象直接赋给一个父类引用变量,无须任何类型转换,或者被称为向上转型(upcasting)
,向上转型由系统自动完成。
**当把一个子类对象直接赋给父类引用变量时,例如上面的BaseClass2 bs = new SubClass2();
, 这个bs
引用变量的编译时类型是BaseClass2
,而运行时类型是SubClass2
,当运行时调用该引用变量的方法时,其方法行为总是表现出子类方法的行为特征,而不是父类方法的行为特征,这就可能出现:相同类型的变量、调用同一个方法时呈现出多种不同的行为特征,这就是多态。
与方法不同的是,对象的实例变量则不具备多态性
,比如上面的bs
引用变量,程序中输出它的age
实例变量时,并不是输出SubClass2
类里定义的实例变量age
:25,而是输出BaseClass2
类中的实例变量age
:21。
# 总结
- 引用变量在编译阶段只能调用其编译时类型所具有的方法,但运行时则执行它运行时类型所具有的方法。
- 编写Java代码时,引用变量只能调用声明该变量时所用类里包含的方法。例如,通过
Object p = new Person();
代码定义一个变量p,则这个p只能调用Object
类的方法,而不能调用Person
类里定义的方法。(有点绕~得好好琢磨琢磨) - 通过引用变量来访问其包含的实例变量时,系统总是试图访问它编译时类型所定义的成员变量,而不是它运行时类型所定义的成员变量。
# 引用变量的强制类型转换
编写Java程序时,引用变量
只能调用它编译时类型
的方法
,而不能调用它运行时类型
的方法
,即使它实际所引用的对象确实包含该方法
。如果需要让这个引用变量
调用它运行时类型
的方法
,则必须把它强制类型转换
成运行时类型
,强制类型转换需要借助于类型转换运算符
:(type)variable
,这种用法可以将variable
变量转换成type
类型的变量。
# 基本类型、引用类型分别进行强制类型转换时注意事项
- 基本类型之间的转换只能在数值类型之间进行,这里所说的数值类型包括
整数型
、字符型
和浮点型
。但数值类型
和布尔类型
之间不能进行类型转换。 - 引用类型之间的转换只能在具有
继承关系
的两个类型之间进行,如果是两个没有任何继承关系的类型,则无法进行类型转换,否则编译时就会出现错误。如果试图把一个父类实例转换成子类类型,则这个对象必须实际上是子类实例才行(即编译时类型为父类类型,而运行时类型是子类类型),否则将在运行时引发ClassCastException
异常。
# 代码示例
package com.abc.part4;
/**
* @Auther: ABC
* @Date: 2020/5/13 22:58
* @Description:
*/
public class ConversionTest {
public static void main(String[] args) {
double a = 14.9;
long b = (long) a;
//输出:14 双精度浮点型强转至长整形会变为一个整数。
System.out.println(b);
int c = 4;
// `数值类型`和`布尔类型`之间不能进行类型转换
//boolean i = (boolean) c;//Inconvertible types; cannot cast 'int' to 'boolean'
//object变量的编译时类型为Object,Object与String存在继承关系,可以强制类型转换
//而且object变量的实际类型是String,所以运行时也可通过。
Object object = "哈哈哈";
String objStr = (String) object;
// 定义一个objPri变量,编译时类型为Object,实际类型为Integer
Object objPri = Integer.valueOf(5);
// objPri变量的编译时类型为Object,objPri的运行时类型为Integer
// Object与Integer存在继承关系
// 可以强制类型转换,而objPri变量的实际类型是Integer
// 所以下面代码运行时引发ClassCastException异常
//String str = (String)objPri;//Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
//为了使程序更健壮,可以事先先用instanceof运算符来判断是否可以成功转换。
//在进行强制类型转换之前,先用instanceof运算符判断是否可以成功转换,从而避免出现ClassCastException异常,这样可以保证程序更加健壮
//下面程序将会输出:objPri不能被强制转换为String类型!
if (objPri instanceof String) {
String str = (String)objPri;
}else {
System.out.println("objPri不能被强制转换为String类型!");
}
/*
当把子类对象赋给父类引用变量时,被称为向上转型(upcasting),这种转型总是可以成功的,这也从另一个侧面证实了子类是一种特殊的父类。
这种转型只是表明这个引用变量的编译时类型是父类,但实际执行它的方法时,依然表现出子类对象的行为方式。
但把一个父类对象赋给子类引用变量时,就需要进行强制类型转换,而且还可能在运行时产`ClassCastException`异常,使用instanceof运算符可以让强制类型转换更安全。
*/
}
}
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
# 向上转型和强制类型转换
- 向上转型,此处多指Java面向对象中的
多态
- 当把子类对象赋给父类引用变量时,被称为向上转型(
upcasting
)。如:BaseClass2 bs = new SubClass2();
- 证实了子类是一种特殊的父类。
- 这种转型只是表明这个引用变量的编译时类型是父类,但实际执行它的方法时,依然表现出子类对象的行为方式。(多态的一种体现)
- 当把子类对象赋给父类引用变量时,被称为向上转型(
- 强制类型转换
- 把一个父类对象赋给子类引用变量时,就需要进行强制类型转换。如:
SubClass2 sc = (SubClass2) new BaseClass2();
- 而且还可能在运行时产
ClassCastException
异常,使用instanceof
运算符可以让强制类型转换更安全。if (objPri instanceof String) { String str = (String)objPri; }else { System.out.println("objPri不能被强制转换为String类型!"); }
1
2
3
4
5
- 把一个父类对象赋给子类引用变量时,就需要进行强制类型转换。如:
关于引用变量的强制类型转换具体详解见Java面向对象之引用数据类型的强制类型转换 (opens new window)
# instanceof
运算符
- 01
- Python实现对字符串的加解密02-25
- 02
- Python3对大文件中指定字符进行排序再写入到新的文件10-24
- 03
- Ubuntu下配置adb环境连接Android设备进行调试08-17