Java注解

一、什么是注解

        Java注解用于为Java代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解可以用于这一目的。Java注解是从Java5开始添加到Java中。可以理解为“给计算机看的注释”。

        Java注解通常用于以下目的:

  • 编译器指令
  • 编译时指令
  • 运行时指令

二、Java内置注解

@Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。

@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。

@SuppressWarnings - 指示编译器去忽略注解中声明的警告。

@SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。

@FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。

三、自定义注解

        @interface是声明一个注解的关键字,而这个注解非常相似于接口。注解可有或没有元素。注解元素的特点:

  • 没有函数体;
  • 没有函数参数;
  • 返回的声明必须在一个特定的类型:
    • 基本类型 (boolean, int, float,…)
    • 枚举
    • 注解
    • String
    • Class(例如String.class)
    • 以上类型的数组
  • 元素可以有默认值;

        例如,下面是一个自定义注解:

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyFirstAnnotation {
    // element name.
    public String name();
    // Element description, default value "".
    public String description() default "";  
}

        其中,@Documented、@Target、@Retention是元注解。

        元注解是什么意思呢?元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。如果难于理解的话,你可以这样理解。元注解也是一张标签,但是它是一张特殊的标签,它的作用和目的就是给其他普通的标签进行解释说明的。元注解有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种。

1、元注解

(1)@Retention

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

        它的取值如下:

  • RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
  • RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
  • RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。这是最常用的。

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

(2)@Documented

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

(3)@Target

        Target 是目标的意思,@Target 指定了注解运用的地方。

        你可以这样理解,当一个注解被 @Target 注解时,这个注解就被限定了运用的场景。 @Target 有下面的取值:

  • ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
  • ElementType.CONSTRUCTOR 可以给构造方法进行注解
  • ElementType.FIELD 可以给属性进行注解
  • ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
  • ElementType.METHOD 可以给方法进行注解
  • ElementType.PACKAGE 可以给一个包进行注解
  • ElementType.PARAMETER 可以给一个方法内的参数进行注解
  • ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举

(4)@Inherited

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

        说的比较抽象。代码来解释。

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}
@Test
public class A {}
public class B extends A {}

        注解 Test 被 @Inherited 修饰,之后类 A 被 Test 注解,类 B 继承 A,类 B 也拥有 Test 这个注解。 

(5)@Repeatable

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

        当我们需要重复使用某个注解时,希望利用相同的注解来表现所有的形式时,我们可以借助@Repeatable注解。

        比如,@MyAnnotation被@Repeatable注解,可以这样用:

@MyAnnotation(...)
@MyAnnotation(...)
@MyAnnotation(...)
public class MyClass{
    ...
    }

2、注解的属性

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

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

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

        赋值的方式是在注解的括号内以 value=”” 形式,多个属性之前用 “,”隔开。

@TestAnnotation(id=3,msg="hello annotation")
public class Test {
}

        需要注意的是,在注解中定义属性时它的类型必须是 8 种基本数据类型外加String类、Class类、接口、注解及它们的数组。

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

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
    public int id() default -1;
    public String msg() default "Hi";
}

        TestAnnotation 中 id 属性默认值为 -1,msg 属性默认值为 Hi。
        它可以这样应用。

@TestAnnotation()
public class Test {}

        因为有默认值,所以无需要再在 @TestAnnotation 后面的括号里面进行赋值了,这一步可以省略。

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

public @interface Check {
    String value();
}

        上面代码中,Check 这个注解只有 value 这个属性。所以可以这样应用。

@Check("hi")
int a;

        这和下面的效果是一样的

@Check(value="hi")
int a;

        最后,还需要注意的一种情况是一个注解没有任何属性。比如

public @interface Perform {}

        那么在应用这个注解的时候,括号都可以省略。

@Perform
public void testMethod(){}

四、具体案例

        利用注解写一个简单的测试框架,当主方法执行后,会自动执行被检测的所有方法(被Check注解的方法),判断方法是否有异常,记录到文件中。

1、@Check类

package Study.Annotation_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 Check {
}

2、TestCheck类

package Study.Annotation_Study;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;

/**
 * @author NJUPT_MR.Z
 * @create 2022-05-16 20:11
 * @Description 简单的测试框架
 * 当主方法执行后,会自动执行被检测的所有方法(被Check注解的方法),判断方法是否有异常,记录到文件中
 */
public class TestCheck {
    public static void main(String[] args) throws IOException {
        //创建计算器对象
        Calculator c = new Calculator();
        //获取字节码文件对象
        Class cls = c.getClass();
        //获取所有方法
        Method[] methods = cls.getDeclaredMethods();

        int number = 0;//出现异常的次数
        BufferedWriter bw = new BufferedWriter(new FileWriter("src\\Study\\Annotation_Study\\bug.txt"));

        for (Method method : methods) {
            //判断方法上是否有Check注解
            if (method.isAnnotationPresent(Check.class)) {//有,执行
                try {
                    method.invoke(c);
                } catch (Exception e) {//捕获异常,记录到文件中
                    bw.write(method.getName() + "方法出异常了");
                    bw.newLine();
                    bw.write("异常的名称是:" + e.getCause().getClass().getSimpleName());
                    bw.newLine();
                    bw.write("异常的原因是+" + e.getCause().getMessage());
                    bw.newLine();
                    bw.write("-------------");
                    bw.newLine();
                    number++;
                }
            }
        }
        bw.write("本次测试一共出现 " + number + " 次异常");
        bw.flush();
        bw.close();
    }
}

3、利用上述两个类测试Calculator类

package Study.Annotation_Study;

/**
 * @author NJUPT_MR.Z
 * @create 2022-05-16 20:08
 * @Description 计算器类
 */
public class Calculator {
    @Check
    public void add() {
        System.out.println("1 + 0 = " + (1 + 0));
    }

    @Check
    public void sub() {
        System.out.println("1 - 0 = " + (1 - 0));
    }

    @Check
    public void mul() {
        System.out.println("1 * 0 = " + (1 * 0));
    }

    @Check
    public void div() {
        System.out.println("1 / 0 = " + (1 / 0));
    }
    @Check
    public void show(){
        String s = null;
        s.toString();
    }
}

        运行TestCheck类的main方法,生成bug.txt文件,内容如下:

div方法出异常了
异常的名称是:ArithmeticException
异常的原因是+/ by zero
-------------
show方法出异常了
异常的名称是:NullPointerException
异常的原因是+Cannot invoke "String.toString()" because "s" is null
-------------
本次测试一共出现 2 次异常

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值