06、继承 -- 面向对象的特性

  • 继承(Inheritance)-- 代码重用扩展(导致子类父类耦合)。
  • 作用:
    • 1、表示 is-a 关系
    • 2、支持多态特性。
    • 3、代码复用

1、继承的概述

  • 继承是用来表示之间is-a 关系
    • 如:猫是一种哺乳动物。

  • 关键字 extends 指示正在构造的新类派生于一个已存在的类。
    • 这个已存在的称为超类(superclass)、基类(base class)、父类(parent class)
    • 新类称为子类(subclass / child class)、派生类(derived class)
  • 超类子类 是 Java 程序员最常用的两个术语。
    • ”(super)和 “”(sub)来源于计算机科学数学理论集合语言的术语。
    • 所有员工组成的集合包含所有经理组成的集合。
      • 所以,可以说,员工集合是经理集合的超集;经理集合是员工集合的子集

2、继承的模式

  • 继承关系上来讲,继承可以分为两种模式:
    • 单继承多继承
  • 单继承:表示一个子类只继承一个父类
  • 多继承:表示一个子类可以继承多个父类
    • 如:猫既是哺乳动物,又是爬行动物。

3、继承所需的支持

  • 为了实现继承这个特性,编程语言需要提供特殊的语法机制 来支持。
    • Java 使用 extends 关键字来实现继承
    • C++ 使用冒号(class B : public A)。
    • Python 使用 paraentheses()。
    • Ruby 使用 <

  • 有些编程语言只支持单继承不支持多重继承
    • 如: Java、PHP、C#、Ruby 等。
  • 有些编程语言既支持单继承又支持多重继承
    • 如:C++、Python、Perl 等。

4、Java 不支持多继承的原因

原因解释
菱形继承问题避免方法/属性冲突,简化继承逻辑。
语言设计简洁性减少构造函数、内存布局和类型系统的复杂性。
接口的替代性通过接口多实现和默认方法提供灵活性。
组合优于继承鼓励更灵活、低耦合的代码复用方式。
代码可维护性单一继承层次更清晰,减少调试和维护难度。
  • 1、避免菱形继承钻石继承问题
    • 指一个类同时继承两个父类,而这两个父类又继承自同一个祖先类,导致方法属性冲突
      • 会导致调用该方法时的二义性(该执行哪个父类方法呢?)。
    • C++ 通过虚继承(Virtual Inheritance)解决此问题,但会引入额外的复杂性
class A {
    void method() { System.out.println("A"); }
}
class B extends A {
    void method() { System.out.println("B"); }
}
class C extends A {
    void method() { System.out.println("C"); }
}
// 假设允许多继承:
class D extends B, C { } // 编译错误:method() 继承自 B 和 C,冲突!

  • 2、可以简化语言设计和实现
    • 构造函数链的复杂性
      • 多继承需要处理多个父类的构造函数 调用顺序,可能导致初始化逻辑混乱
    • 内存布局复杂性
      • 对象需要包含所有父类字段内存布局可能变得复杂,影响性能和维护性。
    • 类型系统复杂性
      • 多继承可能导致类型转换二义性(如:instanceof 判断)。

  • 3、Java 接口可以实现多继承的效果

  • 4、组合优于继承(Composition over Inheritance)
    • Java 鼓励用组合(将其它类的对象作为成员)而非继承来实现代码复用。更灵活且避免耦合。
class Engine { /* 引擎功能 */ }
class Wheel { /* 轮胎功能 */ }

// 通过组合实现复用,而非多继承
class Car {
    private Engine engine;
    private Wheel[] wheels;
    // 使用 engine 和 wheels 的功能
}
  • 5、保持代码清晰和可维护性
    • 减少二义性
      • 单继承使类层次结构更清晰,开发者可以明确知道方法的来源。
    • 简化调试
      • 继承链单一,调试时更容易追踪代码行为。

5、Java 中隐藏多继承

  • 如:代码是可以执行成功的。
    • 子接口的默认方法优先级更高
package org.rainlotus.materials.javabase.a04_oop.multiExtend;

/**
 * 隐藏的多继承(子接口的默认方法优先级更高。)
 */
public class Test1 {
    public static void main(String[] args) {
        Tom tom = new Tom();
        // 小孩子吃饭
        tom.eat();
    }
}

interface People{
    default void eat(){
        System.out.println("人吃饭");
    }
}

interface Man{
    default void eat(){
        System.out.println("男人吃饭");
    }
}

interface Boy extends People, Man {
    @Override
    default void eat(){
        System.out.println("小孩子吃饭");
    }
}

class Tom implements People, Boy{

}
  • 如:代码是编译报错的。
    • 类 org.rainlotus.materials.javabase.a04_oop.multiExtend.Jim 从类型 org.rainlotus.materials.javabase.a04_oop.multiExtend.People 和 org.rainlotus.materials.javabase.a04_oop.multiExtend.Man 中继承了 eat() 的不相关默认值
public class Test1 {
    public static void main(String[] args) {
        Jim jim = new Jim();
        // 小孩子吃饭
        jim.eat();
    }
}

interface People{
    default void eat(){
        System.out.println("人吃饭");
    }
}

interface Man{
    default void eat(){
        System.out.println("男人吃饭");
    }
}

// 会编译报错
class Jim implements People, Man {

}
  • 解决办法:在 Jim 类中,重写 eat() 方法
class Jim implements People, Man{

    @Override
    public void eat() {
        People.super.eat();
    }
}

6、继承的优缺点

  • 优点:
    • 继承的本质在于 抽象
      • 类是对对象的抽象,继承是对某一批类的抽象
    • 为了提高代码的复用性
      • 子类拥有父类的所有属性方法,从而实现代码的复用
  • 缺点:
    • 1、子类和父类高度耦合修改父类的代码,会直接影响 到子类。
    • 2、继承层次过深过复杂,就会导致代码可读性可维护性变差

7、阻止继承:final 类 和 方法

  • 有时候,我们希望阻止用户定义某个类的子类。不允许被扩展的类被称为 final 类
    • 如果在类定义中使用了 final 修饰符,就表明这个类是 final 类
  • 注意:枚举记录 总是 final 修饰的,它们不允许扩展。

  • 在早期的 java 中,有些程序员为了避免动态绑定带来的系统开销而使用 final 关键字
    • 如果一个方法没有被覆盖并且很短,编译器就能够对它进行优化处理
      • 这个过程称为 内联(inlining)。
        • 如:内联调用 e.getName() 将把它替换为访问字段 e.name。
    • 这是一项很有意义的改进,CPU 在处理当前指令时,分支会扰乱预取指令的策略。
      • 所以,CPU 不喜欢分支。
    • 如果 getName 可能在另外一个类中被覆盖,那么编译器就无法知道覆盖代码将会做什么操作。
      • 因此,也就不能对它进行 内联 处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值