admin管理员组文章数量:1032181
为什么缺少Annotations不会导致类未找到异常
1. 概述
在本教程中,我们将熟悉 Java 编程语言中一个看似奇怪的功能:缺少注释不会在运行时导致任何异常。
然后,我们将深入挖掘,看看控制这种行为的原因和规则,以及这些规则的例外情况。
2. 快速复习
让我们从一个熟悉的 Java 示例开始。有A类,然后有B类,这取决于A:
代码语言:javascript代码运行次数:0运行复制public class A {
}
public class B {
public static void main(String[] args) {
System.out.println(new A());
}
}
现在,如果我们编译这些类并运行编译后的B,它将在控制台上为我们打印一条消息:
代码语言:javascript代码运行次数:0运行复制>> javac A.java
>> javac B.java
>> java B
A@d716361
但是,如果我们删除编译的A.class文件并重新运行类B,我们将看到由ClassNotFoundException 引起的NoClassDefFoundError:
代码语言:javascript代码运行次数:0运行复制>> rm A.class
>> java B
Exception in thread "main" java.lang.NoClassDefFoundError: A
at B.main(B.java:3)
Caused by: java.lang.ClassNotFoundException: A
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:606)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:168)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
... 1 more
发生这种情况是因为类装入器在运行时找不到类文件,即使它在编译期间存在。这是许多Java开发人员所期望的正常行为。
3. 缺少注释
现在,让我们看看在相同情况下注释会发生什么。为此,我们将A类更改为注释:
代码语言:javascript代码运行次数:0运行复制@Retention(RetentionPolicy.RUNTIME)
public @interface A {
}
如上所示,Java 将在运行时保留注释信息。之后,是时候用A 注释B类了:
代码语言:javascript代码运行次数:0运行复制@A
public class B {
public static void main(String[] args) {
System.out.println("It worked!");
}
}
接下来,让我们编译并运行这些类:
代码语言:javascript代码运行次数:0运行复制>> javac A.java
>> javac B.java
>> java B
It worked!
因此,我们看到B在控制台上成功打印了其消息,这是有道理的,因为所有内容都编译并很好地连接在一起。
现在,让我们删除A 的类文件:
代码语言:javascript代码运行次数:0运行复制>> rm A.class
>> java B
It worked!
如上所示,即使缺少注释类文件,注释类也会运行,没有任何异常。
3.1. 使用类标记进行注解
为了使它更有趣,让我们引入另一个具有Class<?> 属性的注释:
代码语言:javascript代码运行次数:0运行复制@Retention(RetentionPolicy.RUNTIME)
public @interface C {
Class<?> value();
}
As shown above, this annotation has an attribute named value with the return type of Class<?>. As an argument for that attribute, let's add another empty class named D:
代码语言:javascript代码运行次数:0运行复制public class D {
}
Now, we're going to annotate the B class with this new annotation:
代码语言:javascript代码运行次数:0运行复制@A
@C(D.class)
public class B {
public static void main(String[] args) {
System.out.println("It worked!");
}
}
当所有类文件都存在时,一切应该都可以正常工作。但是,当我们只删除D类文件而不触摸其他文件时会发生什么?让我们来了解一下:
代码语言:javascript代码运行次数:0运行复制>> rm D.class
>> java B
It worked!
如上所示,尽管运行时没有D,但一切仍在工作!因此,除了注释之外,属性中引用的类标记也不需要在运行时存在。
3.2. Java 语言规范
因此,我们看到一些具有运行时保留的注释在运行时丢失,但注释类运行良好。尽管听起来出乎意料,但根据Java 语言规范 §9.6.4.2,此行为实际上完全没问题:
注释可能仅存在于源代码中,也可能以类或接口的二进制形式存在。二进制形式存在的注释在运行时可能通过 Java SE 平台的反射库提供,也可能不可用。
此外,JLS §13.5.7条目还指出:
添加或删除注释不会影响 Java 编程语言中程序二进制表示的正确链接。
底线是,运行时不会因缺少注释而引发异常,因为 JLS 允许这样做。
3.3. 访问缺失的注释
让我们更改B类,使其以反射方式检索A信息:
代码语言:javascript代码运行次数:0运行复制@A
public class B {
public static void main(String[] args) {
System.out.println(A.class.getSimpleName());
}
}
如果我们编译并运行它们,一切都会好起来的:
代码语言:javascript代码运行次数:0运行复制>> javac A.java
>> javac B.java
>> java B
A
现在,如果我们删除A类文件并运行B,我们将看到由ClassNotFoundException 引起的相同 NoClassDefFoundError:
代码语言:javascript代码运行次数:0运行复制Exception in thread "main" java.lang.NoClassDefFoundError: A
at B.main(B.java:5)
Caused by: java.lang.ClassNotFoundException: A
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:606)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:168)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
... 1 more
根据 JLS 的说法,注释不必在运行时可用。但是,当其他代码读取该注释并对其进行处理时(就像我们所做的那样),该注释必须在运行时存在。否则,我们将看到ClassNotFoundException。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2023-03-13,如有侵权请联系 cloudcommunity@tencent 删除二进制异常javaannotations编译为什么缺少Annotations不会导致类未找到异常
1. 概述
在本教程中,我们将熟悉 Java 编程语言中一个看似奇怪的功能:缺少注释不会在运行时导致任何异常。
然后,我们将深入挖掘,看看控制这种行为的原因和规则,以及这些规则的例外情况。
2. 快速复习
让我们从一个熟悉的 Java 示例开始。有A类,然后有B类,这取决于A:
代码语言:javascript代码运行次数:0运行复制public class A {
}
public class B {
public static void main(String[] args) {
System.out.println(new A());
}
}
现在,如果我们编译这些类并运行编译后的B,它将在控制台上为我们打印一条消息:
代码语言:javascript代码运行次数:0运行复制>> javac A.java
>> javac B.java
>> java B
A@d716361
但是,如果我们删除编译的A.class文件并重新运行类B,我们将看到由ClassNotFoundException 引起的NoClassDefFoundError:
代码语言:javascript代码运行次数:0运行复制>> rm A.class
>> java B
Exception in thread "main" java.lang.NoClassDefFoundError: A
at B.main(B.java:3)
Caused by: java.lang.ClassNotFoundException: A
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:606)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:168)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
... 1 more
发生这种情况是因为类装入器在运行时找不到类文件,即使它在编译期间存在。这是许多Java开发人员所期望的正常行为。
3. 缺少注释
现在,让我们看看在相同情况下注释会发生什么。为此,我们将A类更改为注释:
代码语言:javascript代码运行次数:0运行复制@Retention(RetentionPolicy.RUNTIME)
public @interface A {
}
如上所示,Java 将在运行时保留注释信息。之后,是时候用A 注释B类了:
代码语言:javascript代码运行次数:0运行复制@A
public class B {
public static void main(String[] args) {
System.out.println("It worked!");
}
}
接下来,让我们编译并运行这些类:
代码语言:javascript代码运行次数:0运行复制>> javac A.java
>> javac B.java
>> java B
It worked!
因此,我们看到B在控制台上成功打印了其消息,这是有道理的,因为所有内容都编译并很好地连接在一起。
现在,让我们删除A 的类文件:
代码语言:javascript代码运行次数:0运行复制>> rm A.class
>> java B
It worked!
如上所示,即使缺少注释类文件,注释类也会运行,没有任何异常。
3.1. 使用类标记进行注解
为了使它更有趣,让我们引入另一个具有Class<?> 属性的注释:
代码语言:javascript代码运行次数:0运行复制@Retention(RetentionPolicy.RUNTIME)
public @interface C {
Class<?> value();
}
As shown above, this annotation has an attribute named value with the return type of Class<?>. As an argument for that attribute, let's add another empty class named D:
代码语言:javascript代码运行次数:0运行复制public class D {
}
Now, we're going to annotate the B class with this new annotation:
代码语言:javascript代码运行次数:0运行复制@A
@C(D.class)
public class B {
public static void main(String[] args) {
System.out.println("It worked!");
}
}
当所有类文件都存在时,一切应该都可以正常工作。但是,当我们只删除D类文件而不触摸其他文件时会发生什么?让我们来了解一下:
代码语言:javascript代码运行次数:0运行复制>> rm D.class
>> java B
It worked!
如上所示,尽管运行时没有D,但一切仍在工作!因此,除了注释之外,属性中引用的类标记也不需要在运行时存在。
3.2. Java 语言规范
因此,我们看到一些具有运行时保留的注释在运行时丢失,但注释类运行良好。尽管听起来出乎意料,但根据Java 语言规范 §9.6.4.2,此行为实际上完全没问题:
注释可能仅存在于源代码中,也可能以类或接口的二进制形式存在。二进制形式存在的注释在运行时可能通过 Java SE 平台的反射库提供,也可能不可用。
此外,JLS §13.5.7条目还指出:
添加或删除注释不会影响 Java 编程语言中程序二进制表示的正确链接。
底线是,运行时不会因缺少注释而引发异常,因为 JLS 允许这样做。
3.3. 访问缺失的注释
让我们更改B类,使其以反射方式检索A信息:
代码语言:javascript代码运行次数:0运行复制@A
public class B {
public static void main(String[] args) {
System.out.println(A.class.getSimpleName());
}
}
如果我们编译并运行它们,一切都会好起来的:
代码语言:javascript代码运行次数:0运行复制>> javac A.java
>> javac B.java
>> java B
A
现在,如果我们删除A类文件并运行B,我们将看到由ClassNotFoundException 引起的相同 NoClassDefFoundError:
代码语言:javascript代码运行次数:0运行复制Exception in thread "main" java.lang.NoClassDefFoundError: A
at B.main(B.java:5)
Caused by: java.lang.ClassNotFoundException: A
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:606)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:168)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
... 1 more
根据 JLS 的说法,注释不必在运行时可用。但是,当其他代码读取该注释并对其进行处理时(就像我们所做的那样),该注释必须在运行时存在。否则,我们将看到ClassNotFoundException。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2023-03-13,如有侵权请联系 cloudcommunity@tencent 删除二进制异常javaannotations编译本文标签: 为什么缺少Annotations不会导致类未找到异常
版权声明:本文标题:为什么缺少Annotations不会导致类未找到异常 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://it.en369.cn/jiaocheng/1747922199a2228182.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论