什么是注解

什么是注解

注解就是给某个注解标注的类或者方法一种特定行为的描述。

注解的语法

通过@interface关键字进行定义

public @interface MyAnnoation {}

它的形式跟接口类似,不过前面多了一个@符号,上面的代码就是创建了一个MyAnnoation注解。

也可以理解为创建了一个名字为MyAnnoation的标签。

注解的用法

如何使用上面的注解?

创建一个类 MyAnnoationTest,进行使用MyAnnoation注解。

@MyAnnoation
public class MyAnnoationTest {}

如上图所示,就是在类定义的地方加上该注解。

其实如果按照上面这样,这个注解就让他工作,那是不行的,还需要使用到元注解。

元注解

什么是元注解?

元注解就是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其他注解上面。

元注解有五种,分别是@Retention、@Documented、@Target、@Inherited、@Repeatable

@Retention

Retention的英文意为保留期的意思,当@Retention应用到一个注解上的时候,它解释说明了这个注解的存活时间

它的取值如下:

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE, // 注解只在源码阶段保留,在编译器进行编译时它将被丢弃

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS, //注解只被保留到编译进行的时候,它并不会被加载到JVM中

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME // 注解可以保留到程序运行的时候,它会被加载到JVM中,所以程序运行时可以获取到它们
}

我们可以这样的方式来理解,@Retention去给一张标签解释的时候,它指定了这张标签的张贴的时间,@Retention相当于给一张标签上面盖了一张时间戳,时间戳指明了标签张贴的时间周期。

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnoation {}

@Documented

这个元注解是和文档有关的,它的作用是能够将注解中的元素包含到Javadoc中

@Target

指定了注解运用的地方。可以理解为当一个注解被@Target注解时,这个注解就被限定了运用的场景。

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE, // 可以给一个类型进行注解,比如类,接口,枚举

    /** Field declaration (includes enum constants) */
    FIELD,// 可以给属性进行注解

    /** Method declaration */
    METHOD, // 可以给方法进行注解

    /** Formal parameter declaration */
    PARAMETER, // 可以给一个方法内的参数进行注解

    /** Constructor declaration */
    CONSTRUCTOR, // 可以给构造方法进行注解

    /** Local variable declaration */
    LOCAL_VARIABLE, // 可以给局部变量进行注解

    /** Annotation type declaration */
    ANNOTATION_TYPE, // 可以给一个注解进行注解

    /** Package declaration */
    PACKAGE, // 可以给一个包进行注解

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

@Inherited

Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被**@Inherited**注解过的注解进行注解的话,那么如果他的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。

@Repeatable

Repeatable 自然是可重复的意思,假如一个人有三种身份,就可以这么定义。

首先定义@Persons注解

public @interface Persons {
    Person[] value();  // 这里的Person是一个数组
}

再定义@Person注解,在Person注解上定义@Repeatable(Persons.class),表明该类可以重复

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

在SuperMan类上面使用

@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan {
}

注解的属性

注解的属性也叫做成员变量,注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其返回值定义该成员变量的类型。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnoation {
    int id();
    String msg();
}

上面代码定义了MyAnnoation这个注解中拥有两个属性,在使用的时候,我们应该给他们进行赋值。

赋值的方法是在注解的括号内以value=""形式,多个属性用逗号进行分割。

@MyAnnoation(id = 1,msg = "anan")
public class MyAnnoationTest {}

需要注意的是,在注解中定义属性屎它的类型必须是8中数据类型外加类,接口,注解及它们的数组。

注解中属性可以有默认值,默认值需要用default关键字指定。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnoation {
    int id() default 5;
    String msg() default "hi annotation";
}

有默认值之后,就可以这样使用注解

@MyAnnoation()  // 不用在括号中进行赋值了
public class MyAnnoationTest {
}

另外,如果一个注解内仅仅只有一个名字为value的属性时,应用这个注解就可以直接填写属性值到括号里面

@Check("hi")
public class MyAnnoationTest {
}

还有一种,如果是只有一个注解,里面没有任何属性,使用注解的时候,就可以不用写括号了,直接写注解就行

@Perform
public class MyAnnoationTest {
}

注解与反射

注解通过反射获取。首先通过Class对象的isAnnotationPresent()方法判断它是否应用了某个注解。

然后通过getAnnotation()方法来获取Annotation对象,或者getAnnotations()方法。

前一种方法发挥指定类型的注解,后一种方法返回注解到这个元素上的所有注解。

如果获取到的Annotation不为空,就可以调用属性方法了。

@MyAnnoation(id = 1,msg = "anan")
public class MyAnnoationTest {
    public static void main(String args[]) {
        boolean hasAnnotation = MyAnnoationTest.class.isAnnotationPresent(MyAnnoation.class);
        if (hasAnnotation) {
            MyAnnoation myAnnoation = MyAnnoationTest.class.getAnnotation( MyAnnoation.class);
            System.out.println(myAnnoation.id());
            System.out.println(myAnnoation.msg());
        }
    }
}

注解的用途

注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。
注解有许多用处,主要如下:
提供信息给编译器: 编译器可以利用注解来探测错误和警告信息
编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
运行时的处理: 某些注解可以在程序运行的时候接受代码的提取
值得注意的是,注解不是代码本身的一部分。

注解主要针对的是编译器和其它工具软件(SoftWare tool)。

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

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

亲手定义注解,完成目的

定义注解

@Retention(RetentionPolicy.RUNTIME)
public @interface Jiacha {
}

使用注解

public class NoBug {
    @Jiacha
    public void suanShu() {
        System.out.println("1234567890");
    }
    @Jiacha
    public void jiaFa() {
        System.out.println("1+1="+(1+1));
    }
    @Jiacha
    public void jianFa() {
        System.out.println("1-1="+(1-1));
    }
    @Jiacha
    public void chengFa() {
        System.out.println("3x5="+3*5);
    }
    @Jiacha
    public void chuFa() {
        System.out.println("6/0="+6/0);
    }
    public void ziwojieshao() {
        System.out.println("我写的程序没有bug!");
    }
}

解析注解

public class TestTool {
    public static void main(String[] args) {
        NoBug noBug = new NoBug();
        Class clazz = noBug.getClass();
        Method[] methods = clazz.getDeclaredMethods();
        StringBuilder log = new StringBuilder();
        int errornum = 0;
        for (Method m:methods) {
            if (m.isAnnotationPresent( Jiacha.class)) {
                try {
                    m.setAccessible(true);
                    m.invoke(noBug,null);
                    
                } catch (Exception e) {
                    errornum++;
                    log.append( m.getName());
                    log.append("");
                    log.append("has error:");
                    log.append("\n\r  caused by ");
                    log.append(e.getCause().getClass().getSimpleName());
                    log.append("\n\r");
                    log.append(e.getCause().getMessage());
                    log.append("\n\r");
                } 
            }
        }
        log.append(clazz.getSimpleName());
        log.append(" has  ");
        log.append(errornum);
        log.append(" error.");
        // 生成测试报告
        System.out.println(log.toString());
    }
}
发布了32 篇原创文章 · 获赞 4 · 访问量 2383

猜你喜欢

转载自blog.csdn.net/weixin_44644403/article/details/100694573