Java注解Annotation

Annotation翻译过来为注解、释文等。而在Java中注解算是一个尤为重要的知识点,而且不易理解。

摘抄至百度百科:

java.lang.annotation,接口 Annotation。对于Annotation,是Java5的新特性,JDK5引入了Metadata(元数据)很容易的就能够调用Annotations。Annotations提供一些本来不属于程序的数据,比如:一段代码的作者或者告诉编译器禁止一些特殊的错误。An annotation 对代码的执行没有什么影响。Annotations使用@annotation的形式应用于代码:类(class),属性(attribute),方法(method)等等。一个Annotation出现在上面提到的开始位置,而且一般只有一行,也可以包含有任意的参数。

是不是感觉看完还是一脸懵逼完全不知道说的是什么?

别担心:我通俗的把Annotation理解为一个Label(标签),用于对其他事物进行修饰解释。

注解的语法以及使用

  1. 定义:注解通过 @interface关键字进行定义。
    下面即定义了一个名为TestAnnotation的注解
		public @interface TestAnnotation{}
  1. 应用:在使用的地方加上@TestAnnotation即可
		@TestAnnotation
		public class Test{}

要想注解能够正常使用,还需要介绍一下元注解。

元注解

什么元注解?就是可以注解到注解上的注解。即元注解是基本的注解,他能到应用到其他注解上,去修饰其他的注解。
基本的元注解有如下:

元注解可以取的值所取值的含义意义
@RetentionRetentionPolicy.SOURCE只保留在源码阶段,编译时丢弃生命周期,即根据它来判断注解的存活时长
RetentionPolicy.CLASS保留到编译进行时,不加载到JVM中去
RetentionPolicy.RUNTIME保留到程序运行时并加载到JVM中去,在程序运行时可以加载到它们
@Documented将注解的元素包含到javadoc文档中去
@TargetElementType.ANNOTATION_TYPE可以给注解进行注解指定注解应用的目标(即使用场景)
ElementType.CONSTRUCTOR可以给构造方法进行注解
ElementType.FIELD可以给属性进行注解
ElementType.LOCAL_VARIABLE可以给局部变量进行注解
ElementType.METHOD可以给方法进行注解
ElementType.PACKAGE可以给包进行注解
ElementType.PARAMETER可以给方法内的参数进行注解
ElementType.TYPE可以给给一个类型进行注解,类/接口/枚举等
@Inherited继承的意思,它并不是说注解本身可以被继承,而是说一个超类被@Inherited注解过的注解进行注解时,如果它的子类没有被其他注解所注解,那么子类就继承了超类的注解
@Repeatable可重复的意思。什么样的注解会多次应用呢?通常是注解的值可以同时取多个。

元注解的使用例子:

@Retetion:

@Retetion(RetetionPolicy.RUNTIME)
public @interface TestAnnotation{}

@Inherited:

@Inherited
@Retetion(RetntionPolicy.RUNTIME)
public @interface TestAnnotation{}

@TestAnnotation
class Parent{}

class Children extend Parent{}

解释:
注解 TestAnnotation被 @Inherited 修饰,之后类 Parent 被 TestAnnotation 注解,类 Children 继承 Parent,类 Children 也拥有 Test 这个注解。

@Repeatable:

例子:一个人可以是产品也可以是程序员还可以是驻唱
@interface Persons{
	Person[] value();
}

@Repeatable(Persons.class)
@interface Person{
	String role default "";
}

@Person(role="singer")   //给注解的role属性赋值为singer
@Person(role="PM")
@Person(role="coder")
public class Man{}

解释:
@Repeatable注解了Person。后面括号的类相当于注解容器(可以存放其他注解的注解),按照规定内部必须有一个value的属性,属性类型是一个被@Repeatable注解过的注解数组。 即Persons是一个总标签,上面贴满各种各样的Person标签。

@Target:

@Target(ElementType.TYPE)
@Retetion(RetntionPolicy.RUNTIME)
public @interface TestAnnotation{}

注解属性

定义注意点:

  1. 注解的属性也叫成员变量,无方法。
  2. 注解的属性以“无形参的方法”的形式来声明
  3. 方法名定义了该成员变量的名字返回类型为成员变量的类型
  4. 注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组。

使用案例:(内部注释已经很完善了)

定义一个注解

@Target(ElementType.TYPE)
// 运行时
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
    int id();        // 定义一个属性名为id 的int变量

    String msg();    // 定义一个属性名为msg 的String变量

    double price() default 2.0f;   //定义一个默认值为2.0名为price的Double变量
}

使用注意点:

  1. 使用,括号内给变量赋值
  2. 赋值的方式是在注解的括号内以 value="" 形式,多个属性之前用 ,隔开。
  3. 注解中属性可以有默认值,默认值需要用 default 关键字指定;有默认值在使用时就无需进行赋值
  4. 当注解中只有一个value属性时,在使用时可以直接在括号里加上值,无需value=""
  5. 注解没有任何属性时,使用 括号也可以省略

使用上面的注解:

@TestAnnotation(id = 1, msg = "wdl")     //定义默认值的可有可无
class Test {
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
@interface TextAnnotation{
    int value();
}

//  @TextAnnotation(value = 2)
@TextAnnotation(2)
class Text {
}



@Retention(RetentionPolicy.CLASS)
@interface Perform{
}

Java中预置的注解

  1. @Deprecated 标记过时的元素,编译器在编译阶段遇到这个注解时会发出提醒警告

  2. @Override 复写父类中的被@Override修饰的方法

  3. @SuppressWarnings 阻止警告,@Deprecated标记过的方法会发出警告,我们可以使用这个注解忽略警告

  4. @SafeVarargs 参数安全类型注解。提醒开发者不要用参数做一些不安全的操作

  5. @FunctionalInterface 函数式接口注解。Runnable就是一个被@FunctionalInterface修饰的接口。函数式接口 (Functional Interface) 就是一个具有一个方法的普通接口。

上面大概的介绍了一下注解的基本使用方法以及常用的预置注解/元注解等。前面提到过注解用来修饰/解释,那我们让这些注解生效应该怎么办,即提取这些注解?那就是使用反射技术了。这一过程就相当于撕标签/检阅标签内容了。

注解提取的一般步骤

  1. 通过xx.class内置的函数判断这个类是否应用了某个注解
 /**
     * {@inheritDoc}
     * @throws NullPointerException {@inheritDoc}
     * @since 1.5
     */
    @Override
    public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
        return GenericDeclaration.super.isAnnotationPresent(annotationClass);
    }
  1. 通过 getAnnotation()或者getAnnotations()方法来获取 Annotation 对象。
 /**
     * @throws NullPointerException {@inheritDoc}
     * @since 1.5
     */
    @SuppressWarnings("unchecked")
    public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
        Objects.requireNonNull(annotationClass);

        return (A) annotationData().annotations.get(annotationClass);
    }

 /**
     * @since 1.5
     */
    public Annotation[] getAnnotations() {
        return AnnotationParser.toArray(annotationData().annotations);
    }
  1. 调用它们的属性方法

简单的使用例子:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Check{
    int value();
}

// 注解没有任何属性时,使用 括号也可以省略
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Perform{
}

// 使用,括号内给变量赋值
// 赋值的方式是在注解的括号内以 value="" 形式,多个属性之前用 ,隔开。
// 注解中属性可以有默认值,默认值需要用 default 关键字指定;有默认值在使用时就无需进行赋值
// 注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组。
@TestAnnotation(id = 1, msg = "wdl")
class Test {
    @Check(6)
    int a;

    @Perform
    public void testMethod(){
    }
    
}

boolean isAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
        if (isAnnotation){
            TestAnnotation textAnnotation = Test.class.getAnnotation(TestAnnotation.class);
            System.out.println(textAnnotation.id());
            System.out.println(textAnnotation.msg());
            System.out.println(textAnnotation.price());
        }
        try {
            Field field = Test.class.getDeclaredField("a");
            field.setAccessible(true);
            if (field!=null){
                Check check = field.getAnnotation(Check.class);
                System.out.println("Check value is:"+ check.value());
            }

            Method testMethod = Test.class.getDeclaredMethod("testMethod");
            if (testMethod!=null){
                Annotation[] annotations = testMethod.getAnnotations();
                for (int i = 0; i < annotations.length; i++) {
                    System.out.println("testMethod annotations:"+annotations[i].annotationType().getSimpleName());
                }
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

输出为:
在这里插入图片描述

注解使用场景

大名鼎鼎的Butterknife/Retrofit/Dagger等都是使用注解来实现的。因此注解非常的重要虽然平常开发中很少遇到。

注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。

作用:

  1. 提供信息给编译器:编译器可以利用注解来探测错误和警告信息
  2. 编译阶段时的处理:软件工具可以利用注解信息来生成代码,HTML文档等
  3. 运行时处理:某些注解可以在程序运行时接受代码的提取

当开发者使用了Annotation 修饰了类、方法、Field 等成员之后,这些 Annotation
不会自己生效,必须由开发者提供相应的代码来提取并处理 Annotation 信息。这些处理提取和处理 Annotation 的代码统称为
APT(Annotation Processing Tool)。

学完终于知道了使用Butterknife时多加的那一串是什么意思了。。。。
在这里插入图片描述

参考自:

https://blog.youkuaiyun.com/briblue/article/details/73824058

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值