Java基础教程:注解

本篇文章参考的相关资料链接:

  • 维基百科:https://zh.wikipedia.org/wiki/Java%E6%B3%A8%E8%A7%A3
  • 注解基础与高级应用:http://linbinghe.com/2017/ac8515d0.html
  • 秒懂注解:https://blog.csdn.net/briblue/article/details/73824058

概述

  这篇文章参考了很多其他文章的写作思路和篇章内容,主要用来帮助我们更好的理解Java中注解的使用,解开注解的神秘面纱。

  维基百科上对注解的解释是这样的:

  Java注解又称Java标注,是Java语言5.0版本开始支持加入源代码的特殊语法元数据[1]。Java语言中的类、方法、变量、参数和包等都可以被标注。和Javadoc不同,Java标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中Java虚拟机可以保留标注内容,在运行时可以获取到标注内容[2]。 当然它也支持自定义Java标注[3]

  我们可以这样理解,就是我们在类、方法、变量、参数等元素上面贴一个标签,并且我们能够在运行时动态的获取到这些标签

 

  我们在方法上贴了一个名为RoleCheck的标签,它里面有一个标签的描述信息为level。在运行该方法时,我们同时可以获取到这个标签及里面的描述信息。具体的语法和更多的内容我们将会在下面的文章中分享到。

内置注解

Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中

作用在代码的注解是

  • @Override - 检查该方法是否是重载方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
  • @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
  • @SuppressWarnings - 指示编译器去忽略注解中声明的警告

作用在其他注解的注解(或者说 元注解)是:

  • @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问
    • SOURCE:注解将被编译器丢弃
    • CLASS:注解在class文件中可用,但会被JVM丢弃
    • RUNTIME:JVM将在运行期间保留注解,因此可以通过反射机制读取注解的信息。
  • @Documented - 标记这些注解是否包含在用户文档中。
  • @Target - 标记这个注解应该是哪种 Java 成员
  • @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

关于Target:

你可以这样理解,当一个注解被 @Target 注解时,这个注解就被限定了运用的场景

类比到标签,原本标签是你想张贴到哪个地方就到哪个地方,但是因为 @Target 的存在,它张贴的地方就非常具体了,比如只能张贴到方法上、类上、方法参数上等等。@Target 有下面的取值

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

从 Java 7 开始,额外添加了 3 个注解:

  • @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
  • @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
  • @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

自定义注解

  内置注解我们可以直接用到就只有lang中的几个,annotation中几个注解的都是作用在其他注解上的,我们称之为元注解。其他注解就是我们的自定义注解

定义注解

  使用@interface自定义注解,会自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。

  在定义注解时,不能继承其他的注解或接口

  @interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

public @interface RoleCheck {
int[] level() default 0;
}

  比如上面这个代码中,我们定义一个名叫RoleCheck的注解(标签),和一个名为level配置参数(一个标签描述信息,是数值型的),并且默认值为0

描述注解

  但是现在这个注解并不能直接使用,因为我们还没有用元注解去描述这个注解。

  1.这个注解在哪里保存呢?

    @Retention,当然是RUNTIME:JVM将在运行期间保留注解,因此可以通过反射机制读取注解的信息。

  2.这个注解的运用场景是哪里?也就是说它被写在哪里?

    @Target,我们选择 ElementType.METHOD ,它可以给方法进行注解。

  3.用不用@Document?

    @Documented 表示含有该注解类型的元素(带有注释的)会通过javadoc或类似工具进行文档化,Documented是一个标记注解(类似@Override 这种只需要一个简单的声明即可的注解即为标记注解),没有成员。我们这里用或不用都可以,因为我们不涉及文档化处理,但是还是写上了。

  最终注解将变成如下的样子:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RoleCheck {
int[] level() default 0;
}

说明:

  如果只有一个参数成员,最好把参数名称设为”value”,后加小括号。注解在只有一个元素且该元素的名称是value的情况下,在使用注解的时候可以省略“value=”,直接写需要的值即可。也就是下面这样。

public @interface RoleCheck {
int value() default 0;
}
//用的时候,不用写参数名称了,直接写值
@RoleCheck(1)
public void doSomeThing()
{
.....
}

使用注解

//注解没有属性
@RoleCheck
public void do(){.....} //注解只有一个参数为value的属性
@RoleCheck(1)
public void do(){.....} //注解有一到多个属性
@RoleCheck(level=1,name="MS")
public void do(){.....}

注解处理器

  看完以上的内容,我们其实只是做了一件事,就是给不同的物件贴上标签,但是只贴上标签,如果我们不会根据标签进行处理的话,就毫无意义了,和注释毫无区别

  注解处理器就是我们的处理环节,注解处理器可以在运行时通过反射机制读取到标签的信息从而进行处理

1、首先可以通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解

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

2、然后通过 getAnnotation() 方法来获取 Annotation 对象。

public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}

或者是 getAnnotations() 方法。

public Annotation[] getAnnotations() {}

前一种方法返回指定类型的注解,后一种方法返回注解到这个元素上的所有注解

一个实例

  我们做的事情是什么,通过class对象的 isAnnotationPresent()判断我Test类是不是有@RoleCheck注解,如果有的话就把这个注解的描述信息打印出来,这里打印出的是默认值。

@RoleCheck
public class Test { public static void main(String[] args) { boolean hasAnnotation = Test.class.isAnnotationPresent(RoleCheck.class); if ( hasAnnotation ) {
RoleCheck testAnnotation = Test.class.getAnnotation(RoleCheck.class);
System.out.println("id:"+roleCheck.level());
}
} }

  上面的例子中,只是检阅出了注解在类上的注解,其实属性、方法上的注解照样是可以的。同样还是基于反射。

     //返回HelloController类中所有定义的方法
Method[] methods = HelloController.class.getDeclaredMethods();
//遍历每一个方法
for(Method method:methods)
{
//对于标有注解,且名称为doSomething的方法进行处理
RoleCheck roleCheck =method.getAnnotation(RoleCheck.class);
if(roleCheck!=null&&method.getName().equals("doSomething"))
{
//..........
}
}
**********************************************************
//返回HelloController类中所有定义的字段
Field[] fields = HelloController.class.getDeclaredFields();
//遍历每一个字段
for(Field field :fields)
{
RoleCheck roleCheck = field.getAnnotation(RoleCheck.class);
if(roleCheck!=null&&field.getName().equals("level"))
{
//.......
}
}

注解的使用场景

  当开发者使用了Annotation 修饰了类、方法、Field 等成员之后,这些 Annotation 不会自己生效,必须由开发者提供相应的代码来提取并处理 Annotation 信息。这些处理提取和处理 Annotation 的代码统称为 APT(Annotation Processing Tool)。我们可以给自己答案了,注解有什么用?给谁用?给 编译器或者 APT 用

  比如举一个注解使用实例,JUNIT测试框架:

public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}

  @Test 标记了要进行测试的方法 addition_isCorrect().

  再举一个注解使用实例,SpringMVC

@RequestMapping(value = "/add.do")
public String addOrder(Order order) {
try {
orderService.add(order);
return "操作成功";
} catch (Exception e) {
e.printStackTrace();
return "操作失败,请重试";
}
}

  @RequestMapping表明了要进行地址映射的方法。

Java基础教程:注解的更多相关文章

  1. Java基础教程——注解

    注解 JDK 5开始,Java支持注解. 注解,Annotation,是一种代码里的特殊标记,这些标记可以在编译.类加载.运行时被读取并执行,而且不改变原有的逻辑. 注解可以用于:生成文档.编译检查. ...

  2. Java基础教程(18)--继承

    一.继承的概念   继承是面向对象中一个非常重要的概念,使用继承可以从逻辑和层次上更好地组织代码,大大提高代码的复用性.在Java中,继承可以使得子类具有父类的属性和方法或者重新定义.追加属性和方法. ...

  3. Java基础教程:IDEA单元测试

    Java基础教程:IDEA单元测试 环境配置 使用idea IDE 进行单元测试,首先需要安装JUnit 插件. 安装JUnit插件步骤 File-->settings-->Plguins ...

  4. Java基础教程(12)--深入理解类

    一.方法的返回值   当我们在程序中调用方法时,虚拟机将会跳转到对应的方法中去执行.当以下几种情况发生时,虚拟机将会回到调用方法的语句并继续向下执行: 执行完方法中所有的语句: 遇到return语句: ...

  5. Java基础教程:网络编程

    Java基础教程:网络编程 基础 Socket与ServerSocket Socket又称"套接字",网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个s ...

  6. Java基础教程(5)--变量

    一.变量 1.变量的定义   正如上一篇教程<Java基础教程(4)--面向对象概念>中介绍的那样,对象将它的状态存在域中.但是你可能仍然有一些疑问,例如:命名一个域的规则和惯例是什么?除 ...

  7. Java基础教程:Lambda表达式

    Java基础教程:Lambda表达式 本文部分内容引用自OneAPM:http://blog.oneapm.com/apm-tech/226.html 引入Lambda Java 是一流的面向对象语言 ...

  8. Java基础教程:泛型基础

    Java基础教程:泛型基础 引入泛型 传统编写的限制: 在Java中一般的类和方法,只能使用具体的类型,要么是基本数据类型,要么是自定义类型.如果要编写可以应用于多种类型的代码,这种刻板的限制就会束缚 ...

  9. Java基础教程:多线程基础(1)——基础操作

    Java:多线程基础(1) 实现多线程的两种方式 1.继承Thread类 public class myThread extends Thread { /** * 继承Thread类,重写RUN方法. ...

随机推荐

  1. 关于新塘 M0 M4添加库文件的说明

  2. CALayer的position,anchorPoint属性 与UIView的frame 属性

    彻底理解CALayer的position,anchorPoint属性 与UIView的frame 属性 一.position,anchorPoint两者都是CALayer的属性,都是CGPoint点 ...

  3. IOS深入学习(20)之Object modeling

    1 前言 本节简单的介绍了对象建模,以及需要注意的事项. 2 详述 对象建模是对设计通过一个面向对象应用检测和操作服务的对象或者类的加工.许多模型技术是可能的:Cocoa开发环境不推荐歧义性. 典型地 ...

  4. 【LeetCode】Pascal's Triangle II (杨辉三角)

    Given an index k, return the kth row of the Pascal's triangle. For example, given k = 3, Return [1,3 ...

  5. 腾讯云大数据套件Hermes-MR索引插件使用总结

    版权声明:本文由王亮原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/121 来源:腾云阁 https://www.qclou ...

  6. Chrome浏览器下CSS字体大小设置小于12px无效问题

    当字体大小被设置小于12px时,IE.firefox可以起作用.但chrome中仍然显示为12px大小. 解决方法为: html, body {     -webkit-text-size-adjus ...

  7. Excel 2010 最熟悉的陌生功能:筛选器(将当前所选内容添加到筛选器)

    使用excel2010版的同学,在进行筛选时,肯定都对这句话很熟悉:将当前所选内容添加到筛选器.但很多同学天天看到,却不知道什么是筛选器?它有什么作用. 其实,这里所指的筛选器就是储存筛选结果的一个虚 ...

  8. LightOJ 1348(Aladdin and the Return Journey )

    题目链接:传送门 题目大意:一棵无根树,每个点上有权值,两种操作,0 x y询问x~y路径上权值和 1 x y将 节点 x 权值变为y.对于询问操作输出答案. 题目思路:树链剖分 #include & ...

  9. [黑金原创教程] FPGA那些事儿《设计篇 III》- 图像处理前夕·再续

    简介 一本为入门图像处理的入门书,另外还教你徒手搭建平台(片上系统),内容请看目录. 注意 为了达到最好的实验的结果,请准备以下硬件. AX301开发板, OV7670摄像模块, VGA接口显示器, ...

  10. C# 验证码生成

    后台: //生成验证码 public void CreateImage() { //获取4位验证码,并转成小写. ).ToLower(); //验证码赋值Cookie HttpCookie myCoo ...