java泛型元数据通过signature属性在字节码中存储,不参与运行时类型实例化,仅支持反射等工具还原泛型信息;该属性位于类、字段、方法三处,指向常量池中的jvm签名字符串,如list编码为ljava/util/list;。

Java 中泛型元数据不是运行时“存在”的类型,而是靠编译器在生成 .class 文件时,把泛型结构编码成特定字符串,存进 Signature 属性里。这个属性本身不存泛型逻辑,只存一个索引,真正内容藏在常量池中。
Signature 属性是 JVM 规范定义的标准属性,专为保留泛型信息而设
它不改变运行时行为(JVM 仍执行类型擦除),但为反射、序列化、IDE 等工具提供还原泛型的能力。关键在于:它只在字节码层面“记一笔”,不参与类加载后的类型实例化。
Signature 属性出现在三类位置,对应不同泛型场景
- 类声明处:比如
class Box<t extends number></t>,其泛型参数T的约束信息就记在这里 - 字段定义处:比如
private List<string> items;</string>,字段的完整参数化类型签名就放这儿 - 方法定义处:比如
<k> Map<k> build()</k></k>或public <t> T get()</t>,方法自身的类型变量和返回值泛型都靠它记录
每个位置的 Signature 属性结构一致,都是:
-
attribute_name_index→ 指向常量池中"Signature"字符串 -
attribute_length→ 固定为 2 字节(因为只存一个u2) -
signature_index→ 指向常量池中一个CONSTANT_Utf8_info项,里面才是真正的签名字符串
签名字符串用 JVM 特有语法编码,不是 Java 源码写法
它是一套紧凑、无空格、带特殊符号的类型描述格式,例如:
-
List<string></string>→Ljava/util/List<ljava>;</ljava> -
Map<integer extends charsequence></integer>→Ljava/util/Map<ljava>;</ljava> - 泛型方法
<t extends comparable>> T get()</t>→<ljava>>TT;</ljava>
注意细节:
-
L开头、;结尾表示类类型 -
<...></...>包裹类型变量声明或参数化类型 -
T后跟标识符(如T)再加;表示对类型变量的引用(TT;就是引用T) -
+表示上界通配符(? extends),-表示下界(? super) - 原始类型(如
int)用单字母(I),数组用[,多维数组叠加[
泛型信息最终不进 Metaspace 的运行时类型结构
JVM 加载类时会解析 Signature 属性,提取签名字符串并构建反射可用的元数据节点(比如 TypeVariableImpl、ParameterizedTypeImpl),但这些只是供 getGenericXxx() 调用的轻量对象;List<string></string> 和 List<integer></integer> 在运行时共享同一个 java.util.List 的 Klass,不额外占元空间。真正的泛型区分只存在于字节码 + 常量池 + Signature 属性这套组合里。
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











