注解(一)- 基础知识与运行时注解

在java和android 中,注解的运用非常广泛,很多的类库,第三方框架中都用到了注解。所以我们有必要来熟悉注解的相关知识。

Annotation,注解(也称为元数据),可以为我们在代码中添加额外的信息,我们也可以很方便的使用这些数据

当然,在代码中添加额外信息我们最经常使用的是注释(comment),好的注释对于理解代码或逻辑是非常重要的,comment通俗易懂,并且使用一些工具,注释也可以生成专门的文档,但是注解相比注释,拥有更加强大的,不可替代的功能。它可以提供编译期的一些操作,比如类型检查,生成新的文件(包括java文件等)。

annotation是java5才引入的新特性,它通过将信息和源代码结合在一起,可以提供一些java语言本身无法的表达的额外信息。它的相关内容可以由编译器来测试或者验证,当然,运行时期也可以使用注解来提供额外信息。

注解的语法其实比较简单,除了多个@符号,其他和java本身语法一样。java 5 在java.lang中内置了三种标准注解。(android中内置的注解更多)。

  • @Override: 表示当前方法其实是覆盖父类的方法,如果有拼写错误等,编译器(和IDE)就可以发出错误提示。
  • @Deprecated:表示该类或方法不建议使用了,未来有可能被废弃或者被移除,如果程序员使用了该类或方法,那么编译器(和IDE)将会发出警告信息。 这里也就就是warning而已,你也可以继续使用,不过建议还是不要使用被Deprecated的API,说不定未来哪个版本就被移除了。
  • @SuppressWarnings:给编译器一条指令,告诉它对范围内的某些类型的警告保持静默。这样编译时就不再输出该警告了。

看一个简单的demo:
918357-20170422225803118-292622611.png
从截图看到看到,eclipse中对不同类型的annotation,都做了相应的提示。

并且也可以看到同一个元素上也可以使用多个不同的注解。而SuppressWarnings注解接收的其实是一个数组,例如demo当中的 unchecked,表示是未检查的转换时的警告,而fallthrough则表示在switch块中,某个case没有使用break,而直接流向了下一条case时的警告。从编码习惯上来讲,你屏蔽了fallthrough的警告,也可以告别其他人,case没有使用break,这是因为代码的逻辑,而不是你忘记写了。

自定义注解

从以上demo就可以看出,注解很有用,也方便,但是内置的注解不可能满足我们五花八门的需求,所以此时就需要我们来自定义注解了。
java中内置了四种元注解(meta-annotation),元注解就是负责注解其他注解(这句话好绕啊),来帮助我们自定义注解的。

  • @Target : 表示该注解可以用在什么地方(使用范围),可能的ElementType参数包括:
  • CONSTRUCTOR:构造器的声明。
  • FIELD:域声明(包含enum的实例)。
  • LOCAL_VARIABLE : 局部变量声明。
  • METHOD : 方法声明。
  • PACKAGE: 包声明。
  • PARAMETER: 参数声明。
  • TYPE: 类,接口(包括注解类型)或 enum类型。
  • @Retention: 表示需要在什么级别保存该注解信息(生命周期),可选的RetentionPolicy参数包括:
  • SOURCE:源码级别,注解将被编译器丢弃。
  • CLASS: 在编译器生成的class文件中可用,但是会被VM丢弃。(默认的就是该级别)。
  • RUNTIME: 运行期保留该注解,所以此时可以通过反射机制来读取注解的信息。
  • @Documented:将注解包含在javadoc中。
  • @Inherited:允许子类继承父类的注解。

具体的关于如何定义自己的注解,几行简单的代码胜过千言万语。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {public  int id();public String description() default "no description";
}
public class PasswordUtils {@UseCase(id = 47, description = "password must contain one number")public boolean validatePassword(String password){return true;}@UseCase(id = 48)public String encryptPassword(String password){return "encryptPassword()";}@UseCase(id = 49, description = "new password can't equal previously used ones")public boolean checkouForNewPassword(){return false;}
}

通过代码可以看出,定义一个注解和定义一个interface非常相似,只不过多了一个@符号罢了。在定义注解时,使用了一些元注解,比如@Target或者 @Retention,它们分别用来表明注解的应用范围,生命周期情况。

而从语法的角度来看,注解的使用方法,也和public,static或void等修饰符一样,没什么大的差别。

而定义注解时,可以看到定义体里面包含了一些比较特殊的方法。我们也称之为配置参数,这些方法只能是public或者default访问权限,方法的返回值就是配置参数的类型,并且我们可以为其指定默认值,我们在分析处理注解时,程序或工具就可以利用这些值。

@UseCase由UseCase.java定义,并且其中包含了两个配置参数id和description,并且它们都是有类型的,配置参数可以使用的类型如下:

  • 所有的基本类型。(int,float,boolean等)。
  • String
  • Class
  • enum
  • Annotation
  • 以上类型的数组

关于注解元素的问题,我们还需要注意如下几个方面。

  1. 配置参数本身还可以是一个注解,这就说明注解可以嵌套。
  2. 配置参数不能有不确定的值,也就是说,配置参数要么(在定义时)有默认值,要么(在使用时)提供相应的值。并且这个值还不能是null。所以这个是比较尴尬的地方,因此比如String类型的元素,我们不能赋值为null,那么习惯用法就是使用 "";
  3. 我们在使用注解时,采用的是 键值对这种语法,(id = 48),有一个快捷方式,就是注解中如果定义了名为value的配置参数,那么在使用时,如果该参数是唯一需要赋值的一个参数,那么可以不使用键值对,直接在括号内给出value的值就可以了。(value可以是任何合法类型的参数)。
  4. 注解可以嵌套,但是不可以继承。而实际上,当你使用@来定义注解时,默认继承的是java.lang.annotation.Annotation。

而对于上面我们那个 UseCase的demo,我们写一个程序来进行处理。

public class UseCaseTracker {public static void main(String[] args) {// TODO Auto-generated method stubList<Integer> useCases = new ArrayList<Integer>();Collections.addAll(useCases, 47, 48, 49, 50);trackUseCases(useCases, PasswordUtils.class);}public static void trackUseCases(List<Integer> useCases, Class<?> cl) {for (Method m : cl.getDeclaredMethods()){// getDeclaredAnnotation  返回指定类型的 注解对象 UseCase uc = m.getDeclaredAnnotation(UseCase.class);if (uc != null){System.out.println("Found UseCase :" + uc.id() + "\t" + uc.description());useCases.remove(new Integer(uc.id()));}}System.out.println("--------------------------------------");for (int i : useCases){System.out.println("Warning : Miss use case --" + i);}} }

输出结果为:

Found UseCase :47   password must contain one number
Found UseCase :48   no description
Found UseCase :49   new password can't equal previously used ones
--------------------------------------
Warning : Miss use case --50

通过上面代码我们可以看出,利用反射,我们可以很好的处理注解,这其实就像处理普通类那样。

注解的基本知识本身不复杂,那么下面我们看一个复杂点的例子。在android常用的xutils3框架中,包含了一个数据库模块,这个数据库在使用时,就是通过注解来创造表名或列名的,那么下面,我们也自定义一个注解,通过注解来生成javaBean对象的创建数据库表的语句。

//告诉注解处理器,需要生成一个数据库表
//这个注解只能用于类,接口,enum
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {public String name() default "";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraints {//这些元素都有默认值,这样我们就不必强迫程序员必须赋值了boolean primaryKey() default false;boolean allowNull() default true;boolean unique() default false;
}
/*** @author www.yaoxiaowen.com*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString {//定义了元素名为value,在符合条件时,我们使用时,可以直接在括号内输入value的值就ok了。int value() default 0;String name() default "";Constraints constraints() default @Constraints;
}
/***  SQLInteger 和 SQLString一样,都是要求在javabean上,根据不同的数据类型使用不同的注解*  @author www.yaoxiaowen.com*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLInteger {String name() default "";//注解嵌套Constraints constraints() default @Constraints;
}
/*** 我们的目的就是为该javabean生成一个创建表的语句* @author www.yaoxiaowem.com*/
@DBTable(name = "MEMBER")
public class Member {@SQLString(30)String firstName;@SQLString(50)String lastName;@SQLIntegerInteger age;@SQLString(value = 30,constraints = @Constraints(primaryKey = true))String handle;static int memberCount;}
public class TableCreator {public static void main(String[] args) throws Exception{StringBuilder createCommand = new StringBuilder();String className = "test.annotation.database.Member";Class<?> cl = Class.forName(className);DBTable dbTable = cl.getAnnotation(DBTable.class);String tableName = dbTable.name();//如果名字为空,就使用类名if (tableName.length() < 1){tableName = cl.getName().toUpperCase();}createCommand.append("CREATE TABLE " + tableName + "(");List<String> columnDefs = new ArrayList<String>();for (Field field : cl.getDeclaredFields()){String columnName = null;Annotation[] anns = field.getDeclaredAnnotations();if (anns.length < 1){continue;}//这里的写法之所以简单,因为我们每个Field上面最多只有一个 注解if (anns[0] instanceof SQLInteger){SQLInteger sInt = (SQLInteger)anns[0];//没有名字的话,我们就使用Field的名字来做为 列名if (sInt.name().length() < 1){columnName = field.getName();}else {columnName = sInt.name();}columnDefs.add(columnName + " INT " + getConstraints(sInt.constraints()));}if (anns[0] instanceof SQLString){SQLString sString = (SQLString)anns[0];if (sString.name().length() < 1){columnName = field.getName();}else {columnName = sString.name();}columnDefs.add(columnName + " VARCHAR(" + sString.value() + ")" + getConstraints(sString.constraints()));}}for (String columnDef : columnDefs){createCommand.append("\n\t" + columnDef + ",");}//移除最后的一个逗号String tableCreate = createCommand.substring(0, createCommand.length()-1) + ");";System.out.println("TABLE Creation SQL for " + className + "  is: \n" + tableCreate);}//解析出 Constraints 注解的内容private static String getConstraints(Constraints con){String constraints = "";if (!con.allowNull()){constraints += " Not Null";}if (con.primaryKey()){constraints += " PRIMARY KEY ";}if (con.unique()){constraints += " NNIQUE ";}return constraints;}
}

输出结果为:

TABLE Creation SQL for test.annotation.database.Member  is: 
CREATE TABLE MEMBER(firstName VARCHAR(30),lastName VARCHAR(50),age INT ,handle VARCHAR(30) PRIMARY KEY );

这个demo虽然没有实际的意义,但是仔细分析该demo对于我们理解注解还是比较有帮助的。

在java 5当中引入 annotation时,java也引入了注解处理工具Annotation Processing Tool (apt),apt是一个可以在编译时使用的命令行工具,但是它是Oracle提供的私有实现,所以该工具在java 8中被移除了,而在java6中,通过 JSR 269 annotation processing facility来规范了自定义注解处理器的这一功能。也有了新的API(javax.annotation.processing),而关于这些内容,我们下一个篇文章再进行介绍。


作者: www.yaoxiaowen.com

github: https://github.com/yaowen369

欢迎对于本人的博客内容批评指点,如果问题,可评论或邮件(yaowen369@gmail.com)联系

欢迎转载,转载请注明出处.谢谢

转载于:https://www.cnblogs.com/yaoxiaowen/p/6750192.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/455908.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Python基础入门系列】第02天:Python 基础语法

Python 语言与 Perl&#xff0c;C 和 Java 等语言有许多相似之处。但是&#xff0c;也存在一些差异。在本章中我们将来学习 Python 的基础语法&#xff0c;让你快速学会Python 编程。 开始你的第一个 Python 程序 Python 标识符 在 Python 里&#xff0c;标识符由字母、数字、…

如何理解操作系统的不确定性_温度最低-273度,最高却能有1.4亿亿亿亿度,如何定义的?...

地球绕太阳公转&#xff0c;太阳直射点在南北回归线之间往复的移动&#xff0c;也让地球上出现了一年四季的变化冷热交替&#xff0c;对于温度我们有最直观的感受冷暖自知&#xff0c;但仅限于很小的温度范围&#xff0c;在中国东北地区冬天最低温度可以低于零下30摄氏度&#…

不再单打独斗?中国移动联合多企业组建医疗数据公司

中国移动 6月20日消息&#xff0c;中国移动通信集团公司与浪潮集团有限公司作为发起方&#xff0c;共同组建中国健康医疗大数据股份有限公司&#xff0c;在中国移动总部举行投资意向签约仪式。 国家卫生和计划生育委员会副主任金小桃,国务院国有资产监督管理委员会副主任徐福顺…

【Python基础入门系列】第03天:Python 变量与数据类型

这篇文章我们学习 Python 变量与数据类型 Python 变量类型 变量存储在内存中的值。这就意味着在创建变量时会在内存中开辟一个空间。 基于变量的数据类型&#xff0c;解释器会分配指定内存&#xff0c;并决定什么数据可以被存储在内存中。 因此&#xff0c;变量可以指定不同…

20169210 2016-2017-2《网络攻防实践》第八周总结

教材 一、Linux操作系统基本矿建概述 1、Linux操作系统发展与现状 跨平台的硬件支持&#xff1b;丰富的软件支持&#xff1b;多用户多任务&#xff1b;可靠的安全性&#xff1b;良好的稳定性&#xff1b;完善的网络功能2、Linux系统结构 1&#xff09;Linux进程与线程管理机制2…

cad卸载_想重新安装CAD提示已经安装?不会卸载?进来教你卸载CAD

大家好&#xff0c;我是【小杰趣分享】&#xff0c;这里每天都会分享一下和电脑软件、电脑硬件相关的文章或视频。这一期小杰教大家怎么彻底卸载CAD&#xff01;CAD这款软件是小杰见过最难卸载的软件了&#xff0c;不知道你们有没有遇到过卸载CAD想重新安装&#xff0c;却怎么都…

直击3.15 安防行业如何维护消费者权益

一年一度的315维权活动让众多行业为之惶恐&#xff0c;安防行业发展至今&#xff0c;和各行各业一样也同样面临着安防市场粗制滥造、假冒伪劣产品盛行的局面。 315今年的消费维权主题是 “网络诚信 消费无忧”&#xff0c;中国消费者协会副秘书长董祝礼表示&#xff0c;网络消费…

【Python基础入门系列】第04天:Python 流程控制

在编程的世界中&#xff0c;流程控制是程序员运行的基础&#xff0c;流程控制决定了程序按照什么样的方式去执行&#xff0c;本节给大家介绍 Python 流程控制相关语法。 if 语句 if 语句表示如何发生什么样的条件&#xff0c;执行什么样的逻辑。 Python程序语言指定任何非0和…

excel合并两列内容_还在为合并WPS表格(Excel)中两列内容而犯愁?此方法简单高效...

我们在处理WPS表格(Excel)数据时&#xff0c;时常需要将两列甚至更多列的内容合并显示在同一列中&#xff0c;就像这样&#xff1a;这个时候大家是怎么解决的呢&#xff1f;路人&#xff1a;复制粘贴So easy&#xff01;小杜&#xff1a;不止两三行啊……路人&#xff1a;复制粘…

【Python基础入门系列】第05天:Python函数

前面我们写过九九乘法表&#xff0c;但如果我要七七乘法表或五五乘法表的话&#xff0c;你会看到三者代码极其类似&#xff0c;只是循环变量不同&#xff0c;那么如何做到代码重用&#xff0c;而不是简单拷贝黏贴修改呢&#xff0c;其实可是使用函数完成这一功能! 先来试着看一…

第十五届北京师范大学程序设计竞赛决赛(网络同步赛) B lca水 D 思维,找规律...

第十五届北京师范大学程序设计竞赛决赛&#xff08;网络同步赛&#xff09; B. Borrow Classroom 题意&#xff1a;一棵树&#xff0c;点 1为根&#xff0c;一个人从点 b到 点 c再到点 1&#xff0c;第二个人从点 a出发&#xff0c;问第二个人能否截住第一个人。 tags&#xff…

macbook所有型号大全_苹果笔记本型号大全

很多朋友在选购苹果笔记本也就是MacBook的时候都会考虑究竟买哪一个系列会比较好&#xff0c;下面就为大家介绍一下苹果笔记本型号大全都有什么&#xff0c;希望以下的介绍能够帮助到您。苹果笔记本型号大全目前苹果笔记本有以下的几个主要的型号&#xff1a;1、MacBook Air是目…

【Python基础入门系列】第06天:Python 模块和包

在计算机程序的开发过程中&#xff0c;随着程序代码越写越多&#xff0c;在一个文件里代码就会越来越长&#xff0c;越来越不容易维护。 为了编写可维护的代码&#xff0c;我们把很多函数分组&#xff0c;分别放到不同的文件里&#xff0c;这样&#xff0c;每个文件包含的代码…

空间直线与平面的交点

这内容属于计算几何&#xff0c;在 3D游戏开发编程基础 或者在游戏开发中的数学和物理算法 这种资料上也可以找到相关的内容和代码。或者更广泛点称为是计算机图形学&#xff0c; 接下来我们进入正题&#xff0c;如果直线不与平面平行&#xff0c;将存在交点。如下图所示&#…

iphone导出视频 无法连接到设备_拷贝iphone照片,显示无法连接设备?TRIZ 3秒钟解决...

手机存储满了&#xff0c;想把手机里面的照片和视频拷贝出来。 又不想交给苹果cloud的“苹果税”。USB手动连上IPHONE&#xff0c;结果每次复制了几百兆&#xff0c;就会弹出“无法连接设备”&#xff0c;导致拷贝失败。并且每次重新连接之后&#xff0c;删掉的照片又出现在手机…

【Python基础入门系列】第07天:Python 数据结构--序列

python内置序列类型最常见的是列表&#xff0c;元组和字符串。&#xff08;序列是python中最基础的数据结构&#xff0c;而数据结构是计算机存储&#xff0c;组织数据的方式。&#xff09; 另外还提供了字典和集合的数据结构&#xff0c;但他们属于无顺序的数据集合体&#xf…

springBoot(20):使用Spring Session实现集群-redis

一、session集群的解决方案1.1、扩展指定server利用Servlet容器提供的插件功能&#xff0c;自定义HttpSession的创建和管理策略&#xff0c;并通过配置的方式替换掉默认的策略。缺点&#xff1a;耦合Tomcat/Jetty等Servlet容器&#xff0c;不能随意更换容器。1.2、利用Filter利…

docker desktop ubuntu镜像_原创 | Docker入门,看了不理解,假一赔命

写在前面这篇博客适合谁&#xff1f;对于Docker并不了解&#xff0c;只是有一点模糊的感觉&#xff0c;觉得Docker可以当成虚拟机用之类的只是下载了Docker软件&#xff0c;对于怎么配置&#xff0c;怎么玩&#xff0c;第一步干什么&#xff0c;完全一无所知其二&#xff0c;我…

【Python基础入门系列】第08天:Python List

Python内置的一种数据类型是列表&#xff1a;list。list是一种有序的集合&#xff0c;可以随时添加和删除其中的元素。 LIST 列表 比如&#xff0c;列出班里所有同学的名字&#xff0c;就可以用一个list表示&#xff1a; >>> classmates [liuwang, xuezhang, al…

【Python基础入门系列】第09天:Python tuple

Python 中的数据结构是通过某种方式组织在一起的数据元素的集合&#xff0c;这些数据元素可以是数字、字符、甚至可以是其他数据结构 在 Python 中&#xff0c;最基本的数据结构是序列&#xff08;在前面文章我们也说过序列&#xff09;&#xff0c;序列中的每个元素都有一个序…