【Java】多态
前言
在 Java 面向对象编程的三大支柱 —— 封装、继承、多态中,多态无疑是最具 “灵活性” 与 “智慧感” 的存在。它如同现实世界中 “同一行为在不同场景下的多样表现”,让代码摆脱了僵化的绑定,呈现出更贴近生活逻辑的动态特性。
1.多态
顾名思义多态,就是多种态度,哈哈哈开玩笑的,在Java中的概念就是不同的对象调用同一个方法,会产生不同的状态。多态的核心思想是:父类引用可以指向子类对象,并且通过这个引用调用方法时,会执行子类重写后的方法(而非父类的方法)。所以说在多态中,只需要弄懂两个概念就可以理解多态,即向上转型和方法的重写。
2.向上转型
父类的引用引用了子类的对象
2.1向上转型的实现方式
向上转型的实现方式一共有三种,直接赋值,方法的传参,返回值
class Dog extends Aminal{public Dog(String name, int age) {super(name, age);}public void eat(){System.out.println(this.name + " 在吃饭");}@Overridepublic String toString() {return "Dog{" +"name='" + name + '\'' +", age=" + age +'}';}
}class Aminal {public String name;public int age;public Aminal(String name, int age) {this.name = name;this.age = age;}public void eat(){System.out.println(this.name + " 在吃饭");}@Overridepublic String toString() {return "Aminal{" +"name='" + name + '\'' +", age=" + age +'}';}
}public class Text {public static Aminal show1(){return new Dog("小夏",21);}public static void show(Aminal aminal){System.out.println(aminal.toString());}public static void main(String[] args) {Aminal aminal = new Dog("旺财", 18);//直接赋值System.out.println(aminal.toString());show(new Dog("小黄",19));//方法的传参Aminal aminal1 = show1();//通过返回值System.out.println(aminal1.toString());}
}
注:
- 如果子类中的方法,父类没有重写的话,通过父类的引用只能调用自己独特的方法,无法调用子类的方法.
- 向上转型后,若子类重写了父类的方法,通过父类引用调用该方法时,实际执行的是子类重写后的版本(多态的核心体现)。
- 与方法不同,属性不具备多态性。向上转型后,通过父类引用访问的属性一定是父类中声明的属性,而非子类的同名属性。
3.方法的重写
特点:
- 方法名相同。
- 参数列表相同(数量,类型,顺序)。
- 返回值可以不一样,但是两者的返回值必须构成父子关系,除此情况之外,必须一样,否则报错。
3.1 动态绑定
上面那串代码中写了两个重写的方法,eat和toString,在构成向上转型的时候,如果调用的方法是重写的方法,那么在调用该方法的时候,会调用父类的方法,但是实际上是执行子类方法的,这种情况就叫做动态绑定
注:
- 不能被static所修饰,因为静态的方法是属于类的,不依赖于对象,向上转型后调用的是父类的静态方法
- 不能被final所修饰,被 final 修饰的方法无法被重写,向上转型后调用的是父类的 final 方法。
- 重写的时候,子类的限定修饰符必须大于等于父类,否测不能被调用,所以被private修饰的方法,无法被重写。
3.2 静态绑定
典型代表就是重载,在编译的时候,根据用户所传递的实参类型就确定了调用哪个方法。
3.2 向下转型
向下转型是有风险的,因为需要进行强转。如果父类引用指向的是其他类型的对象,强行向下转型会抛出 ClassCastException(类型转换异常)。为了避免 ClassCastException,转型前应使用 instanceof 运算符判断父类引用指向的对象是否为目标子类类型。
public class Text{public static void main(String[] args) {Aminal aminal = new Dog("小黄",21);Aminal aminal = new Cat("小雪",23);if (animal instanceof Dog) {Dog dog = (Dog)aminal;} else {System.out.println("无法转换为Dog类型");}}
}
4.再讲多态
父类引用的对象不同,但调用同一个方法,此时表现出不同的行为,就叫做多态。
注:
- 必须在继承的体系之下
- 子类必须要对父类方法进行重写
- 通过父类的引用调用重写的方法
4.1 多态的优缺点
优点:
- 提高代码的可扩展性。
- 降低代码耦合度。
- 简化代码维护 。
- 支持动态绑定。
- 降低了代码的“圈复杂度”,避免了多次调用大量的if-else。(一段代码中条件语句和循环语句的个数就叫做“圈复杂度”)。
缺点:
- 性能略有损耗。
- 调试难度增加。
- 限制访问子类特有功能。
- 过度设计可能导致逻辑混乱。
- 属性没有多态性。
5.重写和重载的区别(面试题)
5.1重写
定义:子类和父类中间都具有的方法
特点:
- 重写的方法名,子类和父类必须一样
- 重写的方法的参数列表必须一样,包含参数个数,参数类型,参数顺序
- 重写方法的返回值基本上都是一样的,除了返回值是有父子关系的
要求:
- 不能被final和static所修饰
- 子类的访问权限必须大于等于父类的访问权限
- 被private修饰的方法不可以被重写
5.2重载
定义:方法名一致,返回值和参数列表必须不一样,当然两者取一即可
5.3重写和重载的区别
重写的方法名、参数列表和返回值(除返回值具有父子关系之外)必须一样,重载的方法名保持一致即可,返回值和参数必须不一致,一样会报错。