Java多态

发布于:2024-10-16 ⋅ 阅读:(6) ⋅ 点赞:(0)

1.多态

1.1 多态的概念

多态,通俗来说,就是多种形态。总的来说,就是同一件事情,发生在不同的对象身上,就会产生不同的结果。
比如,同一个老师给学生上课,翠花的成绩特别优秀,小明虽然成绩不行吧,但是小明游戏玩的还不错,先不考虑有点气人这一点,这怎么不算是一种成绩呢。

1.2 多态实现条件

在Java中要实现多态,必须要满足以下几个条件,缺一不可:

  1. 必须在继承体系下
  2. 子类必须要对父类中的方法进行重写
  3. 通过父类的引用调用重写的方法

多态体现:在代码运行时,当传递不同的对象时,会调用对应类中的方法

//Animal类
public class Animal {
    String name;
    int age;
    public Animal(String name, int age){
        this.name = name;
        this.age = age;
    }
    public void eat(){
        System.out.println(name + "吃饭");
    }
}
//继承了Animal的cat类
public class Cat extends Animal{
    public Cat(String name, int age){
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(name + "吃鱼");
    }
}
//继承了Animal的dog类
public class Dog extends Animal{
    public Dog(String name, int age){
        super(name, age);
    }
    public void eat(){
        System.out.println(name + "吃骨头");
    }
}
//测试类
public class TestAnimal {
    public static void eat(Animal a){
        a.eat();
    }
    //编译器在编译代码时,并不知道要调用Dog 还是Cat 中的eat方法
    //等程序运行起来后,形参a引用父类的具体对象确定后,才知道调用哪个方法
    public static void main(String[] args) {
        Cat cat = new Cat("小猫",2);
        Dog dog = new Dog("小狗",1);在这里插入图片描述

        eat(cat);
        eat(dog);
    }
}

首先我们写了四个类:
Animal , 继承了Animal的cat, 继承了Animal的dog
Animal、cat、dog这三个类中都有eat这个方法
还写了一个测试类TestAnimal类,这个类里面也有eat方法,并且需要注意的是,这个eat方法的参数是Animal类型,这就说明这里我们可以传的有三个选项,Animal可以传,由于cat和dog它们继承了Animal类,所以这两个类我们也可以传它们的对象.

public static void eat(Animal a){
        a.eat();
    }

大家看到这样的一个参数不要觉得很复杂,其实跟我们平时经常用的 int 和 String 没什么不一样,不要被它的样子唬住了.
这个方法编译器在编译代码时,并不知道要调用的是哪个类里面的eat,当程序运行起来,形参 a 引用的具体对象确定后,才知道要调用哪个方法,需要注意的是,此处的形参类型必须是父类类型才可以.

Cat cat = new Cat("小猫",2);
Dog dog = new Dog("小狗",1);
eat(cat);
eat(dog);

上面的这几行代码分别传了三个不同类的对象,我们可以来看一下此处代码的运行结果:
在这里插入图片描述

由于 a 引用不同的实例调用eat方法可能会有多种不同的表现,这种行为就称为多态

1.3 重写

重写(override):也成为覆盖。重写是子类对父类非静态、非private修饰、非final修饰,非构造方法等的实现过程进行重新编写,返回值和形参都不能改变。即外壳不变,核心重写!
重写的好处就在于子类可以根据需要,定义自己的行为,也就是说子类能够根据父类的需要实现父类的方法。
【方法重写的规则】

  • 子类在重写父类的方法时,一般必须与父类方法原型一致,具体表现在:返回值类型、方法名、参数列表,要完全一致.
  • 被重写的方法返回值类型可以不同,但是必须是具有父子关系的.
  • 访问权限不能比父类中被重写的方法的访问权限更低. 例如,如果父类方法被public修饰,则子类中重写该方法就不能声明为protected.
  • 父类被static、private修饰的方法、构造方法都不能重写.
  • 重写的方法,可以使用@Override注解来显式指定. 有了这个注解可以帮我们进行一些合法性校验. 例如,不小心将方法名字拼错了,那么此时编译器就会发现父类中并不存在这个方法,就会编译报错,提示无法构成重写.
    【重写和重载的区别】
区别点 重写(override) 重载(overload)
参数列表 一定不能修改 必须修改
返回类型 一定不能修改(除非可以构成父子类关系) 可以修改
访问限定符 一定不能做更严格的限制(可以降低限制) 可以修改

即:方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现.
【重写的设计原则】
对于已经投入使用的类,尽量不进行修改。最好的方式是:重新定义一个类,来重复利用其中共性的内容,并且添加或者改动新的内容。

静态绑定:
也成为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用哪个方法,典型代表是方法重载。
动态绑定:
也成为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用哪个类的方法。