Java注解解析及示例

首先引入官方的一句话:Java注解用于为Java代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。也许你对这句话不是很理解,从下面的说明中我们就能逐渐理解这句话的含义。

一、注解的定义

Java注解是从Java5开始引入的,它是类的一种类型,修饰符是@interface。

二、元注解

在写一个注解之前我们要对元注解的概念进行理解。元注解,顾名思义,就是最基础的注解,自定义的一些注解都要依赖于这些元注解。常用的元注解有以下五种:
(1)@Retention
Retention中文有保留、保持的意思,它用于声明注解保留存在于编译期(源码)、类加载(字节码)还是运行期(JVM解释运行过程),在@Retention注解中使用枚举RetentionPolicy来表示注解保留时期:

保留时期说明
@Retention(RetentionPolicy.SOURCE)注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS)默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
@Retention(RetentionPolicy.RUNTIME)注解会在class字节码文件中存在,在运行时可以通过反射获取到

说明:如果我们使用到了自定的注解,如果只存在于源码或字节码文件中,那么在运行时就无法使用,也就无法发挥作用,所以自定义注解一般使用@Retention(RetentionPolicy.RUNTIME)保留策略。
(2)@Target
Target中文有目标的意思,它用于声明注解作用的范围:

作用范围说明
@Target(ElementType.TYPE)作用于接口、类、枚举、注解
@Target(ElementType.FIELD)作用于属性字段、枚举的常量
@Target(ElementType.METHOD)作用于方法
@Target(ElementType.PARAMETER)作用于方法参数
@Target(ElementType.CONSTRUCTOR)作用于构造函数
@Target(ElementType.LOCAL_VARIABLE)作用于局部变量
@Target(ElementType.ANNOTATION_TYPE)作用于注解
@Target(ElementType.PACKAGE)作用于包
@Target(ElementType.TYPE_PARAMETER)作用于类型泛型,即泛型方法、泛型类、泛型接口 (jdk1.8加入)
@Target(ElementType.TYPE_USE)类型使用,可以用于标注任意类型除了 class (jdk1.8加入)

(3)@Inherited
Inherited的中文意思是继承,一个被@Inherited注解标注的注解修饰的父类,如果它的子类没有被其它注解所修饰,则它的子类也会继承该父类的该注解。
(4)@Documented
Documented中文意思是文档。它的作用是将注解中的元素包含到Javadoc中去。
(5)@Repeatable
Repeatable中文意思是可重复的,被这个元注解修饰的注解可以作用于一个对象多次,可用于多次元数据赋值操作。

三、注解属性

注解中属性可以有以下类型:

  • 基本数据类型
  • String类型
  • 枚举类型
  • 注解类型
  • Class类型
  • 以上类型的一维数组类型
四、使用示例

1)编写一个注解People.java,定义了三个属性。

package com.lpl.anno;

import java.lang.annotation.*;

/**
 * 人类注解
 */
@Documented     //将注解中的元素包含到Javadoc中
@Inherited      //该注解修饰的一个父类,如果它的子类没有被其它注解修饰,则它的子类也继承了该父类的该注解
@Retention(RetentionPolicy.RUNTIME)     //该注解会在运行时存在(存在于class字节码文件中)
@Target(ElementType.TYPE)   //作用于接口、类、枚举、注解上
public @interface People {

    String name() default "人类";    //注解属性,姓名

    int age() default 0;       //注解属性,年龄

    Game[] value() ;    //玩的游戏,定义为游戏注解类型数组

}

其中,属性value用到了Game注解数组,编写Game.java注解,

package com.lpl.anno;

import java.lang.annotation.*;

/**
 * 游戏注解
 */
@Repeatable(People.class)   //@Game注解可重复标注,声明了此注解的目标注解必须要指定value属性
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Game {

    String value() default "";  //游戏名称

    String name() default "";   //游戏名称
}

2)编写一个父类Father.java,使用注解People修饰,并指定其属性值。然后获取定义的属性值打印输出。

package com.lpl.anno;

/**
 * 父类,标注了自定义注解People
 */
@People(value = {@Game(value = "王者荣耀"), @Game(value = "部落冲突")},
        name = "父亲", age = 50)      //自定义注解提供元数据
public class Father {

    public static void main(String[] args) {

        //获取Father的Class对象
        Class<Father> fatherClass = Father.class;
        //获取指定类型的public自定义注解
        People peopleAnnotation = fatherClass.getAnnotation(People.class);
        //获取注解属性
        String name = peopleAnnotation.name();
        int age = peopleAnnotation.age();
        System.out.println("姓名:" + name + ",年龄:" + age + ",喜欢的游戏:");
        //获取注解数组类型的注解属性
        Game[] games = peopleAnnotation.value();
        for (int i=0; i<games.length; i++){
            String gameName = games[i].value();
            System.out.print(gameName + " ");
        }
    }
}

3)编写子类Son.java,继承自父类Father,因为父类的自定义注解声明了元注解@Inherited,所以也会继承父类的该注解,此时子类拿到的是父类注解声明的属性值。当然子类也可自己声明自定义注解People并指定属性值。

package com.lpl.anno;

/**
 * 子类,继承自父类Father,未标注任何注解,所以可继承父类的自定义注解People(自定义注解People标注
 *      了元注解@Inherited)
 *
 *     如果此处未声明注解,使用的就是父类上声明的自定义注解及注解中元数据。
 *     如果此处未声明注解元数据,使用的是自定义注解默认元数据
 */
@People(value = {@Game(value = "王者荣耀"), @Game(value = "LOL")}, name = "儿子", age = 23)
public class Son extends Father {

    /**
     * 子类未声明注解的情况下,也可以获取父类自定义注解(前提是父类自定义注解有@Inherited元注解)
     */
    public static void main(String[] args) {

        //获取Son的Class对象
        Class<Son> sonClass = Son.class;
        //获取继承自父类指定类型的public自定义注解
        People peopleAnnotation = sonClass.getAnnotation(People.class);
        //获取注解属性
        String name = peopleAnnotation.name();
        int age = peopleAnnotation.age();
        System.out.println("姓名:" + name + ",年龄:" + age + ",喜欢的游戏:");
        //获取注解数组类型的注解属性
        Game[] games = peopleAnnotation.value();
        for (int i=0; i<games.length; i++){
            String gameName = games[i].value();
            System.out.print(gameName + " ");
        }

    }
}
五、JDK提供的注解
注解作用
@Override它是用来描述当前方法是一个重写的方法,在编译阶段对方法进行检查
@Deprecated它是用于描述当前方法是一个过时的方法
@SuppressWarnings去除程序中的警告
六、应用示例

下面我们看一个银行转账的例子,银行转账限额会根据汇率变化,我们可以通过注解灵活配置转账限额,而不必每次都去修改业务代码。
1)编写转账限额注解TransferMoneyLimit.java,属性是转账限额大小。

package com.lpl.anno;

import java.lang.annotation.*;

/**
 * 定义转账限额注解
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TransferMoneyLimit {

    double maxMoney() default 10000;    //转账限额,默认值为10000
}

2)编写转账业务类TransferService.java,使用注解配置转账限额。

package com.lpl.anno;

import java.lang.reflect.Method;

/**
 * 转账业务类
 */
public class TransferService {

    /**
     * 转账金额限制
     */
    @TransferMoneyLimit(maxMoney = 12000)   //指定转账限额为12000
    public static void transferMoney(double money){
        String processResult = processTransferMoney(money);
        System.out.println(processResult);
    }

    /**
     * 处理转账金额
     */
    private static String processTransferMoney(double money) {

        try{
            //1.获取当前业务类的Class对象
            Class<TransferService> transferServiceClass = TransferService.class;
            //2.获取转账方法对象
            Method transferMoneyMethod = transferServiceClass.getDeclaredMethod("transferMoney", double.class);
            //3.判断方法上是否有指定类型注解
            boolean annotationPresent = transferMoneyMethod.isAnnotationPresent(TransferMoneyLimit.class);
            if (annotationPresent){
                //4.获取注解对象
                TransferMoneyLimit transferMoneyLimitAnnotation = transferMoneyMethod.getAnnotation(TransferMoneyLimit.class);
                //5.获取注解属性数据
                double maxMoney = transferMoneyLimitAnnotation.maxMoney();
                if (money > maxMoney){  //如果大于转账限额
                    return "转账金额大于限额,转账失败!";
                }else{
                    return "转账金额为:" + money + ",转账成功!";
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return "转账发生异常!";
    }

    public static void main(String[] args) {
        transferMoney(15000);
    }
}

参考:Java 注解完全解析

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值