Java注解

最近看了些Java的代码,发现注解这块自己一片空白,平时都是编译器内直接生成注解,自己从来没关心过,但是具体有什么用,怎么用,今天来系统学一下。本文主要是将这篇博客的内容整理在此,也加入了一些自己的理解。大家可以把本文当成该链接文章的读后感。

Java注解是在 Java SE 5.0 版本中开始引入的概念,java.lang.annotation是JAVA软件包中专门为Java注解提供库支持的。注解,简单来说,就是“做记号”。

目前自己对于注解的理解——注解一是用来告诉编译器一些信息,以便于在自己出差错时让编译器提醒自己的工具;二是用来给一些变量、函数“做记号”,以便根据“记号”找出它们以供使用下面先介绍怎么写注解,贴到哪个地方

注解的创建方式:

public @interface TestAnnotation {
}

注解的使用方式:

@TestAnnotation
public class Test {
}

元注解

(自认)元注解是给我们自定义的注解添加/设置一些关键属性用的,类似我们自定义注解中的一些核心属性。反过来,也可以将自定义注解理解为是一种元注解的组合方式——将多种元注解组合在一起,使用一种自定义注解相当于使用了多个元注解——从而简化了注解部分的代码量
元标签有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种。

@Retention:保留期(自己认为翻译为“有效期”更合适)

当 @Retention 应用到一个注解上的时候,负责说明这个注解的的有效期(即存活时间)。

  • RetentionPolicy.SOURCE:注解只保留到源码阶段,在编译器进行编译时它将被丢弃,失去作用。
  • RetentionPolicy.CLASS:注解只保留到编译进行的时候,它不会被加载到 JVM 中。有效期 到 编译完成or编译开始?
  • RetentionPolicy.RUNTIME:注解保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
//使用 [元注解@Retention] 设置注解
//指定 TestAnnotation 可以在程序运行周期被获取到
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
}

@Documented:文档

作用是能够将注解中的元素包含到 Javadoc 中去。

@Target:目标

作用是限制 所修饰注解 可以使用的地方。当一个注解被 @Target 注解时,这个注解就被限定了使用场景。举例来说,原本标签是你想张贴到哪个地方就到哪个地方,但是因为 @Target 的存在,它张贴的地方就有了限制,比如只能张贴到方法上、类上、方法参数上等等。@Target 的取值如下:

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

@Inherited:继承

作用是使所修饰的注解可以被继承——子类在没有使用别的相同注解的前提下,保留所有父类的注解。换句话说,如果一个类被 [ @Inherited 注解过的注解 ] 注解,在它的子类没有被任何注解应用的情况下,子类会直接继承父类的注解。

//定义test注解
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {

}

//使用test注解
@Test
public class A {}
public class B extends A {}

@Repeatable:可重复

当 @Repeatable 应用到一个注解上时,负责说明这个注解中的某个属性的值可以同时取多个,(自认)从而减少因为同属性不同值,而专门重新自定义不同注解去取不同值的麻烦。需要注意的是,需要存放多个值的属性不能是一个普通变量,而是数组,即便使用了@Repeatable,代码如下所示。
@Repeatable使用时,需要其后跟上一个容器注解(一个注解内部有数组),用于存储同属性的那不同个值

Java 1.8 加入的特性(待验证)。

@interface Persons {
	//函数返回值定义了对应成员变量的类型( Person[] )
	//函数名定义了对应成员变量的名字(value()),按照规定,它里面必须要有一个 value 属性
	//快速记忆方法:正好对应着“ 类型 变量名 ”的变量声明语法,只不过结尾处多了一个()
    Person[]  value();//该数组用于存放重复属性的多个值
}

//将 Person注解 标示为“可重复”,并给出Persons.class为容器注解,来存储重复的属性
@Repeatable(Persons.class)
@interface Person{
	//设置role的默认值为空?
    String role default "";
}

//使用方式
@Person(role="artist")//给 Person 这个注解的 role 属性赋值为artist
@Person(role="coder")
@Person(role="PM")
public class SuperMan{
}

简单点说,Persons 是一个总标签,上面贴满了与Person同一类型、但内容不同的标签。贴上Persons,相当于同时给他贴了Persons上的所有标签。

注解的属性

定义

前面提到过,注解的属性也叫做成员变量。注解只有成员变量,没有函数,所以不必担心注解的属性都用函数形式表示,与真正的函数无法区分的问题。成员变量是使用无形参的函数的形式来声明、定义的。其中,函数名定义了对应成员变量的名字,函数返回值定义了对应成员变量的类型。注解中定义的属性类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组。

@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 {
}

设置默认值

在定义时,设置默认值。注解中属性默认值需要用 default 关键字指定。如下所示:

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

当这个注解有默认值之后,就可以不用在使用时赋值,如下所示。当然,当你不想使用默认值时,还是需要在使用时赋值的

@TestAnnotation()
public class Test {}

注解的省略写法

注解内仅一个属性时

如果一个注解内仅仅只有一个名为 value 的属性时,使用这个注解时可以直接将属性值写到括号内,而不必再写赋值语句“ (id=3 , msg= “hello annotation” ) ”。

public @interface Check {
    String value();
}

//不必再写@Check(value = "hi")
@Check("hi")
int a;

注解内没有属性时

使用没有属性的注解括号可以省略
快速记忆:有属性,就要赋值,所以有括号。无属性,不用赋值,没有括号。

public @interface Perform {}

@Perform
public void testMethod(){}

Java预设的注解

这里介绍的是Java自带,但不是元注解,的注解。

@Deprecated

作用是给过时的元素做上记号,有了这个记号之后再使用这个元素时,编译器会有提示,如下图所示:

public class Hero {
    @Deprecated
    public void say(){
        System.out.println("Noting has to say!");
    }
    public void speak(){
        System.out.println("I have a dream!");
    }
}

@Deprecated过时提醒
编译器在编译阶段遇到这个注解时还会发出提醒警告,告诉开发者正在调用一个过时的元素,比如过时的方法、过时的类、过时的成员变量。

@Override

作用是提示子类要复写父类中被 @Override 修饰的方法。

补反例:没重写的话有无提醒,有无警告

@SuppressWarnings

作用是屏蔽警告。比如,调用被(上面刚提过的 )@Deprecated 注解(即过时)的方法后,编译器会警告提醒,而有时开发者不需要这种警告,就可以在调用的地方使用 @SuppressWarnings 达到这一目的,如下所示(下面这段代码,是配合上面@Deprecated的例子一块用的)。

//在表明屏蔽@Deprecated后,再运行,将没有过时提醒。
@SuppressWarnings("deprecation")
public void test1(){
    Hero hero = new Hero();
    hero.say();
    hero.speak();
}

@Autowired(补)

补入这篇博客

与@Resource的区别

@SafeVarargs(补)

@FunctionalInterface(补)

函数式接口 (Functional Interface) 就是一个具有一个方法的普通接口。
用途:函数式接口可以很容易转换为 Lambda 表达式。函数式接口注解是在Java 1.8 版本引入的新特性,当时函数式编程很火,所以 Java 8 也及时添加了这个特性。

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

注解的获取与使用

此处我们再讲如何把这些注解标签在合适的时候撕下来,然后检阅上面的内容信息。获取注解,我们需要用到Java中的反射机制。补(语法也要学,比如,TestAnnotation类;" .class" 的用法)

通过反射获取类上的注解

首先可以通过 Class 对象(类名.class.isAnnotationPresent)的 isAnnotationPresent() 方法判断它是否应用了某个注解。

public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}

补:①类名.class.isAnnotationPresent ②Class<? extends Annotation>的使用语法

然后通过 getAnnotation() 方法来获取 Annotation 对象,返回指定类型的注解。

//            返回值类型       		函数名
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}

或者是 getAnnotations() 方法,返回注解到这个元素上的所有注解。

//      返回值类型       函数名
public Annotation[] getAnnotations() {}

在注解这个类的同时,就可以在类中调用注解了,见下面的例子(TestAnnotation的定义,见上面 “设置默认值” 这一节)

@TestAnnotation()
public class Test {
    public static void main(String[] args) {
        boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
         //此处要先判断一下,是否有Annotation
        if ( hasAnnotation ) {
            TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
            System.out.println("id:"+testAnnotation.id());
            System.out.println("msg:"+testAnnotation.msg());
        }
    }
}

得到了原来标注在类Test上@TestAnnotation()属性中的值,运行结果如下:

id:-1
msg:

通过反射获取属性、方法上的注解

方法如下所示

@TestAnnotation(msg="hello")
public class Test {

    @Check(value="hi")
    int a;
    
    @Perform
    public void testMethod(){}
    
    @SuppressWarnings("deprecation")
    public void test1(){
        Hero hero = new Hero();
        hero.say();
        hero.speak();
    }
——————————————————————————————————————————————————————————————————————————————
    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("msg:"+testAnnotation.msg());
        }
        try {
        	//Field类简介,看下方介绍
        	//下面代码,补看
            Field a = Test.class.getDeclaredField("a");
            a.setAccessible(true);
            //获取一个成员变量上的注解
            Check check = a.getAnnotation(Check.class);
            if ( check != null ) {
                System.out.println("check value:"+check.value());
            }
            Method testMethod = Test.class.getDeclaredMethod("testMethod");
            if ( testMethod != null ) {
                // 获取方法中的注解
                Annotation[] ans = testMethod.getAnnotations();
                for( int i = 0;i < ans.length;i++) {
                    System.out.println("method testMethod annotation:"+ans[i].annotationType().getSimpleName());
                }
            }
        } catch (NoSuchFieldException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println(e.getMessage());
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println(e.getMessage());
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
    }
}

运行结果如下:

id:-1
msg:hello
check value:hi
method testMethod annotation:Perform

注意,如果一个注解要在运行时被成功提取,那么@Retention(RetentionPolicy.RUNTIME) 是必须的。

SpringMVC中使用的注解

@RequestMapping(“/路径名”)

通过@RequestMapping(“/路径名”)使得用户输入的网址转换为了自己开发的对应类中的对应方法。页面上的每个组件通过此映射可以指定映射到服务器端程序的具体某一个方法
其内的各个参数如下,具体用法可以参考这篇博客以后整理入本文。

@RequestMapping(“/paperfile”)等同于@RequestMapping(value=“/paperfile”)?

@RequestMapping("/paperfile")
public class PaperfileController extends BaseController {
	@RequestMapping("/delivery")
	public void deliveryPaperfile(HttpServletRequest request) {
	}
}

@RequestMapping(params = “minidaoListDemo”)

@RequestMapping(method = RequestMethod.GET)

@RequestMapping(value=“/list”,method = RequestMethod.GET)

@RequestBody与@RequestParam

补入这篇博客这篇

@RequestParam(name = “username”) 参数名

目前自己理解,用于从网址中截取username,并将其赋值给其后的“参数名”,用法如下所示,从http://localhost:8080/sealManage/index.jsp?username=111中取到username。

@Controller
@RequestMapping("/index")
public class SealManageController {
    @RequestMapping("/getData")
    public ModelAndView getSealPendingList(@RequestParam(name = "username") String username)

上述代码还可以通过http://localhost:8080/sealManage/index/getData?username=111
补入此博客

@Column

来自@interface javax.persistence.Column,用来关联实体类与数据库表中对应列之间的映射关系。

@Column(name ="ISENABLE",nullable=true,length=50)

在这里插入图片描述

@Api注解

来自io.swagger.annotations.ApiOperation包,是swagger为了识别哪些是要通过swagger来给用户展示的接口所在的controller类,经常在接口开发中使用。在controller上先添加一个@Api注解,再在接口方法上添加一个@ApiOperation(value=“jeecgDemo列表信息”,produces=“application/json”,httpMethod=“GET”)注解,这样设置会使同一个controller的接口方法都放在一起,效果如下图所示如下图所示。
在这里插入图片描述

@Api(value=“JeecgDemo”,description=“Jeecg接口开发Demo”,tags=“JeecgAPIDemo”)表示,tags属性对应第一个红框中显示的文字,description属性对应第二个红框中显示的文字。

注解的用途

官方给出的解释如下:

注解是一系列 元数据(就是指元注解?),它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。

注解有许多用处,主要如下:

提供信息给编译器: 编译器可以利用注解来探测错误和警告信息
编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
运行时的处理: 某些注解可以在程序运行的时候接受代码的提取
值得注意的是,注解不是代码本身的一部分。

简单来说,注解不会直接影响程序的运行,不是代码本身的一部分。注解主要针对的是编译器和其它工具软件(SoftWare tool),是给编译器和APT 用的

APT(Annotation Processing Tool):处理提取和处理 Annotation 的代码统称 当开发者使用注解修饰类、方法、Field 等成员之后,这些注解不会自己生效,必须由开发者提供相应的代码来提取并处理注解中的信息(这段话,也是对注解的一个很生动的解释)。

注解实战实例(仅是自己看完本部分的概括和观后感,想看具体代码的,请看原文)

此处对应上方链接中“亲手自定义注解完成某个目的”的部分。
看原文代码之前,看博主的介绍,感觉是个很强大的应用。看完代码之后,感觉注解在其中就是起了一个记号的作用——将需要测试的代码、函数用注解标上记号,然后将运行注解函数的代码包裹进try catch里面,以便收集其中所有异常,并将有注解的代码函数都调用一遍,最后统计try catch中的异常数量——若异常为0,则代码没问题,仅此而已。注解起到的作用仅仅是给那些是需要测试的函数做上记号,方便找出它们而已,检测也仅仅是用try catch来捕获其中其中异常。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值