java 注解

目录

定义注解

元注解

如何定义Annotation

@Repeatable

小结

注解官方解释

为什么使用注解

密码验证代码演示


定义注解

Java语言使用@interface语法来定义注解(Annotation),它的格式如下:

 
  1. public @interface Report {
  2. int type() default 0;
  3. String level() default "info";
  4. String value() default "";
  5. }

注解的参数类似无参数方法,可以用default设定一个默认值(强烈推荐)。最常用的参数应当命名为value

元注解

有一些注解可以修饰其他注解,这些注解就称为元注解(meta annotation)。Java标准库已经定义了一些元注解,我们只需要使用元注解,通常不需要自己去编写元注解。

@Target

最常用的元注解是@Target。使用@Target可以定义Annotation能够被应用于源码的哪些位置:

  • 类或接口:ElementType.TYPE
  • 字段:ElementType.FIELD
  • 方法:ElementType.METHOD
  • 构造方法:ElementType.CONSTRUCTOR
  • 方法参数:ElementType.PARAMETER

例如,定义注解@Report可用在方法上,我们必须添加一个@Target(ElementType.METHOD)

 
  1. @Target(ElementType.METHOD)
  2. public @interface Report {
  3. int type() default 0;
  4. String level() default "info";
  5. String value() default "";
  6. }

定义注解@Report可用在方法或字段上,可以把@Target注解参数变为数组{ ElementType.METHOD, ElementType.FIELD }

 
  1. @Target({
  2. ElementType.METHOD,
  3. ElementType.FIELD
  4. })
  5. public @interface Report {
  6. ...
  7. }

实际上@Target定义的valueElementType[]数组,只有一个元素时,可以省略数组的写法。

@Retention

另一个重要的元注解@Retention定义了Annotation的生命周期:

  • 仅编译期:RetentionPolicy.SOURCE
  • 仅class文件:RetentionPolicy.CLASS
  • 运行期:RetentionPolicy.RUNTIME

如果@Retention不存在,则该Annotation默认为CLASS。因为通常我们自定义的Annotation都是RUNTIME,所以,务必要加上@Retention(RetentionPolicy.RUNTIME)这个元注解:

 
  1. @Retention(RetentionPolicy.RUNTIME)
  2. public @interface Report {
  3. int type() default 0;
  4. String level() default "info";
  5. String value() default "";
  6. }

@Repeatable

使用@Repeatable这个元注解可以定义Annotation是否可重复。这个注解应用不是特别广泛。

 
  1. @Repeatable
  2. @Target(ElementType.TYPE)
  3. public @interface Report {
  4. int type() default 0;
  5. String level() default "info";
  6. String value() default "";
  7. }

经过@Repeatable修饰后,在某个类型声明处,就可以添加多个@Report注解:

 
  1. @Report(type=1, level="debug")
  2. @Report(type=2, level="warning")
  3. public class Hello {
  4. }

 

@Documented: 该注解可以修饰其他注解,表示将使用 Javadoc 记录被注解的元素。

@Inherited

使用@Inherited定义子类是否可继承父类定义的Annotation@Inherited仅针对@Target(ElementType.TYPE)类型的annotation有效,并且仅针对class的继承,对interface的继承无效:

 
  1. @Inherited
  2. @Target(ElementType.TYPE)
  3. public @interface Report {
  4. int type() default 0;
  5. String level() default "info";
  6. String value() default "";
  7. }

在使用的时候,如果一个类用到了@Report

 
  1. @Report(type=1)
  2. public class Person {
  3. }

则它的子类默认也定义了该注解:

 
  1. public class Student extends Person {
  2. }

如何定义Annotation

我们总结一下定义Annotation的步骤:

第一步,用@interface定义注解:

 
  1. public @interface Report {
  2. }

第二步,添加参数、默认值:

 
  1. public @interface Report {
  2. int type() default 0;
  3. String level() default "info";
  4. String value() default "";
  5. }

把最常用的参数定义为value(),推荐所有参数都尽量设置默认值。

第三步,用元注解配置注解:

 
  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface Report {
  4. int type() default 0;
  5. String level() default "info";
  6. String value() default "";
  7. }

其中,必须设置@Target@Retention@Retention一般设置为RUNTIME,因为我们自定义的注解通常要求在运行期读取。一般情况下,不必写@Inherited@Repeatable

@Repeatable

Repeatable 自然是可重复的意思。@Repeatable 是 Java 1.8 才加进来的,所以算是一个新的特性。

什么样的注解会多次应用呢?通常是注解的值可以同时取多个。

举个例子,一个人他既是程序员又是产品经理,同时他还是个画家。

@interface Persons {
    Person[]  value();
}
@Repeatable(Persons.class)
@interface Person{
    String role default "";
}
@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{
}

注意上面的代码,@Repeatable 注解了 Person。而 @Repeatable 后面括号中的类相当于一个容器注解。

什么是容器注解呢?就是用来存放其它注解的地方。它本身也是一个注解。

我们再看看代码中的相关容器注解。

@interface Persons {
    Person[]  value();
}

按照规定,它里面必须要有一个 value 的属性,属性类型是一个被 @Repeatable 注解过的注解数组,注意它是数组。

如果不好理解的话,可以这样理解。Persons 是一张总的标签,上面贴满了 Person 这种同类型但内容不一样的标签。把 Persons 给一个 SuperMan 贴上,相当于同时给他贴了程序员、产品经理、画家的标签。

我们可能对于 @Person(role=”PM”) 括号里面的内容感兴趣,它其实就是给 Person 这个注解的 role 属性赋值为 PM ,大家不明白正常,马上就讲到注解的属性这一块。

小结

Java使用@interface定义注解:

可定义多个参数和默认值,核心参数使用value名称;

必须设置@Target来指定Annotation可以应用的范围;

应当设置@Retention(RetentionPolicy.RUNTIME)便于运行期读取该Annotation

注解官方解释

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

注解有许多用处,主要如下:

  • 提供信息给编译器: 编译器可以利用注解来探测错误和警告信息
  • 编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
  • 运行时的处理: 某些注解可以在程序运行的时候接受代码的提取
    值得注意的是,注解不是代码本身的一部分。

如果难于理解,可以这样看。罗永浩还是罗永浩,不会因为某些人对于他“傻x”的评价而改变,标签只是某些人对于其他事物的评价,但是标签不会改变事物本身,标签只是特定人群的手段。所以,注解同样无法改变代码本身,注解只是某些工具的的工具。

还是回到官方文档的解释上,注解主要针对的是编译器和其它工具软件(SoftWare tool)。

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

现在,我们可以给自己答案了,注解有什么用?给谁用?给 编译器或者 APT 用的

为什么使用注解

使用注解最主要的部分在于对注解的处理,那么就会涉及到注解处理器。

从原理上讲,注解处理器就是通过反射机制获取被检查方法上的注解信息,然后根据注解元素的值进行特定的处理

注解可以理解成成一种标签,比如马云就是有钱的标签

密码验证代码演示

found use case : 47Passwords must contain at least one numeric
found use case : 48no description
warning : missing use case - 110

package com.zz.db.mysql.config.study;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
    public int id();
    public String description() default "no description";
}



package com.zz.db.mysql.config.study;

public class PasswordUtils {


    @UseCase(id = 47,description = "Passwords must contain at least one numeric")
    public boolean validatePassword(String password){
        return (password.matches("\\w\\d\\w"));
    }


    @UseCase(id = 48)
    public String encryptPassword(String password){
        return new StringBuffer(password).reverse().toString();
    }

}



package com.zz.db.mysql.config.study;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class AnTest {

    public static void main(String[] args) {
        List<Integer> useCases = new ArrayList<>();
        Collections.addAll(useCases,47,48,110);
        trackUseCases(useCases,PasswordUtils.class);
    }


    public static void trackUseCases(List<Integer> useCases,Class<?> cl){

        for(Method m : cl.getDeclaredMethods()){
            UseCase uc = m.getAnnotation(UseCase.class);
            if(null != uc){
                System.out.println("found use case : " + uc.id() + uc.description());
                useCases.remove(new Integer(uc.id()));
            }
        }

        for (int i : useCases){
            System.out.println("warning : missing use case - " + i);
        }


    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值