代码之家  ›  专栏  ›  技术社区  ›  Matt McHenry

为什么在运行时缺少注释不会导致ClassNotFoundException?

  •  80
  • Matt McHenry  · 技术社区  · 14 年前

    考虑以下代码:

    爪哇语:

    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    @Retention(RetentionPolicy.RUNTIME)
    @interface A{}
    

    C.java语言:

    import java.util.*;
    
    @A public class C {
            public static void main(String[] args){
                    System.out.println(Arrays.toString(C.class.getAnnotations()));
            }
    }
    

    按预期编译和运行工作:

    $ javac *.java
    $ java -cp . C
    [@A()]
    

    $ rm A.class
    $ java -cp . C
    []
    

    我本以为它会 ClassNotFoundException ,自 @A 不见了。但是相反,它会默默地删除注释。

    这种行为是在JLS的某个地方记录的,还是Sun的JVM的一个怪癖?它的理由是什么?

    javax.annotation.Nonnull (看起来应该是 @Retention(CLASS)

    3 回复  |  直到 14 年前
        1
  •  92
  •   Chris Povirk    3 年前

    在JSR-175(annotations)的早期公开草案中,讨论了编译器和运行时是否应该忽略未知的注释,以便在注释的使用和声明之间提供更松散的耦合。一个具体的例子是在EJB上使用应用服务器特定的注释来控制部署配置。如果相同的bean应该部署在不同的应用服务器上,那么如果运行时只是忽略未知的注释而不是引发NoClassDefFoundError,将会很方便。

    即使措辞有点模糊,我也认为你看到的行为是 JLS 13.5.7 : "... 删除注释对Java编程语言中程序的二进制表示的正确链接没有影响,程序应该仍然链接并运行,这意味着当通过反射访问时,将忽略未知的注释。

    Sun的jdk5的第一个版本没有正确地实现这一点,但是它在1.5.0\u06中得到了修复。你可以找到相关的bug 6322301 但是它没有指向任何规范,只是声称“根据JSR-175规范的领导,未知的注释必须被getAnnotations忽略”。

        2
  •  35
  •   Guillaume    14 年前

    注释只能出现在源代码中,或者 它们可能以二进制形式出现 类或接口的。 注解 在运行时可能无法通过 Java的反射库 站台。

    注释类型 注释.保留用于选择 注释a对应于类型T, T有一个(元)注释m 然后:

    • 如果m有一个值为annotation.RetentionPolicy.SOURCE源, 那么Java编译器必须确保 二进制文件中不存在 显示的接口。
    • 批注.RetentionPolicy.RUNTIME一 Java编译器必须确保 用二进制表示 出现a的接口,除非m 注释局部变量 宣言。局部区域上的注释

    如果T没有(meta-)注释 m对应于 注释.保留,然后是Java 编译器必须像对待T一样对待它 有这样一个元注释 值为的元素

    所以呢保留策略.RUNTIME确保注释被编译为二进制文件,但二进制文件中的注释在运行时不必可用

        3
  •  10
  •   irreputable    14 年前

    如果您实际有读取@A并对其执行某些操作的代码,那么该代码对类A有依赖关系,它将抛出ClassNotFoundException。

    如果不是,也就是说,没有代码特别关心@A,那么@A其实并不重要,这是有争议的。