面向对象三大特性---继承

什么是继承

继承的概念来源于现实生活,维基百科将其定义为:在所有权人死亡后,将其财产、债务、爵位、世袭官职等转移给一个或多个继承人继承。而在Java编程语言中,继承是面向对象的三大特性之一。

继承的本质是代码复用,当我们发现多个类之间存在相同的属性或行为时,可以将这些共同特征抽取到一个父类中,然后让具体的子类去继承这个父类。比如,我们可以定义一个Animal类作为父类, 然后让Dog Cat等具体的动物类作为子类来继承它。

让我们用实际例子来理解继承的使用场景:

// 父类:动物类
public class Animal {
    protected String name;
    
    public Animal(String name) {
        this.name = name;
    }
    
    public void eat() {
        System.out.println(name + "正在吃东西");
    }
}

// 子类:猫类
public class Cat extends Animal {
    public Cat(String name) {
        super(name);
    }
}

运行结果:

public class Demo {
    public static void main(String[] args) {
        Animal animal = new Animal("旺财");
        Cat cat = new Cat("咪咪");
        
        animal.eat();      // 输出:旺财正在吃东西
        cat.eat();         // 输出:咪咪正在吃东西
    }
}

我们可以观察到,虽然没有在cat类写任何的属性和方法,但依然可以使用父类的属性和方法

单继承与多继承

单继承

Java采用单继承机制,即一个子类只能有一个直接父类。这种设计避免了许多复杂的问题,使得类的层次结构更加清晰。在上面的例子中,Dog类和Cat类都只继承了Animal这一个父类。

多继承

一些编程语言(如C++)支持多继承,允许一个类同时继承多个父类。虽然这看起来更加灵活,但实际上会带来很多问题:

最典型的问题是菱形继承,假设我们有如下继承结构:

在这里插入图片描述
如果B和C都重写了A类的某个方法,那么D类继承这个方法时就会产生歧义:到底继承哪个父类的实现?俺不中嘞!

C++为了解决这个问题引入了虚继承,但这又增加了语言的复杂性。Java的设计者认为,既然多继承的使用场景相对较少,而带来的问题却很多,不如采用更简洁的单继承模式。

接口:多继承的替代方案

Java通过接口可以弥补单继承的不足。接口定义了一组方法签名,类可以实现多个接口,从而获得类似多继承的效果,但又避免了菱形继承的问题。

// 接口定义
public interface Flyable {
    void fly();
}

public interface Swimmable {
    void swim();
}

// 实现多个接口
public class Duck extends Animal implements Flyable, Swimmable {
    public Duck(String name, int age) {
        super(name, age);
    }
    
    @Override
    public void fly() {
        System.out.println(name + "正在飞翔");
    }
    
    @Override
    public void swim() {
        System.out.println(name + "正在游泳");
    }
}

继承的优缺点分析

优点

  1. 代码复用:子类可以继承父类的属性和方法,减少重复代码的编写。
  2. 扩展性:可以在不修改原有代码的基础上,通过继承来扩展功能。
  3. 多态性基础:继承为多态提供了基础,使得程序更加灵活。
  4. 代码组织:通过继承可以建立清晰的类层次结构,便于理解和维护。

缺点

  1. 耦合度增加:子类与父类之间存在强耦合关系,父类的变化会直接影响子类。
  2. 破坏封装性:子类需要了解父类的内部实现细节,违背了封装的原则。
  3. 维护困难:当父类发生变化时,所有子类都可能需要进行相应的修改,“牵一发而动全身”。
  4. 滥用风险:过度使用继承可能导致类层次过深,增加系统复杂性。

继承的最佳实践

  1. 遵循里氏替换原则:子类应该能够替换父类出现在程序中的任何地方,而不影响程序的正确性。
  2. **优先使用组合而非继承:**当类之间的关系是"has-a"而不是"is-a"时,应该使用组合而不是继承。
  3. 合理使用访问修饰符:
  • 使用protected修饰符来保护父类的内部实现
  • 谨慎使用public继承,避免暴露过多的实现细节
  1. **避免过深的继承层次:**一般建议继承层次不要超过3-4层,过深的继承层次会增加系统的复杂性和维护难度。

现代Java中的继承演进

Java 8的接口改进

从Java 8开始,接口可以包含默认方法(default method)和静态方法,这进一步增强了接口的功能,使其在某种程度上具备了多继承的特性:

public interface Drawable {
    void draw();
    
    // 默认方法
    default void print() {
        System.out.println("正在打印图形");
    }
    
    // 静态方法
    static void info() {
        System.out.println("这是一个可绘制接口");
    }
}

一句话总结

“把继承当成行为契约,而不是代码复用工具;子类签了契约,就必须能随时顶班。”

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值