是,抽象类在字节码中被标记为ACC_ABSTRACT,只要Java源码用abstract关键字修饰该类,无论是否含抽象方法;该标志位于access_flags字段(常量池后2字节),需结合ACC_INTERFACE区分抽象类与接口。

抽象类在字节码中是否被标记为 ACC_ABSTRACT,取决于其 Java 源码是否用 abstract 关键字修饰。这个标志不依赖于类中是否含有抽象方法——哪怕一个抽象类只含具体方法,只要声明为 abstract class,JVM 就会在其 access_flags 中置位 ACC_ABSTRACT。
定位 access_flags 字段位置
Class 文件结构中,access_flags 是紧跟在常量池(constant_pool)之后的 2 字节区域(即 u2 类型),偏移量固定。解析时需按顺序跳过:魔数(4 字节)、主次版本号(各 2 字节)、常量池计数与内容(长度可变),之后的第 2 个字节起就是 access_flags 的起始地址。
- 例如,使用
javap -v查看编译后的类,输出中会明确列出:flags: ACC_PUBLIC, ACC_ABSTRACT - 若用十六进制工具打开
.class文件,找到该 2 字节(如0x0601),将其转为二进制后检查第 7 位(从 1 开始编号,即 bit 6,对应值为0x0100= 256 十进制)是否为 1
ACC_ABSTRACT 标志的语义规则
该标志在 JVM 规范中具有明确含义:它表示该 class 文件描述的是一个抽象类或接口。注意——接口也必须设置此标志(同时还要设置 ACC_INTERFACE),所以单靠 ACC_ABSTRACT 不能区分抽象类和接口,需结合 ACC_INTERFACE 判断:
- 若
ACC_ABSTRACT == 1且ACC_INTERFACE == 0→ 抽象类 - 若
ACC_ABSTRACT == 1且ACC_INTERFACE == 1→ 接口 -
ACC_ABSTRACT == 0且ACC_INTERFACE == 0→ 普通类(非 abstract,非 interface)
验证抽象类字节码的典型场景
写一个最简抽象类:
abstract class A { }
编译后执行:javap -v A | grep "flags:",输出类似:
flags: ACC_SUPER, ACC_ABSTRACT, ACC_SYNTHETIC
说明 JVM 正确设置了 ACC_ABSTRACT;若去掉 abstract 关键字再编译,该标志将消失。即使类中添加抽象方法但未加 abstract 修饰,编译器会报错,不会生成合法 class 文件——因此 ACC_ABSTRACT 是语法层面的强制要求,而非运行时推断。
手动解析时的关键细节
直接读取 access_flags 值后,不能仅靠“是否含抽象方法”来反推该标志是否存在。JVM 不校验抽象类是否真有抽象方法,只校验标志位与类定义是否匹配:
- 抽象类允许不含抽象方法(如仅用于继承约束)
- 普通类即使声明了抽象方法,也无法通过编译,根本不会生成 class 文件
- ASM 等字节码操作库在生成抽象类时,必须显式传入
ACC_ABSTRACT(如cw.visit(V17, ACC_PUBLIC | ACC_ABSTRACT, ...))

















