@Target(ElementType.FIELD)表示自定义注解仅限用于字段,需配合@Retention(RetentionPolicy.RUNTIME)才能在运行时通过反射读取并校验,如NotNull注解校验字段非空。

<p>@Target(ElementType.FIELD) 表示该自定义注解只能用在类的成员变量(即字段)上,不能用在方法、类、参数等其他位置。</p>
<H3>配置方式:在自定义注解上声明 @Target</H3>
<p>你需要在定义注解时,显式加上 <strong>@Target(ElementType.FIELD)</strong>,例如:</p>
<font color="blue">
<pre>
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotNull {
String message() default "字段不能为空";
}
</pre>
</font>
<p>这样定义后,这个 <code>@NotNull</code> 注解就只能标注在字段上,比如:</p>
<font color="blue">
<pre>
public class User {
@NotNull
private String name; // ✅ 合法
@NotNull
public void setName(String name) { } // ❌ 编译报错:非法使用注解
}
</pre>
</font>
<H3>为什么必须配合 @Retention?</H3>
<p>仅加 <code>@Target</code> 不够,运行时要读取注解信息,还需指定生命周期。通常搭配 <strong>@Retention(RetentionPolicy.RUNTIME)</strong>,否则反射无法获取。</p>
<ul>
<li>如果只用 <code>@Retention(RetentionPolicy.SOURCE)</code>,注解仅保留在源码阶段,编译后就丢弃了</li>
<li>如果用 <code>CLASS</code>,注解保留在字节码但反射拿不到</li>
<li>需要在运行时通过反射检查字段是否带注解,就必须选 <code>RUNTIME</code></li>
</ul>
<H3>实际校验字段注解的简单示例</H3>
<p>配合反射,在运行时检查某个对象的字段是否被标记并做校验:</p>
<font color="blue">
<pre>
public static void validate(Object obj) throws IllegalAccessException {
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(NotNull.class)) {
field.setAccessible(true);
Object value = field.get(obj);
if (value == null || (value instanceof String && ((String) value).trim().isEmpty())) {
throw new IllegalArgumentException(field.getName() + " 不能为空");
}
}
}
}
</pre>
</font>
<p>调用时:<code>validate(new User());</code> 就会检查所有带 <code>@NotNull</code> 的字段值。</p>
<H3>常见误用提醒</H3>
<ul>
<li>不要写成 <code>@Target({ElementType.FIELD})</code> 以外的类型,比如混入 <code>METHOD</code>,那就不再“只作用于字段”了</li>
<li>不要漏掉 <code>@Retention</code>,否则注解存在但反射看不到</li>
<li>字段需设为 <code>accessible</code> 才能读取私有字段值,否则会抛 <code>IllegalAccessException</code></li>
</ul>