Java 中的继承与多态
在 Java 面向对象编程中,继承和多态是两大核心特性,它们不仅能帮助我们实现代码复用,还能让程序更具灵活性和可扩展性。下面我们将详细探讨这两个概念。
一.继承:实现代码复用的利器
1.1为什么需要继承
在现实世界中,许多事物之间存在关联,比如狗和猫都属于动物,它们具有一些共同的属性(如名字、年龄)和行为(如吃饭、睡觉)。在 Java 中用类描述这些事物时,如果分别为狗和猫创建类,会出现大量重复的代码。继承机制正是为了解决这一问题,它可以将这些共性内容抽取出来,实现代码的复用。
1.2继承的概念与语法
继承是面向对象编程中实现代码复用的重要手段,它允许程序员在保持原有类特性的基础上进行扩展,生成新的类(派生类)。在 Java 中,通过extends
关键字来表示类之间的继承关系,语法格式为:
修饰符 class 子类 extends 父类 {// ...
}
其中,被继承的类称为父类(基类或超类),继承父类的类称为子类(派生类)。子类可以复用父类中的成员,同时还能添加自己特有的成员。
1.3父类成员的访问规则
- 成员变量:在子类方法中访问成员变量时,遵循就近原则。如果子类中有该成员变量,优先访问子类的;如果没有,则访问父类继承下来的;若父类也没有,则编译报错。
- 成员方法:当成员方法名字不同时,在子类中优先访问自己的方法,自己没有时再到父类中找;当方法名字相同时,若参数列表不同(重载),根据参数选择合适的方法;若参数列表相同,则优先访问子类的方法(重写)。
当需要在子类中明确访问父类的成员时,可以使用super
关键字。
super
关键字:
如果必须要访问和父类同名的方法,我们可以使用父类来访问。
Super.data访问父类的成员变量 Super.func()访问父类的成员方法
super只能指代 当前类的父类不能指代 父类的父类 .
1.4子类构造方法
子类对象构造时,需要先调用父类的构造方法,然后再执行子类的构造方法,即 “先有父再有子”。如果父类显式定义了无参或默认的构造方法,子类构造方法第一行会默认隐含super()
调用;如果父类构造方法带有参数,子类需要显式定义构造方法并调用合适的父类构造方法,且super(...)
必须是子类构造方法中的第一条语句,只能出现一次,不能与this
同时出现。
例如:
public static void main(String[] args) {class Base {private int x;private int y;public Base(int x, int y) {this.x = x;this.y = y;}}class Sub extends Base {private int z;public Sub(int x, int y, int z) {super(x, y);this.z = z;}}
1.5super 与 this 的对比
相同点 | 不同点 |
---|---|
都是 Java 关键字;只能在非静态方法中使用;在构造方法中调用时必须是第一条语句且不能同时存在 | this 是当前对象的引用,用于访问本类的成员和方法;super 是子类对象中从父类继承下来部分成员的引用,用于访问父类的成员和方法 |
1.6初始化顺序
在继承关系中,初始化顺序为:父类静态代码块→子类静态代码块→父类实例代码块→父类构造方法→子类实例代码块→子类构造方法。其中,静态代码块只在类加载阶段执行一次。
1.7继承方式
- 继承方式:Java 支持单继承、多层继承和不同类继承同一个类,但不支持多继承。
- final 关键字:
final
可修饰变量(表示常量,不能修改)、类(表示此类不能被继承)和方法(表示该方法不能被重写)。
1.8继承与组合
继承体现的是 “A是B” 的关系(如狗是动物),组合体现的是 “A有B” 的关系(如汽车有发动机)。两者都能实现代码复用,一般建议能用组合尽量用组合。
组合例子:
class Heart {public void beat() {System.out.println("心脏跳动");}
}class Person {private Heart heart; // 人包含心脏的引用public Person() {this.heart = new Heart(); // 创建人时初始化心脏}public void live() {heart.beat(); // 人通过心脏跳动维持生命}
}
Person
类在内部维护了一个Heart
对象的引用。Heart
对象在Person
的构造函数中被创建,其生命周期与Person
实例绑定。- 当
Person
对象被销毁时,Heart
对象也会随之被销毁。 Person
通过调用heart.beat()
方法来使用其内部组件的功能。
二.多态:灵活多样的行为表现
2.1多态的概念
多态指的是完成某个行为时,不同的对象去完成会产生不同的状态,即 “同一件事情,发生在不同对象身上,会产生不同的结果”。
2.2多态的实现条件
要实现多态,必须满足以下三个条件:
- 存在继承体系;
- 子类对父类中的方法进行重写;
- 通过父类的引用调用重写的方法。
2.3方法重写
重写(override)是子类对父类非静态、非private
修饰、非final
修饰、非构造方法的实现过程进行重新编写,要求返回值类型、方法名和参数列表完全一致(返回值类型可以是具有父子关系的)。重写时,子类方法的访问权限不能比父类中被重写的方法的访问权限更低,可使用@Override
注解进行合法性校验。
重写与重载的区别如下:
区别点 | 重写(override) | 重载(overload) |
---|---|---|
参数列表 | 一定不能修改 | 必须修改 |
返回类型 | 一定不能修改(除非具有父子关系) | 可以修改 |
访问限定符 | 不能做更严格的限制(可以降低限制) | 可以修改 |
2.4向上转型与向下转型
- 向上转型:创建一个子类对象,将其当成父类对象来使用,语法为 “父类类型 对象名 = new 子类类型 ()”。它能让代码更简单灵活,但不能调用子类特有的方法。
- 向下转型:将经过向上转型的父类引用还原为子类对象,需要强制转换。这种操作不安全,可能会抛出
ClassCastException
,可使用instanceof
关键字先判断,再进行转换以提高安全性。
2.5多态的优缺点
- 优点:能降低代码的 “圈复杂度”,避免使用大量的
if-else
语句;可扩展能力更强,新增子类时无需修改原有代码。 - 缺点:代码的运行效率降低;属性没有多态性,通过父类引用只能访问父类的成员属性;构造方法没有多态性。
注意事项
应避免在构造方法中调用被重写的方法,因为此时子类对象还未构造完成,可能会出现隐藏的问题。