什么是注解

  • 注解可以看作类的第6大要素(成员变量、构造器、方法、代码块、内部类)
  • 注解有点像修饰符,可以修饰一些程序要素:类、接口、变量、方法、局部变量等等
  • 注解要和对应的配套工具(APT:Annotation Processing Tool)一起使用,APT会对含有注解进行一些处理
  • 比如API文档里面,有些方法下边的“@Deprecated”,就是一个注解,它表示这个方法已经过时,使用的时候会收到警告
  • 注解完全不影响程序的功能

元注解

  • 元注解有6个,位于java.lang.annotation包下
  • @Retention
    • 只能修饰注解
    • 用于指定被修饰的注解,可以保留到什么时间
    • @Retention只有一个元素:value,是RetentionPolicy类型,这是个枚举类,只有如下三个值
      • RetentionPolicy.SOURCE:注解只保留在源代码中,编译阶段即被丢弃
      • RetentionPolicy.CLASS:保留在class文件中,程序运行时,JVM不能获取到该注解
      • RetentionPolicy.RUNTIME:保留在class文件中,程序运行时,可以通过反射获得该注解
  • @Target
    • 只能修饰注解
    • 用于指定被修饰的注解,可以用于修饰哪些程序元素
    • @Target只有一个元素:value,是ElementType[]类型,ElementType也是个枚举类,只有如下十个值
      • ElementType.TYPE:表示被修饰的注解可以修饰类、接口、注解、枚举
      • ElementType.FIELD:只能修饰成员变量
      • ElementType.METHOD:只能修饰方法
      • ElementType.PARAMETER:可以修饰参数
      • ElementType.CONSTRUCTOR:只能修饰构造器
      • ElementType.LOCAL_VARIBLE:只能修饰局部变量
      • ElementType.ANNOTATION_TYPE:只能修饰注解
      • ElementType.PACKAGE:只能修饰包
      • ElementType.TYPE_USE:since 1.8,表示注解能写在使用类型变量的语句中
      • ElementType.TYPE_PARAMETER:since 1.8,表示注解能写在类型变量的声明语句中
  • @Documented
    • 修饰注解,该注解将被javadoc提取进入文档
    • 该元注解没有元素
  • @Inherited
    • 修饰注解,表示该注解具有继承性
    • 比如:一个父类使用了一个注解X修饰,而X注解在定义时用了@Inherited修饰,那么子类自动被X修饰
  • @Repeatable
    • 用于修饰一个注解,表示该注解可以用于重复注解
    • 包含一个变量value,是Annotation的Class类型
  • @Native

基本注解

  • 基本注解有5个,位于java.lang包下
  • @Override
    • 只能修饰方法;保留在源代码阶段
    • 告诉编译器,被该修饰符修饰的方法是在重写父类的方法,如果父类中没有相同签名的方法,那么会发生错误
  • @Deprecated
    • 修饰构造器、成员变量、局部变量、方法、包、参数、类、注释、枚举;保留到运行阶段;javadoc提取
    • 表示该元素已过时,其他地方使用这些元素时,编译器会给出警告
  • @SuppressWarnings
    • 修饰类、接口、注解、枚举、成员变量、方法、参数、构造方法、局部变量;保留在源代码阶段;
    • 只有一个变量value,类型为String[]
    • 被修饰的程序元素及子元素取消value指定的编译器警告
  • @SafeVarargs
    • 修饰构造器、方法;保留在运行阶段;javadoc提取
    • 专门修饰会引发“堆污染”警告的方法或者构造器
    • since 1.7
  • @FunctionalInterface
    • 只能修饰接口;保留到运行阶段;javadoc提取
    • 告诉编译器严格检查,该接口为函数式接口
    • since 1.8

自定义注解

  • 自定义注解跟定义接口类似,只是用“@interface”关键字替换“interface”
  • 可以用一些元注解修饰即可,对于保留时间,如果不写@Target,那么默认是修饰任何程序要素
  • 可以包含变量
  • 比如@Deprecated的源代码是这样的
  1. package java.lang;
  2. import java.lang.annotation.*;
  3. import static java.lang.annotation.ElementType.*;
  4. @Documented //元注解:表示该注解会被javadoc提取到文档中
  5. @Retention(RetentionPolicy.RUNTIME) //元注解:保留到运行阶段
  6. @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) //元注解:可以修饰的程序元素
  7. public @interface Deprecated {
  8. }
  • 再比如@SuppressWarnings的源代码
  1. package java.lang;
  2. import java.lang.annotation.*;
  3. import static java.lang.annotation.ElementType.*;
  4. @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
  5. @Retention(RetentionPolicy.SOURCE)
  6. public @interface SuppressWarnings {
  7. String[] value(); //可以包含变量,String[]是该变量的类型
  8. }
  • 包含变量的时候,使用时就不能只写注解名称,还得写变量的值,比如:
  1. @SuppressWarnings(value="unchecked")
  2. //如果只有一个变量,那么可以省略变量名:
  3. @SuppressWarnings("unchecked")
  • 不写变量名和值,那么该注解在定义的时候,就得定义默认值,用default关键字,比如
  1. @Target({TYPE, FIELD}) //只有一个变量,省略变量名
  2. @Retention(RetentionPolicy.CLASS) //只有一个变量,省略变量名
  3. public @interface Test{
  4. String name() default "Java"; //用default指定默认值
  5. int age() default 18; //用default指定默认值
  6. }
  • 实际上,自定义注解默认都是继承了java.lang.annotation.Annotation接口的,见示例:
  1. package testpack;
  2. public class Test1 {
  3. public static void main(String[] args){
  4. Class clazz=Test.class;
  5. Class[] is=clazz.getInterfaces();
  6. for (Class i:is) {
  7. System.out.println(i); //输出:interface java.lang.annotation.Annotation
  8. }
  9. }
  10. }
  11. @interface Test{
  12. }

注解的分类

  • 标记注解:不包含成员变量
  • 元数据注解:包含成员变量

获取程序元素的注解信息

  • 光有注解没用,还得有对应的工具来处理,要处理,得先获取注解信息
  • 获取到注解信息的前提是,注解能保留到"获取"这个操作的时候
  • 在文章0033 Java学习笔记-反射-初步1中提到,通过反射可以获取一个类的注解信息,有六个方法,其实这些方法都来自于一个接口:java.lang.reflect.AnnotatedElement,Class是它的实现类之一
  • java.lang.reflect.AnnotatedElement是个接口,表示程序中可以接受注解的程序元素,主要有如下几个实现类
    • Package
    • Class
    • Filed
    • Constructor
    • Method
    • Parameter
  • 以上这些类都包含0033 Java学习笔记-反射-初步1中关于获取注解的那6个方法,
  • 除以上6个方法外,还有一个方法用于判断程序元素上是否存在指定类型的注解:boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
  • 见示例:
  1. package testpack;
  2. import java.lang.annotation.Annotation;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. @Test(name="Java",age=18)
  6. public class Test1 {
  7. public static void main(String[] args){
  8. Class clazz=Test1.class;
  9. Annotation a=clazz.getAnnotation(Test.class); //获取对应类的Test类型的注解
  10. System.out.println(a);
  11. if (a instanceof Test){
  12. System.out.println(((Test)a).name()); //强制类型转型后获取name的值
  13. System.out.println(((Test)a).age()); //强制类型转型后获取age的值
  14. }
  15. }
  16. }
  17. @Retention(RetentionPolicy.RUNTIME) //保留到运行时,这条很重要
  18. @interface Test{
  19. public String name(); //定义两个变量
  20. public int age();
  21. }

重复注解

  • 重复注解是Java8新增的功能
  • 重复注解是值:用多个相同类型的注解修饰同一个程序元素
  • 使用重复注解要分3步走:
    • 定义一个注解,并用@Repeatable修饰
    • 再定义一个该注解的容器注解
    • 使用重复注解
  • 示例:
  1. package testpack;
  2. import java.lang.annotation.Annotation;
  3. import java.lang.annotation.Repeatable;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. @Test(name="C++",age=30)
  7. @Test(name="Java",age=18) //使用了两个相同类型的注解
  8. public class Test1 {
  9. public static void main(String[] args){
  10. Class clazz=Test1.class;
  11. Annotation[] as=clazz.getAnnotations();
  12. for (Annotation a:as){
  13. System.out.println(a);
  14. //输出:@testpack.Tests(value=[@testpack.Test(name=C++, age=30), @testpack.Test(name=Java, age=18)])
  15. //其实还是只有一个注解,就是Tests这个容器注解
  16. }
  17. }
  18. }
  19. @Repeatable(Tests.class) //用@Repeatable修饰
  20. @Retention(RetentionPolicy.RUNTIME)
  21. @interface Test{ //定义Test注解
  22. public String name();
  23. public int age();
  24. }
  25. @Retention(RetentionPolicy.RUNTIME) //容器注解的保留时间不能早于其对应的注解
  26. @interface Tests{ //定义Test的容器注解Tests
  27. Test[] value(); //容器注解的变量
  28. }

类型注解

  • 类型注解也是Java8新增功能
  • Java8以前的注解只能用在一些特定的程序元素上,
  • ElementType.TYPE_PARAMETER:表示注解能写在类型变量的声明语句中
  • ElementType.TYPE_USE:表示注解能写在使用类型变量的语句中
  • 对该部分内容没什么理解,更详细的内容见:Java 8的类型注解:工具和机会

APT工具

  • APT(Annotation Processing Tool):一种注解处理工具,用于对源代码文件进行检测,找出特定的注解信息,然后进行特别的处理
  • 比如可以在编译源代码的同时,生成一些附属的描述文件,
  • 在用javac.exe编译源代码时,可以用“-processor”选项指定注解处理器
  • 注解处理器一般实现javax.annotation.processing.Processor接口或者继承AbstractProcessor抽象类
  • 一个注解处理器,可以处理一个或多个注解

其他

0035 Java学习笔记-注解的更多相关文章

  1. Java学习笔记--注解和反射

    注解和反射 1. 注解 注解作用: 对程序做出解释 被其他程序读取 注解格式: @注释名,还可以添加一些参数值,例如@SuppressWarnings(value="unchecked&qu ...

  2. Java学习笔记--注解

    注解的使用与实例:http://www.cnblogs.com/pepcod/archive/2013/02/16/2913474.html 注解的作用及使用方法:http://wenku.baidu ...

  3. 0028 Java学习笔记-面向对象-Lambda表达式

    匿名内部类与Lambda表达式示例 下面代码来源于:0027 Java学习笔记-面向对象-(非静态.静态.局部.匿名)内部类 package testpack; public class Test1{ ...

  4. 《Java学习笔记(第8版)》学习指导

    <Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...

  5. Java学习:注解,反射,动态编译

    狂神声明 : 文章均为自己的学习笔记 , 转载一定注明出处 ; 编辑不易 , 防君子不防小人~共勉 ! Java学习:注解,反射,动态编译 Annotation 注解  什么是注解 ? Annotat ...

  6. Java学习笔记之---Servlet

    Java学习笔记之---Servlet (一)如何实现Servlet 1.实现javax.servlet.Servlet接口: 2.继承javax.servlet.GenericServlet类: 3 ...

  7. java学习笔记之基础篇

    java选择语句之switch   //switch可以用于等值判断 switch (e) //int ,或则可以自动转化成int 的类型,(byte char short)枚举jdk 7中可以防止字 ...

  8. 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁

    什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...

  9. Java学习笔记(04)

    Java学习笔记(04) 如有不对或不足的地方,请给出建议,谢谢! 一.对象 面向对象的核心:找合适的对象做合适的事情 面向对象的编程思想:尽可能的用计算机语言来描述现实生活中的事物 面向对象:侧重于 ...

随机推荐

  1. Android-Drawable、Bitmap、byte[]、资源文件相互转换

    我们在Android的开发中,经常可以遇到图片的处理,当中,有很多是 Bitmap.Drawable.byte[]和资源文件它们直接相互转换. 今天就此总结一下: 1.资源文件转为Drawable 2 ...

  2. 工作笔记--哪些bug应由研发发现?

      标准: 研发应发现: 主功能流程无法正常使用,以及联调时主功能流程是否正常 功能缺失 打包时数据库表非最新.程序文件非最新: 文件导出时有明显错误(如无法导出.导出后格式明显不对.批量导入出错) ...

  3. vue.js 开发生态总结

    ---title: Vue 1.0 的技术栈date: 2016-09-26 00:48:50tags:category:--- ## vuejs概述 Vue.js是用于构建交互式的Web界面的库.它 ...

  4. ITTC数据挖掘平台介绍(五) 数据导入导出向导和报告生成

    一. 前言 经过了一个多月的努力,软件系统又添加了不少新功能.这些功能包括非常实用的数据导入导出,对触摸进行优化的画布和画笔工具,以及对一些智能分析的报告生成模块等.进一步加强了平台系统级的功能. 马 ...

  5. EntityFramework.Extended 实现 update count+=1

    在使用 EF 的时候,EntityFramework.Extended 的作用:使IQueryable<T>转换为update table set ...,这样使我们在修改实体对象的时候, ...

  6. SparkStreaming实现Exactly-Once语义

    作者:Syn良子 出处:http://www.cnblogs.com/cssdongl 转载请注明出处 译自:http://blog.cloudera.com/blog/2015/03/exactly ...

  7. Node基础篇(模块和NPM)

    核心模块 核心模块的意义 如果只是在服务器运行JavaScript代码,意义并不大,因为无法实现任何功能(读写文件,访问网络). Node 的用处在于它本身还提供的一系列功能模块,用于与操作系统互动. ...

  8. new/delete重载

    在c++中,有时我们需要在运行阶段为一个变量分配未命名的内存,并使用指针来访问它,这里就可以用到new关键字.另外需要指出的是,new分配的内存块通常与常规变量分配的内存块不同,常规变量的值都储存在被 ...

  9. TeamCity : Build 版本控制系统配置

    VCS (版本控制系统) 是用来跟踪项目源文件版本变化的系统.它还有其它的名字,比如 SCM(源代码管理).当前 TeamCity 内置支持的 VCS 类型有:Git, Subversion, Mer ...

  10. Windows 10 安装 Sql Server 2014 反复提示需要安装 .NET Framework 3.5 SP1 的解决方案

    一.首先安装.NET Framework 3.5: 离线安装方式: 1.装载相对应的系统安装盘,我是Windows 10 x64 企业版,所以装载Windows 10 x64 企业版安装镜像ISO,盘 ...