注解学习 - java

注解的理解
注解:Annotation 注解就是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。注解是给计算机看的。通过使用注解,程序开发人员可以不改变原有逻辑下,在源文件中嵌入一些补充信息。
注意
注解:注解不影响程序代码的执行,无论增加、删除注解,代码都始终如一的执行。如果希望让程序中注解在运行时起一定的作用。只有通过配套的工具对注解中的信息进行访问和处理,访问和处理注解的工具称为APT(Annotaation Processing Tool)
java提供的五个注解
@Override @Deprecated @SuppressWarnings @FunctionalInterface

@Override:
告诉编译器检查这个方法,保证父类要包含一个被该方法重写的方法,否则就会编译错误。用来避免类中重写时,方法写错这种低级错误。
@Deprecated:
用于表示某个程序元素(类,方法等)已过时。注意某个方法要是过时,或者有瑕疵的时候,在新版本中不能进行删除,这样就不能向下兼容。
@SuppressWarnings
用于指示该注解修饰的程序元素取消显示指定的编译器警告。
如图所示:在这里插入图片描述
此时在右边会出现两个警告。
当使用@SuppressWarnings(“all”)时,
在这里插入图片描述
右边的警告会消失。
@Functionallneterface
从Java8开始:如果接口中只有一个抽象方法(可以包含多个默认方法或者多个static方法),该接口就是函数式接口。@FunctionalInterface就是用来指定这个某个接口必须是函数式接口。
JDK元注解
@Retention:只能用于修饰注解定义,用于指定被修饰的注解可以保留多长时间。其内部包含了一个RetentionPolicy类型的value成员变量,所以使用@Retention时,必须为该value成员变量指定值。
value的值只能是下面三个,RetentionPolicy.Class:编译器将把注解记录在class文件中。当运行Java程序时,JVM不可获取注解信息。这是默认值。
RetentionPolicy.RUNTIME:编译器将把注解记录在class文件中。当运行Java程序时,JVM也可以获得注解信息,程序可以通过反射获得该注解信息。
RententionPolicy.SOURCE:注解只是保存在源代码中,编译器直接丢弃这种注解。

如果自己写代码的话,一般使用的是RententionPolicy.RUNTIME这个值。
代码如下

@Retention(value = RetentionPolicy.RUNTIME)
public @interface Anno {
}

注意:如果使用注解只需要为value成员变量指定值时,则使用该注解时可以直接在该注解后括号里指定value成员变量的值,无需使用“value=变量值”的形式。

使用@Target
@Target也只能修饰注解定义,它用于指定被修饰的注解能够用于修饰哪些程序单元
ElementType.ANNOTATION_TYPE:指定该注解只能用来修饰注解
ElementType.CONSTRUCTOR:指定该注解只能修饰构造器
ElementType.FIELD:指定该注解只能修饰成员变量
ElementType.LOCAL_VARIABLE:指定该注解只能修饰局部变量
ElementType.METHOD:指定该注解只能修饰方法
ElementType.PACKAGE:指定该注解只能修饰包
ElementType.PARAMETER:指定该注解只能修饰参数
ElementType.TYPE:指定该注解可以修饰类、接口、或者枚举类型。
使用@Documented
@Documented用于指定被该元注解修饰的注解类将被javadoc工具提取成为文档
首先写一个注解和一个测试类

/**
 * FileName: Mytest
 *
 * @Author:luguobao Date: 2020/5/1012:58
 * Description:
 * History:
 * <author>   <time>   <version>   <desc>
 * 作者姓名    修改时间    版本号       描述
 */
public class Mytest {
    @Testable
    public void info(){
        System.out.println("info...");
    }
}
import java.lang.annotation.*;

/**
 * FileName: Testable
 *
 * @Author:luguobao Date: 2020/5/1012:55
 * Description:
 * History:
 * <author>   <time>   <version>   <desc>
 * 作者姓名    修改时间    版本号       描述
 */
@Retention(value = RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
//定义Testable注解将被javadoc工具提取
@Documented
public @interface Testable {

然后打开cmd进入它们两个的目录下,指定javadoc Mytest.java
在这里插入图片描述
此时会生成一堆文件。打开在这里插入图片描述
index.html就会看到他的api了。
在这里插入图片描述
可以看到这个注解在api上。当删除这个注解以后,结果如下图所示:在这里插入图片描述
方法上就没有注解了。
使用@Inherited
@Inherited元注解指定被它修饰的注解将具有继承性–如果某个类使用了@Inherited注解(定义该注解时使用了@Inherited修饰)修饰,则其子类将自动被@xxx修饰
代码如下:

@Inheritable
 class Bese {
}
 class InheritableTest extends Bese{
     public static void main(String[] args) {
         //打印InheritableTest类是否有@Inheritable修饰。
         System.out.println(InheritableTest.class.isAnnotationPresent(Inheritable.class));
     }
}

返回结果为true。
自定义注解
格式:public @interface Test{
}
在注解中可以定义成员变量。
由于注解继承了java.lang.annotation.Annotation,所以在定义成员变量的时候,要以方法的形式来进行定义。

public @interface Testable {
    String name();
    int age();
}

一旦在注解中定义了成员变量,在使用注解的过程中需要为它的成员变量指定值。

例: @Testable(name = “xiaolu”,age=22)
如果不想赋初值的话,可以在定义注解的时候,指定默认值。

public @interface Testable {
    String name() default "xiaoming";
    int age() default 22;
}

接口中成员变量返回值只能有以下取值,
1.基本数据类型
2.String
3.枚举
4.注解
5.以上类型的数组
提取注解信息
使用注解修饰类、方法、成员变量之后,这些注解不会自己生效,必须由开发者提供相应的工具提取并处理注解信息。
java使用java.lang.annotation.Annotation接口来代表程序元素前面的注解,该接口是所以注解的父接口。从Java5开始,java.lang.reflect包所提供的反射API增加了读取运行时注解的能力。只有当定义注解是使用了@Retention(RetentionPolicy.RUNTIME)修饰,该注解才会在运行时可见,JVM才会在装载*.class文件中读取保存在class文件中的注解信息,所以这就是一般我们写注解的时候,都用这个@Retention(RetentionPolicy.RUNTIME)修饰。java.lang.reflect.AnnotatedElement,中AnnotatedElement接口值所有程序元素(如Class、Method、Constructor)的父接口,所以程序通过反射获得某个类的AnnotatedElement对象的时候,程序就可以通过调用方法来访问注解信息。
代码分析:

package cn.test;

import java.lang.annotation.Annotation;

/**
 * FileName: Mytest
 *
 * @Author:luguobao Date: 2020/5/1012:58
 * Description:
 * History:
 * <author>   <time>   <version>   <desc>
 * 作者姓名    修改时间    版本号       描述
 */
public class Mytest {
    @Inheritable
    @Testable(name = "xiaolu",age=22)
    public void info(){
        System.out.println("info...");
    }

    public static void main(String[] args) throws NoSuchMethodException {
        final Annotation[] mytests = Mytest.class.getMethod("info").getAnnotations();
        for (Annotation an:mytests
             ) {
            System.out.println(an);
        }
    }
}

输出结果为:在这里插入图片描述
把Mytest中info方法的所有注解都打印了出来。
使用注解的示例
示例一:第一个注解没有任何成员变量,只是一个标记注解,作用是标记哪些方法是可以测试的。

代码如下:

package cn.test;

/**
 * FileName: MyTest
 *
 * @Author:luguobao Date: 2020/5/1016:50
 * Description:
 * History:
 * <author>   <time>   <version>   <desc>
 * 作者姓名    修改时间    版本号       描述
 */
public class MyTest {
    @Testable
    public static void m1(){

    }
    @Testable
    public static void m2(){
        throw new IllegalArgumentException("参数出错了。。。");
    }
    @Testable
    public static void m3(){
        throw new RuntimeException("程序业务出现异常");
    }
    @Testable
    public static void m4(){

    }

    public static void m5(){

    }
}

这里定义了五个方法,此时,仅仅使用注解来标记程序元素对程序是不会有任何影响的,这个java注解的一条重要原则,为了让注解起作用,接下来需要为这些注解提供一个注解处理工具。
代码如下:

package cn.test;

import java.lang.reflect.Method;

/**
 * FileName: ProcessorTest
 *
 * @Author:luguobao Date: 2020/5/1016:58
 * Description:这是对@Testable注解进行处理的注解处理工具
 * History:
 * <author>   <time>   <version>   <desc>
 * 作者姓名    修改时间    版本号       描述
 */
public class ProcessorTest {
    /**
     *
     * @param clazz 代表要处理的类
     */
    public static void process(String clazz) throws ClassNotFoundException {
        int passed = 0;
        int failed = 0;
        //遍历clazz类中所有的方法
        for (Method m:
             Class.forName(clazz).getMethods()) {
            //如果该方法用@Testable修饰
            if(m.isAnnotationPresent(Testable.class)){
                try {
                    m.invoke(null);
                    passed++;
                } catch (Exception e) {
                    System.out.println("方法"+m+"运行失败"+"异常:"+e.getCause());
                    failed++;
                }
            }

        }
        System.out.println("共运行了"+(failed + passed) + "个方法,其中成功了"+passed+"个。失败了" + failed+"个");

    }

    public static void main(String[] args) throws Exception {
        process("cn.test.MyTest");
    }
}


测试结果如图所示:
在这里插入图片描述
这个注解的好处就体现在当我要测试一个类中方法能不能通过测试,如果用这个注解的方法,不管有几个方法直接在它上面加上注解。然后再启动一下注解处理工具即可。

由此可见,注解十分简单,它是对源代码增加的一种特殊标记,这些特殊标记可以通过反射获得,当程序获得这些特殊标记以后,程序可以做出相应的处理。

注解示例二
在反射的学习中,写了一个配置文件,并通过流来读出配置文件中的数据。由于注解也可以有实例变量,所以,可以通过注解来代替配置文件。
代码如下:

package cn.test2;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * FileName: AnnotationTest
 *
 * @Author:luguobao Date: 2020/5/1018:12
 * Description:
 * History:
 * <author>   <time>   <version>   <desc>
 * 作者姓名    修改时间    版本号       描述
 */
@annotation(className = "cn.sainai.Student",methodName = "study")
public class AnnotationTest {
    public static void main(String[] args) throws Exception {
        //1.获取该类的字节码文件
        Class<AnnotationTest> annotationTestClass = AnnotationTest.class;
        //2.获取该类的注解对象,生成了该注解的实现对象
        annotation a = annotationTestClass.getAnnotation(annotation.class);
        //得到该类的名称和要执行的方法。
        final String s = a.className();
        final String s1 = a.methodName();
        //3.加载类进内存
        final Class  cls = Class.forName(s);
        //4.创建对象
        final Object o = cls.newInstance();
        //5.获取方法
        final Method method = cls.getMethod(s1);
        //6.执行方法
        method.invoke(o);
    }
}

此时,只要改变注释中类名和方法名就可以执行该类中的该方法了。

类型注解
java8为ElementType枚举增加了TYPE_PARAMETER、TYPE_USE两个枚举值。类型注解可以用于修饰在任何地方出现的类型。在java8以前,只要在类、接口、方法、成员变量的时候使用注解。在8以后,可以在创建对象,类型转换、使用Implements实现接口、使用throws声明抛出异常时,都可以使用注解了。例子在p673中有。这种类型注解暂时不会其任何作用,但为它们提供处理工具时,才会提供作用。可以让编译器执行更加严格的代码检查,保证代码更加健壮。
APT(Annotation Processing Tool)
注解处理工具:作用:简化程序开发,程序员只要把一些关键信息通过注解写在程序上,然后使用APT工具就可以生成额外的文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值