一、什么是多态
多态是同一个行为具有多个不同表现形式或形态的能力;是同一个接口,使用不同的实例而执行不同操作。
具体的语法体现:向上转型,方法重写,动态绑定。
多态性是对象多种表现形式的体现。
举例说明:现实中,比如我们按下 F1 键这个动作:
- 如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;
- 如果当前在 Word 下弹出的就是 Word 帮助;
- 在 Windows 下弹出的就是 Windows 帮助和支持。
- 同一个事件发生在不同的对象上会产生不同的结果。
二、向上转型
定义:父类的引用指向了一个子类的对象。向上转型是多态的一个重要的语法基础。
Cat cat = new Cat();
Animal animal = null;
animal = cat;
//上面的代码可以合并到一起
Animal animal = new Cat();
此时,animal是一个父类(Animal)的引用,指向一个子类(Cat)的实例。这就是向上转型。
向上转型发生的时机:
直接赋值
方法传参
方法返回
上面是直接赋值的例子,现在来说:方法传参
public class Test {
public static void main(String[] args) {
Cat cat = new Cat();
func(new Cat());
}
public static void func(Animal animal) {
animal.eat("猫粮");
}
}
此时形参animal的类型是Animal (基类),实际上对应到Cat (父类)的实例。
方法返回:
方一:
public static Animal func2(){
Cat cat = new Cat();
Animal animal =cat;
return animal;
}
方二:
public static Animal func2(){
return new Cat();
}
此时方法func2返回的是一个Animal类型的引用,但是实际上对应到Cat 的实例。
其中,方法返回和方法传参,本质上都是“赋值"。
三、动态绑定
如果父类中包含的方法在子类中有对应的同名同参数的方法,就会进行动态绑定。
注意:一般"静态”/“动态"分别指的是"编译期”/“运行时"和"static”没任何关系。
举例说明:
Animal animal = new Cat();
animal.eat("鱼");
如果eat方法只在父类中存在,此时调用的eat 就是父类的这个eat方法。(不涉及动态绑定)
如果eat方法只在子类中存在,此时调用的eat就会编译报错。(也不涉及动态绑定)
如果eat方法在父类和子类中都存在,并且参数也相同,此时调用eat 就会涉及到动态绑定。
在程序运行时,看animal究竟是指向一个父类的实例还是子类的实例:指向父类实例就调用父类版本的eat,指向子类实例就调用子类版本的eat。
四、方法重写
子类实现父类的同名方法,并且参数的类型和个数完全相同,这种情况称为重写。
重写与重载的区别:
区别 | 概念 | 范围 | 限制 |
---|---|---|---|
重载 | 方法名称相同,参数的类型及个数不同 | 一个类 | 没有权限要求 |
重写 | 方法名称、返回值类型、参数类型及个数完全相同 | 继承关系 | 被覆写的方法不能拥有比父类更严格的访问控制权限 |
重写的注意事项:
1、普通方法可以重写, static修饰的静态方法不能重写;
2、重写中子类的方法的访问权限不能低于父类的方法访问权限;
3、重写的方法返回值类型不一定和父类的方法相同(但是建议最好写成相同,特殊情况除外)。
4、重写的方法,可以使用@Override注解来显式指定。
public c1ass Bird extends Animal {
@Override
private void eat(string food) {
·····
}
}
给子类的方法上加上注解是为了显式的告诉编译器,当前这个子类的方法是重写了父类的方法。
如果没有这个注解,其实代码也能编译运行。只是加了这个注解之后,编译器就能给我们更检查和校验工作。
五、多态的优点
1、多态这种设计思想,本质上是“封装"的更进一步。
封装的目的是为了让类的使用者不需要知道类的实现细节,就能使用,但是使用者仍然需要知道这个类是啥类型。
使用多态的话,此时类的使用者,不仅不需要知道类的实现细节,也不需要知道这个类具体是啥类型,只要知道这个类有一个方法就可以了~~这个时候类的使用者知道的信息更少,使用成本就更低。
2、减少一些分支语句(减少if / else switch / case)
3、可扩展能力更强。
如果要新增一种新的实例,使用多态的方式代码改动成本也比较低。
对于类的调用者来说,只要创建一个新类的实例就可以了,改动成本很低。
而对于不用多态的情况,就要把代码中的if - else进行一定的修改,改动成本更高。