“继承是代码复用的利器,多态是面向对象编程的灵魂。”
Java 作为一门面向对象的编程语言,继承与多态是其核心特性之一。理解这两者不仅能帮助你写出更优雅的代码,还能让你在面试中脱颖而出。本文将带你深入探索 Java 继承与多态的世界,从基础概念到高级应用,逐步揭开它们的神秘面纱。
继承是面向对象编程中的一个重要概念,它允许一个类(子类)继承另一个类(父类)的属性和方法。通过继承,子类可以复用父类的代码,并在此基础上进行扩展或修改。
class Animal {
void eat() {
System.out.println("Animal is eating");
}
}
class Dog extends Animal {
void bark() {
System.out.println("Dog is barking");
}
}
在上面的代码中,Dog
类继承了 Animal
类,因此 Dog
类可以使用 Animal
类中的 eat()
方法。
Java 支持单继承,即一个类只能有一个直接父类。但可以通过多层继承实现类似多继承的效果。
继承类型 | 描述 |
---|---|
单继承 | 一个类只能有一个直接父类 |
多层继承 | 子类可以继承父类,父类又可以继承另一个类 |
方法重写是子类对父类方法的重新实现。重写的方法必须与父类方法具有相同的方法签名(方法名、参数列表)和返回类型。
// Animal.java
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
System.out.println("Animal构造器执行");
}
public void eat() {
System.out.println(name + "正在进食");
}
}
// Dog.java
public class Dog extends Animal { // 继承语法
private String breed;
public Dog(String name, String breed) {
super(name); // 必须首先调用父类构造器
this.breed = breed;
System.out.println("Dog构造器执行");
}
@Override
public void eat() { // 方法重写
super.eat(); // 调用父类实现
System.out.println(name + "正在啃骨头");
}
public void bark() {
System.out.println("汪汪!我是" + breed);
}
}
在上面的代码中,Dog
类重写了 Animal
类的 eat()
方法,因此 Dog
类调用 eat()
方法时,执行结果为:Dog .eat()
的结果。
重写规则对比表
特性 | 父类方法 | 子类重写方法 |
---|---|---|
访问修饰符 | protected | public/protected |
返回类型 | Animal | Dog(协变类型) |
异常声明 | IOException | 不声明或子异常 |
方法签名 | eat(String food) | eat(String food) |
static修饰 | 允许 | 不允许(隐藏而非重写) |
特性 | 方法重写(Override) | 方法重载(Overload) |
---|---|---|
方法签名 | 必须一致 | 必须不同 |
返回类型 | 必须一致或协变 | 可以不同 |
访问权限 | 不能更严格 | 可以不同 |
异常 | 不能更广泛 | 可以不同 |
在 Java 中,子类的构造方法会隐式调用父类的构造方法。如果父类没有无参构造方法,子类必须显式调用父类的构造方法。
class Animal {
Animal(String name) {
System.out.println("Animal constructor: " + name);
}
}
class Dog extends Animal {
Dog(String name) {
super(name); // 显式调用父类构造方法
System.out.println("Dog constructor: " + name);
}
}
在上面的代码中,Dog
类的构造方法通过 super(name)
显式调用了 Animal
类的构造方法。
public class Main {
public static void main(String[] args) {
Dog dog = new Dog("Buddy");
}
}
输出结果:
Animal constructor: Buddy
Dog constructor: Buddy
动态绑定是 Java 多态的核心机制。它允许在运行时确定调用哪个方法,而不是在编译时。
Java 通过虚方法表(vtable)实现动态绑定。每个类都有一个虚方法表,其中存储了该类所有可重写方法的地址。当调用一个方法时,JVM 会根据对象的实际类型查找虚方法表,找到对应的方法并执行。
class Animal {
void speak() {
System.out.println("Animal is speaking");
}
}
class Dog extends Animal {
@Override
void speak() {
System.out.println("Dog is barking");
}
}
public class Main {
public static void main(String[] args) {
Animal myAnimal = new Dog();
myAnimal.speak(); // 输出: Dog is barking
}
}
在上面的代码中,myAnimal
的实际类型是 Dog
,因此调用 speak()
方法时,JVM 会查找 Dog
类的虚方法表,找到 Dog
类的 speak()
方法并执行。
方法名 | 地址 |
---|---|
speak | 0x1000 |
eat | 0x2000 |
虽然继承提供了代码复用的便利,但它也存在一些缺陷。过度使用继承会导致代码耦合度高,难以维护。因此,组合优于继承的原则被广泛提倡。
组合是指在一个类中引用另一个类的对象,通过调用该对象的方法来实现功能。组合比继承更灵活,耦合度更低。
class Engine {
void start() {
System.out.println("Engine is starting");
}
}
class Car {
private Engine engine;
Car(Engine engine) {
this.engine = engine;
}
void start() {
engine.start();
}
}
在上面的代码中,Car
类通过组合的方式使用了 Engine
类的功能,而不是通过继承。
维度 | 继承 | 组合 |
---|---|---|
关系类型 | IS-A | HAS-A |
耦合度 | 高(编译时绑定) | 低(运行时绑定) |
灵活性 | 父类修改影响所有子类 | 可动态替换组件 |
复用方式 | 白盒复用 | 黑盒复用 |
扩展性 | 受限于单继承 | 支持多组件组合 |
多态是面向对象编程的三大特性之一(封装、继承、多态)。它允许不同的类对同一消息做出不同的响应。多态的应用场景非常广泛,下面我们通过几个实际案例来探讨多态的应用。
多态分为编译时多态和运行时多态。编译时多态主要通过方法重载实现,而运行时多态主要通过方法重写实现。
class Calculator {
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
}
在上面的代码中,Calculator
类通过方法重载实现了编译时多态。
class Animal {
void speak() {
System.out.println("Animal is speaking");
}
}
class Dog extends Animal {
@Override
void speak() {
System.out.println("Dog is barking");
}
}
public class Main {
public static void main(String[] args) {
Animal myAnimal = new Dog();
myAnimal.speak(); // 输出: Dog is barking
}
}
在上面的代码中,myAnimal
的实际类型是 Dog
,因此调用 speak()
方法时,JVM 会调用 Dog
类的 speak()
方法,这是运行时多态的体现。
Java 集合框架中的 List
接口是一个典型的多态应用场景。List
接口有多种实现类,如 ArrayList
和 LinkedList
,它们对同一方法(如 add()
)有不同的实现。
List<String> list = new ArrayList<>();
list.add("Hello");
list = new LinkedList<>();
list.add("World");
在上面的代码中,list
变量可以指向 ArrayList
或 LinkedList
,这是多态的体现。
工厂模式是一种常见的设计模式,它通过多态来实现对象的创建。
interface Shape {
void draw();
}
class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing Circle");
}
}
class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Drawing Rectangle");
}
}
class ShapeFactory {
Shape getShape(String type) {
if (type.equals("Circle")) {
return new Circle();
} else if (type.equals("Rectangle")) {
return new Rectangle();
}
return null;
}
}
public class Main {
public static void main(String[] args) {
ShapeFactory factory = new ShapeFactory();
Shape shape = factory.getShape("Circle");
shape.draw(); // 输出: Drawing Circle
}
}
在上面的代码中,ShapeFactory
类通过多态返回不同的 Shape
对象,这是工厂模式的典型应用。
虽然继承提供了代码复用的便利,但过度使用继承会导致代码耦合度高,难以维护。因此,建议在以下情况下使用组合而不是继承:
接口是实现多态的重要工具。通过接口,可以定义一组方法,让不同的类实现这些方法,从而实现多态。
interface Flyable {
void fly();
}
class Bird implements Flyable {
@Override
public void fly() {
System.out.println("Bird is flying");
}
}
class Airplane implements Flyable {
@Override
public void fly() {
System.out.println("Airplane is flying");
}
}
public class Main {
public static void main(String[] args) {
Flyable flyable = new Bird();
flyable.fly(); // 输出: Bird is flying
flyable = new Airplane();
flyable.fly(); // 输出: Airplane is flying
}
}
在上面的代码中,Flyable
接口定义了一个 fly()
方法,Bird
和 Airplane
类分别实现了这个方法,从而实现了多态。
抽象类是一种介于接口和具体类之间的类,它可以提供部分方法的默认实现,同时要求子类实现其他方法。
abstract class Animal {
abstract void speak();
void eat() {
System.out.println("Animal is eating");
}
}
class Dog extends Animal {
@Override
void speak() {
System.out.println("Dog is barking");
}
}
public class Main {
public static void main(String[] args) {
Animal myAnimal = new Dog();
myAnimal.speak(); // 输出: Dog is barking
myAnimal.eat(); // 输出: Animal is eating
}
}
在上面的代码中,Animal
类是一个抽象类,它提供了 eat()
方法的默认实现,同时要求子类实现 speak()
方法。
Vehicle
和一个子类 Car
,要求 Car
类重写 Vehicle
类的 start()
方法。Drawable
,并实现两个类 Circle
和 Rectangle
,要求通过多态调用它们的 draw()
方法。Computer
类,通过组合的方式使用 CPU
、Memory
和 HardDisk
类。继承与多态是 Java 面向对象编程的核心特性,理解它们不仅能帮助你写出更优雅的代码,还能让你在面试中脱颖而出。通过本文的学习,你应该已经掌握了继承与多态的基本概念、应用场景以及优化建议。希望你能在实际开发中灵活运用这些知识,写出高质量的代码。
“代码是写给人看的,顺便给机器执行。” —— 《代码大全》
分享到知乎 | 分享到掘金 | 分享到微博 | 分享到 QQ | 分享到 Stack Overflow
#Java全栈开发 #SpringBoot实战 #JVM调优 #微服务架构
期待与你相遇!
如果你对编程充满热情,想获取丰富的编程学习资料,如经典编程书籍、实用教程等,欢迎加入我们的大家庭!点击云盘链接获取入群方式,扫码添加入群,即可领取海量编程资源!一起提升技能,开启你的编程进阶之旅!
上一篇:Java 封装与访问控制
下一篇:Java 抽象类与接口