Skip to content

抽象和继承的区别

约 1493 字大约 5 分钟

Java基础理论

2025-11-10

继承是一个纵向的层级。

抽象是横向的层级。

继承是为了能复用代码,建立类之间的层次关系。

抽象是隐藏具体的实现细节,只暴露行为的设计思想。

抽象和继承的区别:纵向层级 vs 横向契约

在 Java 面向对象编程中,继承(Inheritance)抽象(Abstraction) 是两个核心机制。它们经常被同时提及,但解决的是不同维度的问题。

简单来说:

  • 继承是“纵向”的 is-a 关系 —— 解决“谁是谁”的问题
  • 抽象是“横向”的 can-do 规范 —— 解决“具备什么能力”的问题

一、继承:纵向的 is-a 关系

核心思想

继承是一种代码复用机制,建立类之间的层次关系,表示“是一个(is-a)”的关系。子类从父类那里获得属性和方法,可以在不重复编写代码的前提下进行扩展或重写。

典型场景

  • 猫是一个动物
  • 支付宝是一种支付方式
  • 人民币是一种货币

这些都可以用继承来建模。

class Pay {
    String name;
    
    public void introduction() {
        System.out.println("我是" + name);
    }
    
    public void deductMoney() {
        System.out.println("扣减余额");
    }
}

class Alipay extends Pay {
    @Override
    public void deductMoney() {
        System.out.println("调用支付宝接口扣减余额");
    }
}

class WeChatPay extends Pay {
    @Override
    public void deductMoney() {
        System.out.println("调用微信接口扣减余额");
    }
}

public class Main {
    public static void main(String[] args) {
        Pay pay = new Alipay();
        // 子类可以直接使用父类的属性和方法
        pay.name = "支付宝";
        pay.introduction();
        // 如果子类重写了父类的方法,则调用的就是子类自己的方法
        pay.deductMoney();
    }
}

输出:

我是支付宝
调用支付宝接口扣减余额

Alipay 和 WeChatPay 都属于 Pay 支付工具,都可以用来做“扣减余额”这一操作。通过继承,子类复用了父类的通用行为(如 introduction),并可以根据需要重写特定方法(如 deductMoney),实现多态。


二、抽象:定义行为规范,隐藏实现细节

核心思想

抽象是一种设计思想:它不关心“怎么做”,只关心“能不能做”。

它的目的是将共通的行为从具体实现中剥离出来,形成一个统一的“能力契约”。调用方只需要依赖这个契约,而无需知道背后的实现细节。

这正是面向对象中 “针对接口编程,而不是针对实现编程” 的体现。

抽象关注的核心是:“能做什么”(can-do)

比如:

  • 所有支付方式都“能扣款”、“能退款”
  • 所有交通工具都“能启动”、“能停止”
  • 所有用户都“能登录”、“能查看信息”

这些“能力”应该被抽象出来,作为所有实现类必须遵守的协议。

如何实现抽象?

在 Java 中,抽象可以通过两种方式实现:

  1. 抽象类(abstract class
  2. 接口(interface

两者都可以定义抽象方法(没有方法体的方法),强制子类去实现。

示例:使用抽象类定义支付行为

// 抽象类:定义支付工具必须具备的能力
abstract class Pay {
    String name;

    // 通用方法(有默认实现)
    public void introduction() {
        System.out.println("我是" + name);
    }

    // 抽象方法:必须由子类实现
    public abstract void deductMoney(); // 扣款
    public abstract void refund();      // 退款
}

这个 Pay 类本身不能被实例化,但它规定了:任何继承它的子类,都必须实现 deductMoney()refund() 方法

具体实现类

class Alipay extends Pay {
    @Override
    public void deductMoney() {
        System.out.println("调用支付宝接口完成扣款");
    }

    @Override
    public void refund() {
        System.out.println("发起支付宝原路退款");
    }
}

class WeChatPay extends Pay {
    @Override
    public void deductMoney() {
        System.out.println("调用微信支付接口扣款");
    }

    @Override
    public void refund() {
        System.out.println("通过微信支付系统退款");
    }
}

使用示例

public class Main {
    public static void main(String[] args) {
        Pay pay = new WeChatPay(); // 多态:父类引用指向子类对象
        pay.name = "微信支付";
        
        pay.introduction(); // 通用行为
        pay.deductMoney();  // 具体实现
        pay.refund();       // 具体实现
    }
}

输出:

我是微信支付
调用微信支付接口扣款
通过微信支付系统退款

抽象的本质:横向的能力约束

我们可以这样理解:

维度说明
方向横向层级(不是父子关系,而是能力对齐)
关注点“这个对象有没有某种能力?”
设计目标解耦、可扩展、可替换
典型模式策略模式、工厂模式、模板方法

想象一下插头和插座的关系:

  • 不同国家的插头形状不同(实现不同)
  • 但只要符合“三孔标准”(抽象接口),就能插入同一个插座
  • 插座不关心你是国产还是进口,只关心你“能不能插上”

这就是抽象的力量:让不同的实现,遵循相同的规则,从而可以被统一调用


抽象 vs 实现:解耦的关键

没有抽象时,代码是这样的:

// 紧耦合:直接依赖具体实现
Alipay alipay = new Alipay();
alipay.deductMoney();

有了抽象后,代码变成:

// 松耦合:依赖抽象
Pay pay = getPayInstance(); // 可能是支付宝、微信、银联
pay.deductMoney(); // 运行时决定调用哪个实现

这才是企业级开发中推崇的 高内聚、低耦合 设计。


三、总结对比

特性继承(Inheritance)抽象(Abstraction)
关系类型is-a(是什么)can-do(能做什么)
层级方向纵向(上下级)横向(能力对齐)
主要目的代码复用、结构扩展行为规范、解耦实现
实现方式extendsabstract classinterface
是否强制实现否(可直接使用父类方法)是(抽象方法必须实现)
设计重点类的层级与共享接口的统一与隔离

一句话总结:

继承告诉我们“你是谁”,抽象告诉我们“你能干什么”。 优秀的系统设计,往往是“用抽象定义能力,用继承实现差异”。