1. 什么是 API?
API(Application Programming Interface)描述了软件组件之间如何交互。
这种交互可以是两个应用通过网络通信,比如一个 HTTP API 可以通过 OpenAPI 规范定义,其他应用可以根据这个规范来调用接口。它会明确请求的格式、路径、参数以及返回值的结构。
但 API 不仅限于网络接口。例如 Java 中的类库也有 API,它定义了哪些类、方法、字段是对外暴露的,其他程序或库可以如何调用它。
✅ API 是抽象接口的集合,不局限于网络。
比如:
- 数据库的 schema 也是一种 API,因为它定义了数据的结构和约束
- 消息队列中的消息格式也可以视为 API
- 文件格式(如 CSV、JSON)也可以是 API 的一部分
⚠️ 改变这些 API 都可能影响依赖它的系统,哪怕它们不是传统意义上的接口。
2. 什么是 ABI?
ABI(Application Binary Interface)是 API 在编译后的二进制形式中所表现出来的接口。
在 Java 中,一个类库的 API 是它的源代码接口,而 ABI 则是编译后的 .class
文件。JVM 通过 ABI 来识别类和方法。
2.1. API 与 ABI 的关系
虽然 ABI 通常由 API 编译而来,但它们之间并不总是完全同步:
✅ 某些 API 改动不会影响 ABI
比如 Java 的泛型擦除:
public List<String> getStrings() {} public List<Integer> getInts() {}
这两个方法在源码中不同,但编译后都变成:
public List getStrings() {} public List getInts() {}
所以从 ABI 角度看,这两个方法是重复的,会导致编译错误。
✅ 某些 ABI 改动不影响 API
比如用不同版本的 Java 编译器编译相同的源代码,生成的
.class
文件可能不同(如 Java 8 vs Java 17),但 API 是一致的。
2.2. 有些 API 没有对应的 ABI
不是所有 API 都有 ABI:
- ✅ 解释型语言如 Python、JavaScript 没有编译输出,因此没有 ABI
- ✅ HTTP 接口的 API 是请求/响应格式,但它的 ABI 是网络上传输的字节流(包括 TCP/IP、SSL、HTTP 协议细节等)
⚠️ 但在实际开发中,我们通常不会关心 HTTP 的 ABI,因为我们通过 HTTP 客户端库(如 Apache HttpClient、OkHttp)来隐藏这些细节。
3. API 与 ABI 的兼容性
当依赖的库发生 API 或 ABI 改动时,我们的应用可能需要做相应调整。
3.1. ABI 兼容性
- ✅ 如果 ABI 未变,即使库更新了,我们的应用也无需重新编译
- ✅ 如果 ABI 改变了,但 API 没变,我们需要重新编译应用,但不需要修改代码
例如:类中某个字段的内存偏移变了,或者常量值变了
3.2. API 兼容性
- ❌ 如果 API 改动是破坏性的(如删除方法、改变参数),我们必须修改代码才能继续使用
例如:方法签名改变、删除字段、接口变更等
4. 版本控制与兼容性(Semantic Versioning)
为了清晰表达接口变更的影响,建议使用 语义化版本号(Semantic Versioning):主版本号.次版本号.修订号
4.1. 版本号含义
版本层级 | 示例 | 含义 |
---|---|---|
主版本号 | 2.0.0 | API 或 ABI 有重大不兼容变更 |
次版本号 | 1.2.0 | 增加了新功能,但 API 兼容 |
修订版本 | 1.1.1 | 仅修复 bug,API 与 ABI 均未变 |
4.2. 实际场景举例
- ✅
1.1.0 → 1.1.1
:仅修复 bug,无需任何改动 - ✅
1.1.0 → 1.2.0
:新增了方法或字段,旧代码仍可运行,但可能需要重新编译 - ❌
1.2.0 → 2.0.0
:有破坏性变更,需要修改代码才能升级
5. 总结
对比点 | API | ABI |
---|---|---|
定义 | 源代码层面的接口 | 二进制层面的接口 |
示例 | 类/方法定义、HTTP 接口 | .class 文件、网络字节流 |
改动影响 | 可能需要修改代码 | 可能需要重新编译 |
版本策略 | 主要影响主版本号 | 主要影响次版本号 |
✅ 理解 API 与 ABI 的区别,有助于我们在做接口设计、版本管理、依赖升级时更准确地评估影响范围,避免踩坑。