注解
注解概念
注解作用:注解不会直接影响代码逻辑,但可以被用来“标记”某些信息,从而让工具或框架“知道”该怎么处理这段代码。注解就像“标签”:你贴在代码的类、方法、字段、参数上,告诉系统或开发者,“这段代码有个特殊的用途”。不同的注解,有不同的作用,可以添加在不同的位置,有的可以写值,有的不能写值
。
JDK支持:注解是JDK1.5新加入的内容。
注解的产生:
Java web开发历程:web项目中会存在大量的配置文件,例如xml yml properties文件等;
配置文 件阅读性差,编写错误不能立即提示;配置文件会增加代码的复杂程度;
JDK开发人员在1.5引入了注解,用于来替代配置文件;
早期:Java代码 + 配置文件
现在:Java代码 + 配置文件 + 注解
注解的优点:简化开发 提高代码可读性;
思想:约定大于配置;
Annotation类
Annotation是一个接口,所有注释类型扩展的公共接口。
构造方法
无
方法
annotationType()
作用:返回此注释的注释类型
参数:无
返回值:类<? extends Annotation>
示例:
常用注解
@override
该注解用于标识一个方法是重写(Override)父类或接口中的方法。它能帮助编译器检查你是否真正重写了父类的方法,防止拼写错误
class Animal {
void speak() {
System.out.println("Animal speaks");
}
}
class Dog extends Animal {
@Override
void speak() {
System.out.println("Dog barks");
}
}
@FunctionalInterface
用于标注一个接口是“函数式接口”(只能有一个抽象方法),可以被 Lambda 表达式使用。
@FunctionalInterface
interface MyFunction {
void execute(); // 只能有一个抽象方法
}
// 使用 Lambda 表达式实现该接口
public class Main {
public static void main(String[] args) {
MyFunction func = () -> System.out.println("Hello from functional interface!");
func.execute();
}
}
@SuppressWarnings("unused")
该注解用于告诉编译器忽略特定的警告信息。"unused"
表示忽略“未使用的变量”警告
public class Demo {
// 注解可以加在方法上和变量上
@SuppressWarnings("unused")
public void doSomething() {
String unusedVar = "I won't be used";
System.out.println("Doing something");
}
}
@Deprecated
标记某个类、方法、字段“不建议使用”,但仍然可以用。提示开发者使用新版本
@Deprecated
public void oldMethod() {
System.out.println("This method is deprecated");
}
public void newMethod() {
System.out.println("Use this method instead");
}
@SuppressWarnings
抑制编译器的警告信息,比如 “unchecked”, “deprecation”, “unused” 等
@SuppressWarnings("deprecation")
public void callDeprecated() {
oldMethod(); // 不会有警告了
}
应用场景
场景 | 示例注解 |
---|---|
编译校验 | @Override , @FunctionalInterface |
开发工具提示 | @Deprecated , @SuppressWarnings |
Java EE/Spring 框架 | @Component , @Autowired , @RequestMapping |
测试框架 | @Test , @Before , @After |
ORM 映射 | @Entity , @Table , @Column |
自定义逻辑(反射) | 自定义注解 + @Retention(RUNTIME) |
元注解
元注解是用于注解“注解”的注解,也就是说,它们用来描述其他注解的行为和作用范围。
换句话说,如果你要定义一个自己的注解(如 @MyAnnotation
),那么就需要用元注解来告诉编译器
1、这个注解应该应用到哪里?
2、它应该保留到什么时候?
3、它能否被继承?
4、能否重复使用?
常见元注解
注解名 | 作用简述 |
---|---|
@Target | 指定注解可以应用到哪些程序元素上(类、方法、字段等) |
@Retention | 指定注解在生命周期中的保留策略(源码 / 类文件 / 运行时) |
@Documented | 指定注解是否会包含在 Javadoc 中 |
@Inherited | 指定注解是否会被子类继承 |
@Repeatable | 允许一个注解在同一位置重复使用(Java 8+) |
元注解示例
✅ 1、@Target
限定注解能用在什么位置上,比如类、方法、字段、参数等,默认不写,代表可以添加到任何位置。
@Target({ElementType.METHOD, ElementType.TYPE}) // 写多个位置
public @interface MyAnnotation {}
📌ElementType常用值:
枚举值 | 说明 |
---|---|
TYPE | 类、接口、枚举 |
FIELD | 成员变量 |
METHOD | 方法 |
PARAMETER | 参数 |
CONSTRUCTOR | 构造函数 |
LOCAL_VARIABLE | 局部变量 |
ANNOTATION_TYPE | 注解类型 |
PACKAGE | 包 |
⚡ 注意:如果注解中只有一个属性值,并且属性值为value,则可以直接写值;其他的情况都必须写为 属性值 = 属性值
形式
// @Target Java源代码内容
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
// 注解中只有一个属性值,并且属性值为value
ElementType[] value();
}
@Target(ElementType.TYPE) // 直接写值
public @interface A1 {
}
✅ 2、@Retention
指定注解的保留策略,即它在什么时候对程 序可见。
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
📌Retention常用值:
枚举值 | 说明 |
---|---|
SOURCE | 注解只在源码中存在,编译后被丢弃(如 @Override ) |
CLASS (默认) | 编译到 class 文件中,运行时不可访问 |
RUNTIME | 编译进 class,运行时可通过反射读取(框架常用) |
✅ 3. @Documented
指定注解是否出现在 Javadoc 中。
@Documented
public @interface MyAnnotation {
}
默认情况下,自定义注解不会出现在 Javadoc 中,除非加了 @Documented
。
✅ 4、@Inherited
指定一个注解是否能被子类继承(仅对类有效)。
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Role {
String value();
}
@Role("admin")
class Parent {}
class Child extends Parent {} // Child 也具有 @Role 注解
注意:只能用于类(ElementType.TYPE
),不能用于方法、字段等。
✅ 5 、@Repeatable
(Java 8+)
允许一个注解在同一个位置重复使用。
@Repeatable(Hints.class)
@interface Hint {
String value();
}
@interface Hints {
Hint[] value();
}
@Hint("hint1")
@Hint("hint2")
public class Person {
}
✅ 示例:自定义注解结合元注解使用
新建注解步骤:鼠标右键,新建JavaClass,选择Annotation。
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogExecution {
String value() default "default";
}
🧪 注解的使用
✅ 1、创建元注解
package com.annotatePart;
import java.lang.annotation.*;
@Retention(RetentionPolicy.SOURCE) // 作用时机在源代码时
@Documented // 生成文档
@Deprecated // 过时标记
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD}) // 修饰范围为类,属性,方法
public @interface A1 {
}
✅ 2、使用注解
package com.annotatePart;
@A1
public class TestAnnotation1 {
@A1
String name;
@A1
public void getName(){
System.out.println("你的名字");
}
}
注解属性
注解中的“属性”,其实就是一组没有方法体的抽象方法,这些方法的名字就是属性名,它们可以有默认值。
示例:
public @interface MyAnnotation {
String name(); // 属性1:必须赋值
int age() default 18; // 属性2:有默认值,可选赋值
}
这段代码定义了一个注解 @MyAnnotation
,它有两个“属性”:
name()
:没有默认值,因此在使用注解时 必须赋值。age()
:有默认值 18,因此在使用时可以省略。
注解属性类型
注解中的属性(即你在注解里定义的方法)只能是编译器允许的“常量表达式类型”,而不是任意 Java 类型。
✅ 允许的属性类型:
类型类别 | 示例 |
---|---|
基本类型 | int , long , boolean , float , 等等 |
String | "abc" |
Class | Class<?> , 如 String.class |
枚举类型 | 自定义枚举,如 Gender.MALE |
注解类型 | 另一个注解,如 @MyNestedAnnotation |
上述类型的数组 | 如 int[] , String[] , Class[] , MyAnnotation[] |
❌ 不允许的属性类型:
类型 | 为什么不允许? |
---|---|
List 、Map 、Set | 因为它们是对象,在注解中不可用运行时构造对象 |
任意普通对象类型 | 比如 Person 、Date 、User ,这些不是“编译时常量” |
接口或自定义类对象 | 同样不可在注解属性中初始化,如 Runnable 、MyService 等 |
🧠 为什么不允许这些类型?
- 注解的属性值必须是编译期常量,也就是说必须在
.class
文件中能直接保存下来的值。 List/Map/Set
、普通对象类型并不是常量,需要运行时构造,而注解本质上是编译期工具的“标签”,不能包含运行时代码行为。
❇️ 注解中可以写方法吗
注解内部的这些所谓“方法”,本质上不是正常 Java 方法(没有方法体),它们就是属性声明。
因此,注解中 不能写带方法体的方法,也不能定义构造方法或静态方法。
🏷 注解中不能写的内容:
内容 | 能否写? | 原因 |
---|---|---|
普通方法 | ❌ | 注解接口不能有方法体 |
构造方法 | ❌ | 注解本质上是接口,不能有构造器 |
静态方法 | ❌ | 注解不能有 static 代码块或 static 方法 |
字段(变量) | ❌ | 注解中不能定义字段,只能定义属性(即方法) |