您的位置:首页 > 其它

你说啥什么?注解你还不会?

2020-09-06 20:58 537 查看

注解

文末有彩蛋。

一、什么是注解?

Annotaion

  • 注解(Annotaion)
    是从JDK5.0开始引入的一种新技术称之为注解机制。

  • 注解(Annotaion)
    的格式:

    注解是以"
    @注释名
    "在代码中使用的,可以添加一些参数值,例如:
    @GetMapping("/get")
  • 注解(Annotaion)
    可以使用的范围:

      可以在package、class、method、field等上面使用。例如:

      @Controller
      public class RequestController {
      
      @DeleteMapping("/delete")
      @ResponseBody
      public String delete(String name,Integer id){
      JSONObject json = new JSONObject();
      json.put("requestType","deleteType");
      json.put("name",name);
      json.put("id",id);
      
      return json.toString();
      }
      
      }
  • 我们可以通过反射机制编程对这些元数据的访问。

  • 注解有一些特定的功能,例如:

      当你如果要重写toString()方法的时候,不是按照规定的名字来写的话,就会报错:

  • 正常的话,是不会报错的:

  • 说明

    @Override
    注解带有检查的作用。

  • 二、内置注解

    Java内部定义了一套注解,共有7个:

    注解名称 作用
    @Override 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
    @Deprecated 标记过时方法。如果使用该方法,会报编译警告。
    @SuppressWarnings 指示编译器去忽略注解中声明的警告。

    作用在其他注解的注解(

    元注解
    ):在
    java.lang.annotaion
    包中

    注解名称 作用
    @Retention 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
    @Documented 标记这些注解是否包含在用户文档中。
    @Target 标记这个注解应该是哪种 Java 成员。
    @Inherited 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

    从 Java 7 开始,额外添加了 3 个注解:

    注解名称 作用
    @SafeVarargs Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
    @FunctionalInterface Java 8 开始支持,标识一个匿名函数或函数式接口。
    @Repeatable Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

    三、元注解的使用

    (一)前期准备

    如果

    你想自定义注解
    ,那么
    元注解是必知必会必懂
    的。

    元注解就是

    注解自定义注解的注解
    。可能有点饶,一会看例子就明白了,
    直白点就是给你自定义的注解上一定要加的注解

    作用在其他注解的注解(

    元注解
    ):在
    java.lang.annotaion
    包中

    注解名称 作用
    @Retention 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
    @Documented 标记这些注解是否包含在用户文档中。
    @Target 标记这个注解应该是哪种 Java 成员。
    @Inherited 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

    我们定义一个类,类的作用就是用来测试我们定义的注解:

    /**
    * @Auther: truedei
    * @Date: 2020 /2020/9/6 18:11
    * @Description: 自定义注解测试
    */
    public class Test {
    
    }

    然后创建一个类,

    把class标识符改成@interface
    :这就是自定义好的注解了。

    /**
    * @Auther: truedei
    * @Date: 2020 /2020/9/6 19:00
    * @Description: 自定义注解
    */
    public @interface MyAnnotaion {
    
    }

    现在可以看到就可以使用了,只不过没有任何的功能:

    (二)@Target的用法详解

    我们来赋予一定的功能,来标识这个注解的作用:

    加上元注解:@Target,就是用来指出对什么生效,作用的目标是什么,可以在什么地方使用。

    可以看到传递的是一个E

    可以点看@Target的源码看一下:

    可以看到下面这种情况。

    需要提前知道的是,

    value()
    是接收的参数,并不是一个方法。

    那么Target就需要接收一个

    ElementType[]
    的数组。

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Target {
    
    ElementType[] value();
    }

    没加之前,先改造一下:

    可以看到这个注解什么都没加,既可以加在类上,也可以加在方法上,也可以加在变量上。

    如果我们想做限制呢?只允许我们的这个注解对类生效

    我们先来看一下

    Controller注解
    这个应该不陌生,就不解释了。

    那么我们也可以加上这个:

    可以看到效果了,只要类上的生效了,其余的都报错了。

    那么这就是元注解Target的作用。

    看一下

    ElementType.java
    中枚举的参数,这些都可以使用,了解下就好。

    package java.lang.annotation;
    
    public enum ElementType {
    TYPE,               /* 类、接口(包括注释类型)或枚举声明  */
    
    FIELD,              /* 字段声明(包括枚举常量)  */
    
    METHOD,             /* 方法声明  */
    
    PARAMETER,          /* 参数声明  */
    
    CONSTRUCTOR,        /* 构造方法声明  */
    
    LOCAL_VARIABLE,     /* 局部变量声明  */
    
    ANNOTATION_TYPE,    /* 注释类型声明  */
    
    PACKAGE             /* 包声明  */
    
    /**
    * Type parameter declaration
    *
    * @since 1.8
    */
    TYPE_PARAMETER,
    
    /**
    * Use of a type
    *
    * @since 1.8
    */
    TYPE_USE
    }

    如果你想让你的注解对什么生效,就指定好就OK:

    (三)@Retention的用法详解

    Retention中需要传递RetentionPolicy。

    RetentionPolicy.java
    有三个枚举参数,如下:

    package java.lang.annotation;
    public enum RetentionPolicy {
    SOURCE,            /* Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了  */
    
    CLASS,             /* 编译器将Annotation存储于类对应的.class文件中。默认行为  */
    
    RUNTIME            /* 编译器将Annotation存储于class文件中,并且可由JVM读入 */
    }

    一般都是调用**

    RUNTIME
    **,调用
    RUNTIME
    我们可以通过反射拿到相关的数据,来进行处理等。

    由于这个不太好验证,就不一一验证了,

    了解就好

    (四)@Documented的用法详解

    如果使用 @Documented 修饰该 Annotation,则表示它可以出现在 javadoc 中。

    定义 Annotation 时,@Documented 可有可无;

    若没有定义,则 Annotation 不会出现在 javadoc 中。

    (五)@Inherited的用法详解

    子类可以继承父类的注解。

    /**
    * @Auther: truedei
    * @Date: 2020 /2020/9/6 19:00
    * @Description: 自定义注解
    */
    @Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface MyAnnotaion {
    
    }

    四、自定义参数详解

    注解的参数:参数的类型 + 参数名 ();

    如果想有默认值还需要加上default 值。

    例如:

    //自定义参数1
    String name();
    //自定义参数2  带默认值的
    String type() default "";

    我们给事先定义好的注解类加上一个参数,随便写名字即可:

    @Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface MyAnnotaion {
    
    //参数
    String name();
    
    }

    可以看到我们在使用的时候,就会报错了,原因是必须要给这个定义好的参数传递一个值。

    我们传递过来值:

    @MyAnnotaion(name = "郑晖")
    public void test(){
    
    }

    这个时候就有同学要问了:我定义好参数之后,可以不传递参数吗,用到的时候再传递。

    答案是可以的,如下:设置一个默认值就好了:

    String type() default "";

    当然了,也可以传递很多类型的参数例如:

    import java.lang.annotation.*;
    
    /**
    * @Auther: truedei
    * @Date: 2020 /2020/9/6 19:00
    * @Description: 自定义注解
    */
    @Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface MyAnnotation {
    
    //String类型
    String name();
    
    //int类型
    int age() default 0;
    
    //boolean类型
    boolean bool() default false;
    
    //char 类型
    char cha() default ' ';
    
    //各种数组类型
    String[] strs() default {};
    
    //枚举类型
    MyEnum myEnum() default MyEnum.A;
    
    }

    枚举类型类定义:

    public enum MyEnum {
    A,B,C,D;
    }

    我们在使用的时候,就可以随心所欲的使用:

    /**
    * @Auther: truedei
    * @Date: 2020 /2020/9/6 18:11
    * @Description: 自定义注解测试
    */
    public class Test {
    
    String data;
    
    @MyAnnotaion(name = "郑晖",age = 85,cha = 'A',strs = {"aasd","xsw","你好"},myEnum = MyEnum.C)
    public void test(){
    
    }
    
    }

    五、利用反射操作注解

    MyA.java:

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
    * 对于我们的作用:标识着加了这个注解的我们才允许扫描
    */
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyA {
    
    }

    MyAnnotation.java:

    import java.lang.annotation.*;
    
    /**
    * @Auther: truedei
    * @Date: 2020 /2020/9/6 19:00
    * @Description: 自定义注解 ---->给特定的方法用的
    */
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation {
    
    //String类型
    String name();
    
    }

    Test.java:

    /**
    * @Auther: truedei
    * @Date: 2020 /2020/9/6 18:11
    * @Description: 自定义注解测试
    */
    @MyA
    public class Test {
    
    String data;
    
    1faa1
    @MyAnnotation(name = "郑晖")
    public void test(String name){
    System.out.println("我的名字:"+name);
    }
    
    }

    测试类:

    package cn.annotaion;
    
    import java.lang.annotation.Annotation;
    import java.lang.annotation.Target;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Inherited;
    import java.lang.reflect.Method;
    /**
    *测试类
    */
    public class AnnotationTest {
    
    public static void main(String[] args) throws Exception {
    
    //拿到指定的类的Test
    Class cl = Class.forName("cn.annotaion.Test");
    
    //判断是否是我们特定自定义的注解  如果是就扫描
    if (cl.isAnnotationPresent(MyA.class)) {
    Method[] methods = cl.getMethods();
    
    for (Method method : methods) {
    // 判断 somebody() 方法是否包含MyAnnotation注解
    if(method.isAnnotationPresent(MyAnnotation.class)){
    // 获取该方法的MyAnnotation注解实例
    MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
    
    // 获取 myAnnotation的值,并打印出来
    String name = myAnnotation.name();
    System.out.println(name);
    
    //执行这个方法
    method.invoke(new Test(),name);
    
    }
    }
    
    }
    
    }
    
    }

    运行:

    六、彩蛋

    看完了之后,发现有什么问题吗?

    细心的你一定注意到:最初我使用的是

    MyAnnotaion.java
    后来
    MyAnnotation.java

    没错,少写了个

    t
    。还好不影响大局。

    TrueDei CSDN认证博客专家 Linux 分布式 Java 不荒废现在,不畏惧未来!我认为把知识给别人讲会,讲明白,自己才彻底明白。努力把文章写好,写明白每一篇文章,分享给更多人。
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: 
    相关文章推荐