对象的多态性
以代码为示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class Animals {
public void speak(){
System.out.println("i am animals");
}
}
public class Cat extends Animals{
public void speak(){
System.out.println("i am cat");
}
}
public class Test {
public static void main(String[] args){
//一个对象,两种形态
Animals cat = new Cat();
cat.speak();
}
}运行后的结果为”i am cat”
- 猫这类事物既具备猫的形态,又具备动物形态,这就是对象的多样性,简单说,一个对象对应着多种类型
- 多态在代码中的体现
- 父类或者接口的引用指向了其子类的对象
多态的好处
提高了代码扩展性,前期定义的代码可以使用后期的内容。
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
33public abstract class Animals {
public abstract void eat();
}
public class Cat extends Animals{
public void eat(){
System.out.println("吃鱼");
}
public void catchMouse(){
System.out.println("抓老鼠");
}
}
public class Dog {
public void eat(){
System.out.println("啃骨头");
}
public void guardDoor(){
System.out.println("看门");
}
}
public class Test {
public static void main(String[] args){
Cat c1 = new Cat();
Dog d1 = new Dog();
Dog d2 = new Dog();
method(c1);
method(d1);
method(d2);
}
public static void method(Animals a){
a.eat();
}
}如上图所示代码,如果很多只猫,很多只狗,为了代码复用性,需要在主类中定义一个方法,可是如果动物种类很多,是否要定义不同参数的许多方法那,答案是不用的,只需要定义个参数为Animals对象的即可
多态的局限性及前提
- 前期定义的内容,不能调用后期子类定义的特有内容
- 多态的前提
- 必须有关系,继承,实现
- 要有覆盖
多态转型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//自动类型提升,猫对象提升了动物类型,但特有功能无法访问
//作用就是限制对猫特有功能的访问
//专业讲就是 向上转型,将子类型隐藏,就不用使用子类的特有方法
Animals a = new Cat();
a.eat();
//如果还想访问具体动物的特有功能
//可以进行向下转型
Cat c = (Cat)a;//向下转型目的:为了使用子类中的特有方法
c.eat();
c.catchMouse();
//错误示例
Animals a1 = new Dog();
Cat c1 = (Cat)a1; //ClassCastException类型转换异常注意:对于转型来说,自始至终都是子类对象在做着类型的变化
类型判断
- 还是以之前的代码为示例,如果method方法内想进行输出猫,狗的特有功能该怎么做那?
1
2
3
4
5
6
7
8
9
10
11
12public void method(Animals a){
a.eat();
if(a instanceof Cat){
//instanceof:用于判断对象的具体类型,只能用于引用数据类型判断
//通常在向下转型前用于健壮性的判断
Cat c = Cat(a);
c.catchMouse();
}else if(a instanceof dog){
Dog d = Dog(a);
d.guardDoor();
}
}
多态时成员特点
成员变量
- 编译时:参考引用型变量所属的类中是否有调用的成员变量,有则编译通过,没有则编译失败
- 运行时:参考引用型变量所属的类中是否有调用的成员变量,并运行该所属类中的成员变量
- 编译和运行都参考左边,如 animals a = new Cat();
成员函数(非静态)
- 编译时:参考引用型变量所属的类中是否有调用的函数,有则编译通过,没有则编译失败
- 运行时:参考对象所属的类中是否有调用的函数,
简单说:编译看左边,运行看右边
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class Father {
public void show(){
System.out.println("i am father");
}
}
public class Son extends Father{
public void show() {
System.out.println("i am son");
}
}
public class Test(){
Father f = new Son();
f.show();
}运行后的结果 i am son
静态函数
- 编译时:参考引用型变量所属的类中是否有调用的静态方法,有则编译通过,没有则编译失败
- 运行时:参考对象所属的类中是否有调用的静态方法
- 简单说:编译和运行都看左边
其实对于静态方法,是不需要对象的,直接类名调用即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class Father {
public static void show(){
System.out.println("i am father");
}
}
public class Son extends Father{
public static void show() {
System.out.println("i am son");
}
}
public class Test(){
Father f = new Son();
f.show();
}运行结果为 i am father