为什么不保留注释字符串值?
Why are annotation string values not interned?
尽管重复使用了字符串常量和文字,但以下代码片段打印了 4 个不同的哈希码。为什么字符串值不驻留在注释元素上?
public class Foo {
@Retention(RetentionPolicy.RUNTIME)
@interface Bar {
String CONSTANT = "foo";
String value() default CONSTANT;
}
public static void main(String[] args) throws Exception {
System.out.println(System.identityHashCode(Bar.CONSTANT));
System.out.println(System.identityHashCode(Foo.class.getMethod("test1").getAnnotation(Bar.class).value()));
System.out.println(System.identityHashCode(Foo.class.getMethod("test2").getAnnotation(Bar.class).value()));
System.out.println(System.identityHashCode(Foo.class.getMethod("test3").getAnnotation(Bar.class).value()));
}
@Bar
public void test1() {}
@Bar("foo")
public void test2() {}
@Bar(Bar.CONSTANT)
public void test3() {}
}
字符串文字是驻留的,但注释是经过解析的,并且它们存储在字节数组中。如果您查看 class java.lang.reflect.Method
,您会看到:
private byte[] annotations;
private byte[] parameterAnnotations;
private byte[] annotationDefault;
另请查看相同 class 的方法 public Object getDefaultValue()
,了解 AnnotationParser 的调用方式。流程一直持续到这里
AnnotationParser.parseConst 并输入
case 's':
return constPool.getUTF8At(constIndex);
方法 ConstantPool.getUTF8At
是本机方法的委托。您可以在此处查看代码 native implementation getUFT8At。解析的常量永远不会被驻留,也永远不会从 StringTable 中检索(其中字符串被驻留)。
我认为这可能是一种实施选择。创建实习生是为了在字符串文字之间进行更快速的比较,因此仅用于方法实现中可用的实习生文字。
这是因为您在运行时访问注解并根据java spec - Example 3.10.5-1. String Literals,字符串是新创建的,因此是不同的。
All literal strings and compile-time string-valued constant
expressions are automatically interned
在您的情况下,来自 test1
的值将在运行时从本机 value() 方法计算(查看 AnnotationDefault attribute)。
String value() default CONSTANT;
其他情况也将在运行时计算。
当您从注解中获取值时,您必须明确执行 intern
String poolString = Foo.class.getMethod("test1").getAnnotation(Bar.class).value().intern();
System.out.println(poolString == Bar.CONSTANT);
尽管重复使用了字符串常量和文字,但以下代码片段打印了 4 个不同的哈希码。为什么字符串值不驻留在注释元素上?
public class Foo {
@Retention(RetentionPolicy.RUNTIME)
@interface Bar {
String CONSTANT = "foo";
String value() default CONSTANT;
}
public static void main(String[] args) throws Exception {
System.out.println(System.identityHashCode(Bar.CONSTANT));
System.out.println(System.identityHashCode(Foo.class.getMethod("test1").getAnnotation(Bar.class).value()));
System.out.println(System.identityHashCode(Foo.class.getMethod("test2").getAnnotation(Bar.class).value()));
System.out.println(System.identityHashCode(Foo.class.getMethod("test3").getAnnotation(Bar.class).value()));
}
@Bar
public void test1() {}
@Bar("foo")
public void test2() {}
@Bar(Bar.CONSTANT)
public void test3() {}
}
字符串文字是驻留的,但注释是经过解析的,并且它们存储在字节数组中。如果您查看 class java.lang.reflect.Method
,您会看到:
private byte[] annotations;
private byte[] parameterAnnotations;
private byte[] annotationDefault;
另请查看相同 class 的方法 public Object getDefaultValue()
,了解 AnnotationParser 的调用方式。流程一直持续到这里
AnnotationParser.parseConst 并输入
case 's':
return constPool.getUTF8At(constIndex);
方法 ConstantPool.getUTF8At
是本机方法的委托。您可以在此处查看代码 native implementation getUFT8At。解析的常量永远不会被驻留,也永远不会从 StringTable 中检索(其中字符串被驻留)。
我认为这可能是一种实施选择。创建实习生是为了在字符串文字之间进行更快速的比较,因此仅用于方法实现中可用的实习生文字。
这是因为您在运行时访问注解并根据java spec - Example 3.10.5-1. String Literals,字符串是新创建的,因此是不同的。
All literal strings and compile-time string-valued constant expressions are automatically interned
在您的情况下,来自 test1
的值将在运行时从本机 value() 方法计算(查看 AnnotationDefault attribute)。
String value() default CONSTANT;
其他情况也将在运行时计算。
当您从注解中获取值时,您必须明确执行 intern
String poolString = Foo.class.getMethod("test1").getAnnotation(Bar.class).value().intern();
System.out.println(poolString == Bar.CONSTANT);