保留策略:CLASS与RUNTIME的区别

57

RetentionPolicy.CLASSRetentionPolicy.RUNTIME之间的实际区别是什么?

看起来两者都被记录到字节码中,而且两者在运行时都可以访问。


可能是重复的问题:Java注释 - 寻找RetentionPolicy.CLASS示例 - skaffman
1
请查看https://dev59.com/oHA75IYBdhLWcg3wxsNn。 - skaffman
2
使用反射API无法在运行时访问CLASS - Adam Arold
CLASS注解可以被直接操作字节码的特殊库使用。例如,BCEL - 字节码工程库。 - Vitaly
请在此处阅读更多内容 - https://stackoverflow.com/a/59236293/4770877 - yoAlex5
3个回答

74

无论哪种方式,都可以在运行时访问。

这并不是javadoc所说的:

RUNTIME: 编译器将注释记录在类文件中,并且保留在运行时的VM中,因此可以通过反射读取。

CLASS: 编译器将注释记录在类文件中,但不需要在运行时的VM中保留。

实际上,我不知道任何使用CLASS的用例。它只有在编程方式读取字节码而不是通过类加载器API时才有用,但那是一个非常专门的情况,我不知道为什么你不会只使用RUNTIME

具有讽刺意味的是,默认行为是CLASS


我已经阅读了。听起来“不需要”并不排除“可能性”。这就是为什么会有这个问题的原因。 - Dima
7
如果直接阅读字节码,你可以从中提取出它。如果使用反射 API,它可能不会存在。 - skaffman
可能是对的。不确定在这种情况下存在RetentionPolicy.CLASS是否有太多意义。也许最初背后还有其他东西... - Dima
CLASS 还有另一个用途:编译时仅使用的注释,例如 Proguard 的 @Keep 注释用于排除类、方法不被混淆。 - RobertB
据说 findbugs 提供了一些具有 CLASS 保留策略的注解。 - Tiina
显示剩余5条评论

7
看起来两者都被记录在字节码中,无论如何都可以在运行时访问。
对于基本的内置注解接口比如getAnnotations是错误的。例如:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.CLASS)
@interface RetentionClass {}

@Retention(RetentionPolicy.RUNTIME)
@interface RetentionRuntime {}

public static void main(String[] args) {
    @RetentionClass
    class C {}
    assert C.class.getAnnotations().length == 0;

    @RetentionRuntime
    class D {}
    assert D.class.getAnnotations().length == 1;
}

因此,观察 RetentionPolicy.CLASS 注释的唯一方法是使用字节码解析器。

另一个区别在于,使用 Retention.CLASS 注释的类会获得一个 RuntimeInvisible 类属性,而使用 Retention.RUNTIME 注释则会获得一个 RuntimeVisible 类属性。可以使用 javap 进行观察。

在 GitHub 上的示例 供您参考。


0

默认情况下,注解不可通过反射 API 访问。 (RetentionPolicy.CLASS 是默认的保留策略)

您可以为自定义注解指定其是否应在运行时可用,以便通过反射进行检查。您可以通过使用 @Retention 注解对注解定义进行注释来实现此目的。

尝试这个简单的例子

@Retention(RetentionPolicy.RUNTIME)
public @interface Version {
    int test();
}

@Version(test= 1)
public class Engineer{
   //code
}

public class Main {
        
    public static void main(String[] args) {
        Engineer engineer = new Engineer();
    
        Annotation[] annotations = engineer.getClass().getAnnotations();
        System.out.printf("%d annotations found.", annotations.length);
    }
}

尝试通过将Version注释的RetentionPolicy更改为RetentionPolicy.CLASS再次运行代码并检查差异。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接