为什么注释字符串值不被暂存?

以下代码段打印 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() {}
}

答案 1

字符串文本被暂存,但批注需要分析,它们存储在字节数组中。如果你看一下这个类,你可以看到这个:java.lang.reflect.Method

private byte[]              annotations;
private byte[]              parameterAnnotations;
private byte[]              annotationDefault;  

另请查看同一类的方法,了解如何调用 AnnotationParser。该流程一直持续到此处 AnnotationParser.parseConst 并输入public Object getDefaultValue()

case 's':
  return constPool.getUTF8At(constIndex);

该方法是本机方法的委托。你可以在这里看到代码原生实现getUFT8At。解析的常量从不被暂存,也从不从 StringTable(其中字符串被暂存)中检索。ConstantPool.getUTF8At

我认为这可能是一种实施选择。创建 Interning 是为了在 String 文本之间进行更快速的比较,因此仅用于在方法实现中可用的文本。


答案 2

这是因为您可以在运行时访问注释,并符合java规范 - 示例3.10.5-1。字符串文本,字符串是新创建的,因此是不同的。

所有文本字符串和编译时字符串值常量表达式都将自动暂存

在你的例子中,from 的值将在运行时从本机 value() 方法计算(查看 AnnotationDefault 属性)。test1

String value() default CONSTANT;

其他情况也将在运行时计算。

当您从注释中获取值时,您必须显式执行实习生

String poolString = Foo.class.getMethod("test1").getAnnotation(Bar.class).value().intern();
System.out.println(poolString == Bar.CONSTANT); 

推荐