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

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


















