JAVA注解

注解本质和原理

注解的本质就是,定义了一个接口实现了java.lang.annotation.Annotation 接口。我们定义一个注解类,然后进行编译,再反编译。如下,我们定义一个自定义的注解

public @interface MyAno {

    int a() default 0;

    boolean b() default true;
    String c() default "c";

}

然后使用javac将其编译生成class文件,再使用javap 反编译命令,将生成的class文件进行反编译得到如下

public interface com.demo.annotation.MyAno extends java.lang.annotation.Annotation {
  public abstract int a();
  public abstract boolean b();
  public abstract java.lang.String c();
}

所以注解的本质还是接口。

jdk 1.5 内置注解

jdk 有三个内置注解,他们都是编译期注解,即只会存在.java文件中。编译之后就自动去掉了。

  • @Override 当一个接口的实现类,覆盖父类的方法,上面添加该注解。
  • @Deprecated 添加在方法上,表名该方法已经废弃
  • @SuppressWarnings() 相当于忽略警告(接受一个数组类型的参数,忽略的警告类型),
    如下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ED9Cg6vF-1584887157656)(https://raw.githubusercontent.com/ligengithub/pictest/master/suppressWarning.png)]

使用了@SuppressWarnings(“deprecation”) 之后,过期的方法就不会显示警告。

注解定义和应用场景

注解有三大应用场景

  • 生成文档,如 @Document 注解 配合@see,@param,@return 等注解,可以使用javadoc命令,可以直接生成api文档。
  • 编译检查,如@Override
  • 通过自定义注解,配置属性,配合反射。在运行期获取注解的属性值,做相关用处。
元注解

所谓元注解就是,用来标注注解的注解。有下面四种。

  • @Target
    指定该注解的修饰的范围,参数有如下取值
     1.CONSTRUCTOR:用于描述构造器
    2.FIELD:用于描述域
   3.LOCAL_VARIABLE:用于描述局部变量
   4.METHOD:用于描述方法
   5.PACKAGE:用于描述包
   6.PARAMETER:用于描述参数
    7.TYPE:用于描述类、接口(包括注解类型) 或enum声明

定义了target 之后,如果使用注解 标记的位置和Target指定的不一致会编译报错,这样也可以避免我们将注解添加到错误的位置

  • @Rentention 生命周期
      1.SOURCE:在源文件中有效(即源文件保留)
      2.CLASS:在class文件中有效(即class保留)
     3.RUNTIME:在运行时有效(即运行时保留)

当如果你想使用自定义注解,在程序运行中,获取注解的属性值,需要将生命周期设置位RUNTIME

  • @Documented 标记注解,没有属性,标记之后,可以使用javaDoc 生成api文档

  • @Inherited @nbsp;可被继承 也是一个标记注解,如果你使用@Inherited 标记了一个注解A。
    那么被注解A 标注的类,的子类,也会被A标记。

自定义注解

自定义注解,最大的作用就是配置功能。定义自定义注解的属性,然后在程序运行中通过读取注解的属性值来改变程序的行为。一个简单的自定义注解如下。 在接口中我们称之为方法名的,在注解中我们称之为属性。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAno2 {

    // 属性返回值类型 可以是所有的基本类型
    int a();
    boolean b();
    // 引用类型
    String c();
    // 注解类型
    MyAno d();
    // 枚举类型
    EnumDemo f() default EnumDemo.A;
    // 数组类型,可以是上面任何一中类型的数组
    String[] e();
}

注解定义好了,怎么用呢,如下。

@MyAno2(a = 1, b = true, c = "xixi", d = @MyAno(), f = EnumDemo.B, e = {"1", "2"})
public class Test {

    public int a;
    public boolean b;
    public String c;
    public MyAno d;
    public EnumDemo f;
    public String[] e;
    @Override
    public String toString() {
        return "Test{" +
                "a=" + a +
                ", b=" + b +
                ", c='" + c + '\'' +
                ", d=" + d +
                ", f=" + f +
                ", e=" + Arrays.toString(e) +
                '}';
    }
}

然后,通过反射,可以获取到注解的属性值。。然后我们就可以拿属性值来做任何我们想做的事情。

    public static void testAnoOnClass() throws IllegalAccessException, InstantiationException {
        Class<Test> testClass = Test.class;
        MyAno2 annotation = testClass.getAnnotation(MyAno2.class); // 获取类上的注解
        Test test = testClass.newInstance();
        test.a = annotation.a();
        test.b = annotation.b();
        test.c = annotation.c();
        test.d = annotation.d();
        test.e = annotation.e();
        test.f = annotation.f();
        System.out.println(test);
    }

上面是定义在类上面的,下面再举个定义再属性上的例子

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAno3 {

    // 属性返回值类型 可以是所有的基本类型
    String value() default "13412";

}
public class Entry {

    @MyAno3("hahahhaha")
    private String value;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "Entry{" +
                "value='" + value + '\'' +
                '}';
    }
}
    public static void testOnMethod() throws NoSuchFieldException {
        Entry entry = new Entry();
        Field field = entry.getClass().getDeclaredField("value"); // 获取属性上的注解
        System.out.println(field.getAnnotation(MyAno3.class).value());
    }

这里需要注意的是

  • 1 注解在不同的位置,获取注解的方法不同,比如注解在类上,可以直接使用该类的class对象获取注解。定义在属性上,需要先通过class对象获取到该属性,然后使用属性的getAnnotation()方法。其它部位类似。
  • 当注解只有一个属性的时候,我们通常将属性名字定义为value,这样在使用注解的时候,可以直接如下,不用指定属性名。
@MyAno3("hahahhaha")

总结:注解本质上是定于i了一个接口,这个接口中的方法,我们称之为属性。我们通过设置属性的值,来将我们的参数,传递进代码中。 所以自定义注解通常用在做一些自定义的配置中。

简单的一个demo

试下一个简单的demo,对@Check注解标注的函数,进行测试。如果测试函数抛出异常则将异常记录到文件。

https://github.com/ligengithub/java2020.git
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值