1. 多态的定义
多态是Java面向对象的三大特性之一,它允许不同类型的对象对同一方法进行不同的实现。具体来说就是去完成某个行为,不同的对象去完成时会产生出不同的状态。
比如,狗和猫都是动物,但完成吃饭这个动作时,会有吃狗粮和吃猫粮这两种状态。
public class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void eat(){
System.out.println(name+"在吃饭");
}
}
public class Cat extends Animal{
public Cat(String name){
super(name);
}
@Override
public void eat() {
System.out.println(name+"在吃猫粮");
}
}
public class Dog extends Animal{
public Dog(String name){
super(name);
}
@Override
public void eat() {
System.out.println(name+"在吃狗粮");
}
}
public class TestAnimal {
public static void eat(Animal a){
a.eat();
}
public static void main(String[] args) {
Cat cat = new Cat("富贵");
Dog dog = new Dog("旺财");
eat(cat);
eat(dog);
}
}
当我们定义出猫类,狗类以及动物类中相同的eat方法时,在测试类中,编译器并不知道要调用的是Dog中的eat还是Cat中的eat,只有形参a所引用的对象确定后,才可以决定出运行哪个方法。(这里的形参必须使用父类类型)
2. 多态的条件
由上面的例子可以总结出,要在Java中实现多态,必须满足几个条件:
1. 必须在继承体系下;
2. 子类必须要对父类中方法进行重写;
3. 通过父类的引用调用重写的方法。
3. 重写
重写(Override,也称覆盖),是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程
进行重新编写,子类中方法的名称、返回值类型、参数列表与父类相同,只有方法体中的实现不同。
规则:
- 子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名 (参数列表) 要完全一致;
- 被重写的方法返回值类型可以不同,但是必须是具有父子关系的;
- 访问权限不能比父类中被重写的方法的访问权限更低;
- 父类被static、private修饰的方法、构造方法都不能被重写;
- 重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验。
4. 向上转型
向上转型指的是,父类引用了一个子类对象。例如
Animal animal = new Dog("元宝");
但其实,还存在别的形式的向上转型。一共有三种:
- 直接赋值
- 作为方法的参数进行传递
public class Main {
public static void func1(Animal animal){
}
public static void main(String[] args) {
Dog dog = new Dog("小四");
func1(dog);
}
}
这里的func1中接收的参数为Animal,但是我们仍然可以用dog进行传参。
3. 通过返回类型,也可能向上转型。
public static Animal func2(){
Dog dog = new Dog("小四");
return dog;
}
这里的func2中的返回类型为Animal,但是我们仍然可以用dog来进行返回。
5. 动态绑定
当父类引用子类对象后,当子类重写了父类的方法后,通过父类的引用调用父类和子类重写的方法,最终结果显示是调用了子类的方法,这个过程叫做动态绑定。
public class Dog extends Animal{
public Dog(String name){
super(name);
}
@Override
public void eat() {
System.out.println(name+"在吃狗粮");
}
}
public class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void eat(){
System.out.println(name+"在吃饭");
}
public static void main(String[] args) {
Animal animal = new Dog("元宝");
animal.eat();
}
}
这里通过Animal引用了Dog对象,在子类中也重写了父类的eat方法,最后通过animal.eat调用了子类Dog中的eat方法,实现了动态绑定。
通过对向上转型和动态绑定的学习,我们才能进一步地去理解多态这一特性。