【Java基础】注解——自定义注解

什么是注解?

Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。 比如我们常见的@Override和@Deprecated都是注解,注解可以加在类、方法、成员变量等上面,类似于给他们“打标签"。

注解怎么定义?

public @interface 注解名{} 看起来和定义接口很相似只是多了一个@符号 接口: public interface 接口名 注解: public @interface 注解名

public @interface lkx {
    
}

注解怎么使用?

现在我们注解已经定义好了,使用的时候直接“@注解名”就可以使用了 比如下面我们可以定义在“类、成员变量、成员方法”上:

@lkx
public class Test {
    @lkx
    private int num;
    
    @lkx
    public static void main(String[] args) {
        System.out.println("hello");
    }
}
  • 思考一下

现在注解定义好也已经使用了,但是我不想定义到类上和成员方法上,只想定义在成员方法上,如何才能让注解只能定义到方法上,定义到别的地方报错呢? 想要的效果: 【Java基础】注解——自定义注解_第1张图片

这个时候就要使用元注解来限定范围了。

元注解

元注解通俗的来说就是定义在注解上的注解,在Java中有四个元注解 @Target @Retention @Documented @Inherited

@Target

@Target就是用于描述注解的定义范围,可以限制这个注解定义的元素类型。

参数 作用
ElementType.ANNOTATION_TYPE 可以应用于注解类型
ElementType.CONSTRUCTOR 可以应用于构造函数
ElementType.FIELD 可以应用于字段或属性
ElementType.LOCAL_VARIABLE 可以应用于局部变量
ElementType.METHOD 可以应用于方法级注解
ElementType.PACKAGE 可以应用于包声明
ElementType.PARAMETER 可以应用于方法的参数
ElementType.TYPE 可以应用于类的任何元素

因为我们要限制只想定义在成员变量上,所以我们应该使用ElementType.FIELD

@Target(ElementType.FIELD)
public @interface lkx {

}

但是如果我们想同时定义在成员变量和成员方法上应该怎么办呢? 多个参数只需要用大括号包起来,逗号隔开就可以了

@Target({ElementType.FIELD,ElementType.METHOD})
public @interface lkx {

}

现在就只有定义在类上的注解报错了 【Java基础】注解——自定义注解_第2张图片

@Retention

@Retention是用于定义注解的生命周期,也可以理解为存储方式。

参数 作用
RetentionPolicy.SOURCE 标记的注解仅保留在源级别中,并被编译器忽略
RetentionPolicy.CLASS 标记的注解在编译时由编译器保留,但 Java 虚拟机(JVM)会忽略
RetentionPolicy.RUNTIME 标记的注解由 JVM 保留,因此运行时环境可以使用它

下面两个元注解使用不多,暂时不做详解

@Documented

@Documented是用于描述生成帮助文档时是否要保留其注解信息。

@Inherited

@Inherited是用于描述使被它修饰的注解是否具有继承性。

注解元素

上面我们只是定义了一个注解,但是不能传任何信息,只是相当于一个标签,现在我们看看要怎么给注解定义参数:

@Target({ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface lkx {
    String name() default "张三"; //可以使用default定义默认的值
    int age();
}

现在我们看一下如何传参:

public class Test {

    //name有默认值,也可以不写
    @lkx(name = "李四",age = 18)
    private int num;
    
    public static void main(String[] args) {
        System.out.println("hello");
    }
}

实战

现在注解也定义好了,参数也传入了,你是否还在想注解有什么卵用。。。现在我们有个小需求,把注解传入的参数赋值到成员变量上。 例如:

@lkx(name = "李四",age = 18)
private int num; //num没有被赋值,等于0

赋值完成后
num = 18

注意事项:

  1. 下面代码需要用到反射,如果还不会的小伙伴可以看我前面几篇文章。
  2. 因为要用反射拿到注解参数,所以@Retention需要定义为RetentionPolicy.RUNTIME

实现代码:

@Target({ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface lkx {
    String name() default "张三";
    int age();
}
java复制代码public class Test {

    //name有默认值,也可以不写
    @lkx(name = "李四",age = 18)
    private static int num;

    @lkx(name = "王五",age = 38)
    private static int age;

    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        System.out.println("赋值前: num: " + num+"   age: "+age);

        //拿到类的字节码
        Class<Test> testClass = Test.class;
        //拿到所有成员变量
        for (Field declaredField : testClass.getDeclaredFields()) {
            //检测成员变量上是否有@lkx注解
            if (declaredField.isAnnotationPresent(lkx.class)) {
                lkx annotation = declaredField.getAnnotation(lkx.class);
                //获取到注解中的age的值
                int age = annotation.age();
                declaredField.set(testClass.newInstance(),age);
            }
        }

        System.out.println("赋值后: num: " + num+"   age: "+age);
    }
}

运行结果:

java复制代码赋值前: num: 0   age: 0
赋值后: num: 18   age: 38

你可能感兴趣的:(Java基础,java,开发语言,spring)