java基础-注解篇

    相信搞Java开发的没有没见过java注解的,形如@XXXX这种形式。特别是在最近的web开发框架中,注解表达形式更是大行其道,大有取代原来xml文件配置方式的趋势。在平常应用中,只知道加上这个注解起什么作用,但是怎么起作用就不太清楚,也就是知其然而不知其所以然,所以在闲暇之余就有了这篇注解学习篇。

      本文参考《秒懂,java注解你可以这样学》,这篇文章真的写的很好,也是看了这篇文章才催发我写下学习博客的动力。首先这篇文章的前沿就给了我极大的冲击,它告诉了我如何去学习一门新东西,我记得里面最精辟的一句话就是“用专业名词去解释专业名词是及其糟糕的”,这句话给了我极大的共鸣,因为自己在读技术手册时经常就受到此种描述方式的困扰,此文作者用标签来比喻注解非常形象非常容易理解,使我受益斐然,让我明白了一个道理:学习新事物是需要实物化的过程,总结提炼是需要抽象化的一个过程。好了,开始我们的注解之旅吧。

      java注解的官方解释“注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释”。这是百度百科的定义,这样的定义相信大家看得都是一只半解,那如何才能让别人能够直观的感受注解,这里我借用引文里的比喻【标签】,可以将注解理解为生活中所用的标签,一个人可以为其贴上诚实、勇敢、聪明的标签,一台设备可以为其贴上设备编号、设备名称的标签,总之注解就是类、属性、方法上的标签,作为它们的一个扩展信息。

注解

注解的定义:注解是在java SE5.0版本引入的,定义同接口类相似也是属于一种类型,通过关键字@interface定义

public @interface TestAnnotation{

}

TestAnnotation就是我们定义好的标签,这个标签就可以用在类、属性和方法上了

@TestAnnotation()
public class Test{

}

既然我们是标签,那么标签信息放在哪里呢,自然是放在标签里面,信息就是标签的属性,通过属性方法定义:

public @interface TestAnnotation{
    int id();
    String name();
}

id和name就是我们的注解属性,相当于我们为一张标签规划好了消息结构,如设备的名称、设备编号等,当我们将某张标签贴到某台设备上时,就在标签上填写设备的信息,形如

@TestAnnotation(id = 12,name = "xiaowang")
public class Test{
    

}

通过属性 = 值的方式进行赋值,对于特殊属性value属性,且只有其一个属性时可以简写为@Annotation("value值")

上面我们已经讲了可以将注解标签贴在类上、方法上和属性上,就像机器标签用于贴在机器上,书本标签用来贴在书本上,水果标签用来贴上水果上的,那么如何分类这些标签,限定他们只能用在想要被用到的地方呢?这就需要用到【元注解】,这又是一个难懂的词汇哈,所谓元注解就是java定义好的注解,是用于注解上的注解。

元注解

元注解是注解的注解,是对注解进行分类、解释说明的,它主要包括@Retention、@Target、@Documented、@Inherited、@Repeatable

@Retention

Retention译为保留的意思,它是用于限定这个注解的实效的,也就是这个标签可以保留到什么阶段,在java程序里面可以分为源码阶段、编译阶段、运行阶段,其值有下面几个枚举

- RetentionPolicy.SOURCE   源码阶段,此注解只存在于源码阶段,在代码编译期会被擦除掉

- RetentionPolicy.CLASS      编译阶段,此注解存活于编译阶段,不会被加载到JVM中

- RetentionPolicy.RUNTIME 运行阶段,注解可以保留在程序整个运行期间

@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation{
    int id();
    String name();
}

如上定义了TestAnnotation注解可以在程序运行期间存在。

@Target

Target目标用于对注解进行分类,表明注解可以用在哪个地方,其取值有

- ElementType.ANNOTATION_TYPE 可以用于为注解标注注解

- ElementType.CONSTRUCTOR 可以给构造方法进行注解

- ElementType.FIELD 可以给属性进行注解

- ElementType.LOCAL_VARIABLE 可以给局部变量进行注解

- ElementType.METHOD 可以给方法进行注解

- ElementType.PACKAGE 可以给包进行注解

- ElementType.PARAMETER 可以给方法内的参数进行注解

- ElementType.TYPE 可以给一个类型进行注解,如类、接口、枚举

其属性值基本涵盖了一个java类的所有成员,也就是说可以在一个类的任意地方加上注解标签。

@Documented

Document 文档,用于将注解中的元素包含到javadoc中去

@Inherited

inherited 继承,被此标记注解标注的类,其子类如果没有被其它注解应用则自动继承此注解,如下所示

@Inherited
public @interface test{
    
}
@test
public class  A{}

public class B extends A{}  //相当于有一个@test注解

test注解被标注为可继承,将其应用于A超类上,那么子类B上没有任何它注解则默认相当于加了@test注解

@Repeatable

Repeatable 可重复的,顾名思义其表示注解可以重复使用,也就是此类标签可以贴多张,就像人的技能标签一样,会唱歌,会做饭,应用如下:

public @interface Skills{
    Skill[] value();
}

@Repeatable(Skills.class)
public @interface Skill{
     String skill();
}

@Skill(skill = "sing")
@Skill(skill = "dangce")
public class B {}

上面对注解的限定与规则做了一个简要介绍,下面就讲一讲如何去获取标签的内容。

注解内容提取

大家在逛超市选东西的时候,一眼就能看到货架上商品的标签信息。但是咋们的java程序没有长眼睛,它如何能够查看我们为某一类、方法或者是属性贴注的标签信息呢,这肯定是有办法的,如果不能看内容那注解也就没有任何意义了,那就得依靠java的反射大法了,通过java反射我们能够获取到类、方法、属性等等上面依附的注解标签,拿到标签就能查看其内容了,我们通过一个例子来说明

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

    @Target(ElementType.FIELD)
    public @interface Check{
        String value();
   }

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

   @TestAnnotation(id = 12,name = "xiaowang")
   public class Test{

        @Check("xiaoh")
        int n;

        @Perform
        public void testMethod(){}

        @SuppressWarnings("deprecation")
        public void test(){
            System.out.println("this is annotationTest");
        }

   }
   public static void main(String[] args){
        boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
        if(hasAnnotation){
            //获取注解
            TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
            System.out.println("id:"+ testAnnotation.id());
            System.out.println("name" + testAnnotation.name());
        }

        try
        {
            Field n = Test.class.getDeclaredField("n");//获取已定义的属性
            n.setAccessible(true);//设置私有属性可访问
            Check  check = n.getAnnotation(Check.class);
            if(null != check){
                System.out.println("check value is" + check.value());
            }
            Method method = Test.class.getDeclaredMethod("testMethod");
            Annotation[] annotations = method.getAnnotations();
            for(int i = 0;i < annotations.length;i++){
                System.out.println("method testMethod annotation is:" + annotations[i].annotationType().getSimpleName());
            }
        }catch (Exception e){
            e.printStackTrace();
        }


   }

}
程序输出如下:id:12
namexiaowang
method testMethod annotation is:Perform


Process finished with exit code 0

可以看到,我们可以通过反射获取其身上依附的标签,并且拿到标签内的内容,需要注意的是反射是在程序加载运行时发生的,所以其只能获取到注解生命周期@Retention中RetentionPolicy.RUNTIME标注的注解,文章演示示例是通过反射来处理注解的,也可在程序编译期来处理注解。

注解的应用

介绍了注解的内容,让我们谈谈注解的应用,目前注解的应用比较灵活广泛,可以用于信息配置,输入参数校验,方法测试,方法拦截、方法测试输出javadoc文档等,用得着的地方你就用,不要拘泥于它现有的使用场景,咋们就方法测试来举例吧。

咋们首先定义一个注解标签用于检测我们的算术方法

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Calculate
{
}

随后我们的测试目标类,提供了加减除三种算术方法,咋们分别打上@Calculate计算处理标签

public class MyCalculate
{
    @Calculate
    public void add(){
        int n = 2 + 1;
        System.out.println("加法结果是:" + n);
    }

    @Calculate
    public void subtracation(){

        int n = 5 - 1;
        System.out.println("减法的结果是:"+ n);
    }

    @Calculate
    public void division(){
        int a = 3;
        int b = 0;
        int c = a/b;
        System.out.println("除法的结果是:" + c);
    }
}

注解标签处理类:

public class CalTest
{
    public static void main(String[] args){
        MyCalculate calculateObj = new MyCalculate();
        Method[] methods = MyCalculate.class.getDeclaredMethods();
        for(int i = 0;i < methods.length;i++){
            boolean hasAnnotation = methods[i].isAnnotationPresent(Calculate.class);
            if(hasAnnotation){
                try{
                    methods[i].invoke(calculateObj,null);
                }catch (Exception e){
                    System.out.println("出错方法:" + methods[i].getName());
                    e.printStackTrace();
                }
            }
        }
    }
}

通过反射调用,获取被计算测试注解标注的方法,执行方法检测是否有异常抛出,得到结果如下

java.lang.reflect.InvocationTargetException
加法结果是:3
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
减法的结果是:4
出错方法:division
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at basejava.annotation.CalTest.main(CalTest.java:22)
Caused by: java.lang.ArithmeticException: / by zero
at basejava.annotation.MyCalculate.division(MyCalculate.java:30)

... 5 more

通过结果可以看出成功定位到了错误方法。


以上大致就是注解的基本内容,我们首先要明白注解的一个基本概念,了解其使用场景,知道注解的处理方式(反射,编译期),然后在以后具体的使用场景中在来设计注解的应用目标,下面一篇接着讲《java反射》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值