Java编程思想学习(十五) 注解

时间:2022-09-02 10:50:27

注解Annotation又叫元数据,是JDK5中引入的一种以通用格式为程序提供配置信息的方式。使用注解Annotation可以使元数据写在程序源码中,使得代码看起来简洁,同时编译器也提供了对注解Annotation的类型检查,使得在编译期间就可以排除语法错误。

1JDK内置的3中Annotation:

在JDK5中,内置了3个通用目的的注解Annotation,这三个内置的注解在java.lang包下:

(1).@Override

这个注解常用在继承类或实现接口的子类方法上,表面该方法是子类覆盖父类的方法,该方法的方法签名要遵循覆盖方法的原则:即访问控制权限必能比父类更严格,不能比父类抛出更多的异常。

(2).@Deprecated

这个注解告诉编译器该元素是过时的,即在目前的JDK版本中已经有新的元素代替该元素。

(3).@SuppressWarnings

该注解关闭编译器中不合适的警告,即强行压制编译器的警告提示。

2.注解Annotation的定义:

注解Annotation的定义和接口类似,实际上,编译器也把注解Annotationn像接口一样编译成class字节码文件。例如:

 import java.lang.annotation.*;  

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

像这种没有任何元素的空注解Annotation叫做标记Annotation.

在声明注解的时候往往需要使用@Target,@Retention等注解,这种注解被称为注解的注解(元数据注解),即是专门用于处理注解Annotation本身的。

(1).@Target注解:

用于指示注解所应用的目标程序元素种类,该注解常和ElementType枚举类型一起联合使用,ElementType枚举提供了java程序中声明的元素类型如下:

  1. ANNOTATION_TYPE:注释类型声明。
  2. CONSTRUCTOR:构造方法声明。
  3. FIELD:字段声明(包括枚举常量)。
  4. LOCAL_VARIABLE:局部变量声明。
  5. METHOD:方法声明。
  6. PACKAGE:包声明。
  7. PARAMETER:参数声明。
  8. TYPE::类,接口或枚举声明。

(2).@Retention注解:

该注解用于指示所定义的注解类型的注释在程序声明周期中得保留范围,该注解常和RetentionPolicy枚举联合使用。RetentionPolicy枚举常量定义了注解在代码中的保留策略:

  1. CLASS:编译器把注解记录在类文件中,但在运行时JVM不需要保留注解。
  2. RUNTIME:编译器把注解记录在类文件中,在运行时JVM将保留注解,因此可以通过反射机制读取注解。
  3. SOURCE:仅保留在源码中,编译器在编译时就要丢弃掉该注解。

3.创建和处理自定义注解Annotation:

正常使用注解时,需要在注解中定义元素,用于接收程序设置的值,正常定义注解的例子如下:

 import java.lang.annotation.*;  

 @Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase{
public int id();
public String description() default “no description”;
}

正常定义注解Annotation的方式类似定义接口,id和description是注解UseCase的属性,而不是方法,注解中不能定义方法只能定义属性。其中description属性有默认的值“no description“,即在使用时如果没有指定description的值,则程序使用其默认值。

上面UseCase注解用于跟踪方法的测试用例说明,使用上面注解的例子如下:

 import java.util.*;  

 public class PasswordUtils{
@UseCase(id = 47, description = “Passwords must contain at least one numeric”)
public Boolean validatePassword(String password){
return (password.mathes(“\\w*\\d\\w*”));
}
@UseCase(id = 48)
public String encryptPassword(Srring password){
return new StringBuilder(password).reverse().toString();
}
@UseCase(id = 49, description = “New passwords can’t equal previously used ones”)
public Boolean checkForNewPassword(List<String> prevPasswords, String password){
return !prevPasswords.contains(password);
}
}

JDK5中提供了Annotation相关的API,结合使用java的反射机制可以实现自定义的Annotation注解处理器(JDK中也提供了使用APT,Annotationprocess tool方式处理注解,在后面会讲解),处理上述Annotation的例子如下:

 import java.lang.reflect.*;
import java.util.*; public class UseCaseTracker{
public static void traceUseCases(List<Integer> useCases, Class<?> clazz){
//获取指定类中所有声明的方法
for(Method m : clazz.getDeclaredMethods()){
//获取方法上指定类型的注解
UseCase uc = m.getAnnotation(UseCase.class);
if(uc != null){
System.out.println(“Found Use Case:” + uc.id() + “ ” + uc.description());
useCases.remove(new Integer(uc.id()));
}
}
for(int i : useCases){
System.out.println(“Warning: Missing use case-” + i);
}
}
public static void main(String[] args){
List<Integer> useCases = new ArrayLis<Integer>();
Collections.addAll(useCases, 47, 48, 49, 50);
trackUseCases(useCases, PasswordUtils.class);
}
}

输出结果:

Found Use Case:47 Passwords must contain at least onenumeric

Found Use Case:48 no description

Found Use Case:49 New Passwords can’t equal previously usedones

Warning: Missing use case-50

注意:使用反射获取到注解对象之后,类似使用调用方法的方式获取注解的值,如uc.id()等。另外,注解不支持继承,因此声明注解时不能使用extends和implements关键字。

4.Annotation注解元素:

Annotation注解中的元素只能是下面的数据类型:

(1).java的8中基本类型,如int, boolean等等,如果可以自动装箱和拆箱,则可以使用对应的对象包装类型。

(2).String类型。

(3).Class类型。

(4).Enums类型。

(5).Annotation类型。

(6).上面类型的数组。

除了上面这些类型以外,如果在注解中定义其他类型的数据,编译器将会报错。

注意:注解中的元素要么指定默认值,要么由使用的类赋值,如果即没有默认值,使用类也没有赋值的话,注解元素是不会像普通类成员变量一样给定默认值,即必须赋值或者显示指定默认值。默认值例子如下:

 import java.lang.annotation.*;  

 @Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DefaultValue{
public int id() default -1;
public String description() default “”;
}

5.一个使用Annotation实现ORM的例子:

从EJB3之后,EJB的实体Bean持久化技术被单独抽取出来形成了JPA技术,JPA和Hibernate3之后都支持使用Annotation注解方式进行对象和关系型数据库映射(ObjectRelationship Mapping, ORM),下面使用Annotation注解方式实现一个简单的ORM功能:

(1).相关注解:

 import java.lang.annotation.*;  

 @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;
} @Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString{//String类型数据
int value() default 0;
String name() default “”;
Constraints constraints() default @Constraints;//注解的属性元素也是注解
} @Target(ElementType.FIELD)

26.@Retention(RetentionPolicy.RUNTIME)
  27.public @interface SQLInteger{//int类型数据
  28. String name() default “”;
  29. Constraints constraints() default @Constraints;
  30.}

(2).使用Annotation注解的实体类:

01.@DBTable(name=”MEMBER”)
02.public class Member{
03. @SQLString(30)//当只指定一个属性的值,并且该属性名为value时,不用写属性名称
04. String firstName;
05. @SQLString(50)
06. String lastName;
07. @SQLInteger
08. Integer age;
09. @String(value=30, constraints=@Constraints(primaryKey = true))
10. String handle;
11. static int memberCount;
12.}

注意:当为注解多于1个以上的属性指定值时,即使有value属性也要写value的属性名称。

(3).实现自定义注解处理器:

01.import java.lang.annotation.*;
02.import java.lang.reflect.*;
03.import java.util.*;
04.
05.public class TableCreator{
06. public static void mian(String[] args)throws Exception{
07. Member m = new Member();
08. Class clazz = m.getClass();
09. String tableName = null;
10. DBTable dbTable = class.getAnnotation(DBTable.class);
11. if(db != null){
12. tableName = db.name();
13.}
14.If(tableName == null){//如果没有指定Table名称,使用类名
15. tableName = clazz.getName().toUpperCase;
16.}
17.List<String> columnDefs = new ArrayList<String>();
18.//构造SQL创建列语句
19.for(Field f : clazz.getDeclaredFields()){//遍历类中声明的所有字段
20. String columnName = null;
21. //获取当前字段上声明的所有注解
22. Annotationn[] anns = f.getDeclaredAnnotationns();
23. if(anns.length() < 1){//当前字段上没有声明注解
24. continue;
25.}
26.if(anns[0] instanceof SQLInteger){//注解数组第一个定义的是数据类型
27. SQLInteget sInt = (SQLInteger) anns[0];
28. if(sInt.name().length() < 1){//如果没有指定列名称,则使用字段名称
29. columnName = f.getName().toUpperCase();
30.}else{
31. columnName = sInt.name();
32.}
33.columnDefs.add(columnName + “ INT” + getConstraints(sInt.constraints()));
34.}
35.if(anns[0] instanceof SQLString){
36. SQLString sString = (SQLString) anns[0];
37. if(sString.name().length() < 1){//如果没有指定列名称,则使用字段名称
38. columnName = f.getName().toUpperCase();
39.}else{
40. columnName = sInt.name();
41.}
42.columnDefs.add(columnName + “ VARCHAR(” + sString.value() +”)”
43. + getConstraints(sString.constraints()));
44.}
45.}
46.StringBuilder createCommand = new StringBuilder(“CREATE TABLE”
47.+ tableName + “(”);
48. for(String columnDef : columnDefs){
49. createCommand.append(“\n ” + columnDef + “,”);
50.}
51.//删除最后的”,”
52.String tableCreate = createCommand.subString(0, createCommand.length() - 1)
53.+ “);”;
54. System.out.println(“Table creation SQL for ” + className + “ is :\n”
55.+ tableCreate);
56.}
57.//获取约束
58.Private static String getConstraints(Constraints con){
59. String constraints = “”;
60. if(!con.allowNnull()){
61. constraints += “ NOT NULL”;
62.}
63.if(con.primaryKey()){
64. constraints += “ PRIMARY KEY”;
65.}
66.if(con.unique()){
67. constraints += “ UNIQUE”;
68.}
69.return constraints;
70.}
71.}

输出结果:

Table creation SQL for Member is:

CREATE TABLE MEMBER(

FIRSTNAME  VARCHAR(30),

LASTNAME   VARCHAR(50),

AGE  INT,

HANDLE    VARCHAR(30)  PRIMARY KEY);

6.使用apt处理Annotation注解:

Annotation processing tool, apt是sun提供的第一个版本的Annotation注解处理器,apt的使用和javac类似,是真的未编译的源码,而非已经编译的class字节码文件,使用apt的时候不能使用java的反射机制,因为源码尚未编译,需要使用mirrorAPI,mirror API可以使得apt看到未编译源代码中的方法,字段和类信息。使用apt的例子如下:

(1).注解:

01.package annotations;
02.
03.import java.lang.annotation.*;
04.
05.@Target(ElementType.TYPE)
06.@Retention(RetentionPolicy.SOURCE)
07.public @interface ExtractInterface{
08. public String value();
09.}

这个注解用于从使用该注解的类中抽象公共的方法,并为该类生成一个接口。

(2).使用注解的类:

01.package annotations;
02.
03.@ExtractInterface(“IMultiplier”)
04.public class Multiplier{
05. public int multiply(int x, int y){
06. int total = 0;
07. for(int i = 0; I < x; i++){
08. total = add(total, y);
09.}
10.return total;
11.}
12.private int add(int x, int y){
13. return x + y;
14.}
15.}

(3).注解处理器:

01.package annotations;
02.
03.import com.sun.mirror.apt.*;
04.import com.sun.mirror.declaration.*;
05.import java.io.*;
06.import java.util.*;
07.
08.public class InterfaceExtractorProcessor implements AnnotationProcessor{
09. //注解处理器的工作环境
10.private final AnnotationProcessorEnvironment env;
11. private List<MethodDeclaration> interfaceMethods =
12.new ArrayList< MethodDeclaration>();
13. public InterfaceExtractorProcessor(AnnotationProcessEnvironment env){
14. this.env = env;
15.}
16.public void process(){
17. //查询注解处理器环境中的类型声明
18. for(TypeDeclaration typeDecl : env.getSpecifiedTypeDeclarations()){
19. //获取注解
20.ExtractInterface annot = typeDecl.getAnnotation(ExtractInterface.class);
21. if(annot == null){
22. break;
23.}
24.//遍历所有添加注解的方法
25.for(MethodDeclaration m : typeDecl.getMethods()){
26. //方法签名中的访问控制符是public的,且不是静态方法
27.if(m.getModifiers().contains(Modifier.PUBLIC) &&
28.!(m.getModifiers().contains(Modifier.STATIC))){
29. interfaceMethods.add(m);
30.}
31.}
32.if(interfaceMethods.size() > 0){
33. try{
34. PrintWriter writer = env.getFiler().createSourceFile(annot.value());
35. writer.println(“package ” + typeDecl.getPackage().getQualifiedName()
36.+ “;”);
37. writer.println(“public interface ” + annot.value() + “{“);
38. //写方法声明
39. for(MethodDeclaration m : interfaceMethods){
40. writer.print(“ public’);
41. writer.print(m.getReturnType() + “ ”);
42. writer.print(m.getSimpleName() + “ ”);
43. int i = 0;
44. //写方法参数列表
45. for(ParametherDeclaration parm : m.getParameters()){
46. writer.print(parm.getType() + “ ” + parm.getSimpleName());
47. if( ++i < m.getParameters().size()){
48. writer.print(“, ”);
49.}
50.}
51.writer.println(“);”)
52.}
53.writer.println(“}”);
54.writer.close();
55.}catch(Exception e){
56. Throw new RuntimeException(e);
57.}
58.}
59.}
60.}
61.}

使用sun的mirror API可以获取源码中的Field, type, method等信息。

(4).为apt工厂提供注解处理器:

01.package annotations;
02.
03.import com.sun.mirror.apt.*;
04.import com.sun.mirror.declaration.*;
05.import java.util.*;
06.
07.public class InterfaceExtractorProcessorFactory implements AnnotationProcessorFactory{
08. //获取注解处理器
09.public AnnotationProcessor getProcessorFor(Set<AnnotationTypeDeclaration> atds,
10.AnnotationProcessorEnvironment env){
11. return new InterfaceExtractorProcess(env);
12.}
13.//定义注解处理器所支持的注解类型
14.public Collection<String> supportedAnnotationTypes(){
15. return Collections.singleton(“ExtractInterface”);
16.}
17.//定义注解处理器支持的选项
18.public Collection<String> supportedOptions(){
19. return Collections.emptySet();
20.}
21.}

(5).运行apt:

使用下面的命令允许apt:

apt-factory annotations.InterfaceExtractorProcessorFactory Multiplier.java –s ../annotations

输出结果:

package annotations;

public interface IMultiplier{

public int multiply(intx, int y);

}

转载:http://blog.csdn.net/chjttony/article/details/7017153#

Java编程思想学习(十五) 注解的更多相关文章

  1. Java编程思想学习&lpar;十六&rpar; 并发编程

    线程是进程中一个任务控制流序列,由于进程的创建和销毁需要销毁大量的资源,而多个线程之间可以共享进程数据,因此多线程是并发编程的基础. 多核心CPU可以真正实现多个任务并行执行,单核心CPU程序其实不是 ...

  2. Java编程思想学习&lpar;十&rpar; 正则表达式

    正则表达式是一种强大的文本处理工具,使用正则表达式我们可以以编程的方法,构造复杂的文本模式,并且对输入的字符串进行搜索.在我看来,所谓正则表达式就是我们自己定义一些规则,然后就可以验证输入的字符串是不 ...

  3. Java编程思想学习&lpar;十四&rpar; 枚举

    关键字enum可以将一组具名的值有限集合创建一种为新的类型,而这些具名的值可以作为常规的程序组件使用. 基本enum特性 调用enum的values()方法可以遍历enum实例,values()方法返 ...

  4. Java编程思想学习&lpar;十二&rpar; 数组和容器

    一.数组 1).数组的多种初始化方式 下面总结了初始化数组的多种方式,以及如何对指向数组的引用赋值,使其指向另一个数组对象.值得注意的是:对象数组和普通数组的各种操作基本上都是一样的:要说有什么不同的 ...

  5. &lbrack;Java编程思想-学习笔记&rsqb;第3章 操作符

    3.1  更简单的打印语句 学习编程语言的通许遇到的第一个程序无非打印"Hello, world"了,然而在Java中要写成 System.out.println("He ...

  6. Java编程思想学习&lpar;八&rpar; 内部类

    可以将一个类的定义放在另一个类的定义内部,这就是内部类. 内部类的定义是简单的,但是它的语法确实很是复杂,让人不是很好理解.下面就内部类做一个小结. 一.内部类的分类 总的来讲内部类分为普通内部类,匿 ...

  7. java编程思想--学习心得

    学习Java编程思想,需要了解语言特性,对于各种名词,能够借助项目代码,解释其含义,不借助搜索工具,明白其在什么样场景下使用,会带来什么样的问题,能否避免这类问题. 学习的过程,与软件开发相同,一样是 ...

  8. Java编程思想学习&lpar;一&rpar;----对象导论中多态的理解

    1.1抽象过程 1)万物皆对象. 2)程序是对象的集合,他们通过发送消息来告知彼此所要求做的. 3)每个对象都有自己的由其他对象所构成的存储. 4)每个对象都拥有其类型. 5)某一特定类型的所有对象都 ...

  9. Java编程思想学习笔记——注解

    前言 在Android开发的过程中,我们为了减少重复代码的编写,会使用类似ButterKnife,AndroidAnnotations 这类依赖注解库.代码示例如下: //不使用 Button btn ...

随机推荐

  1. 初学者用div&plus;css结构写网页的几个误区

    1.用div+css结构制作静态html网页不等于彻底抛弃古老的table写法.之所以不建议用table来布局网页是因为在网页加载很慢的时候要等table结构加载完成才能看到网页,其次是table的布 ...

  2. 启动项目报错Error&colon; listen EADDRINUSE

    我在使用elasticsearch的kibana插件时候,有一次启动,遇到这个错误: Error: listen EADDRINUSE 它的意思是,端口5601被其他进程占用. 故而,需要kill掉那 ...

  3. leetcode 233 Number of Digit One

    这题属于需要找规律的题.先想一下最简单的情形:N = 10^n - 1 记X[i]表示从1到10^i - 1中 1 的个数,则有如下递推公式:X[i] = 10 * X[i - 1] + 10^(i ...

  4. &lbrack;Orchard CMS系列&rsqb; 创建主题(Writing a new theme)

    本文需要对Orchard CMS有基本了解. 开启模块 code generation 创建新的主题工程骨架 Codegen theme MyTheme 创建主题样式 src\Orchard.Web\ ...

  5. 枚举算法总结 coming~&Hat;&period;&ast;

    感谢CJ同学监督╭(╯^╰)╮.从放假到现在都木有更新博客了~噶呜~小娘谨记教诲,每天会更新博客==!! 看了一下POJ训练计划,虽然已经零零散散做了40多道题了,还是从头开始整理一下漏掉的知识点.T ...

  6. 利用光场进行深度图估计&lpar;Depth Estimation&rpar;算法之一——聚焦算法

    前面几篇博客主要说了光场相机,光场相机由于能够记录相机内部整个光场,可以实现重聚焦(模糊线索)和不同视角的变换(视差线索),同时也可以利用这个特性进行深度估计(Depth Estimation). 先 ...

  7. Git如何在不提交当前分支的情况下切换到其它分支进行操作——git stash

    假如现在的Bug你还没有解决,而上边又给你派了一个新的Bug,而这个Bug相比较现在正在苦思冥想的Bug比较容易解决. 你想先解决新的Bug,可是之前的Bug还没有解决完而不能提交.怎么办? 解决方法 ...

  8. wget下载指定URL下的特定属性文件

    例子:下载指定URL下的kernel开头的所有包 wget https://archives.fedoraproject.org/pub/fedora/linux/updates/28/Everyth ...

  9. laravel 前后端分离 token

    由于自己开发的项目中用到了 JWT 技术,前端采用了 Vue.js 框架,后端采用了 CodeIgniter 框架,故作此文帮助使用相同技术栈的朋友们. 具体思路如下:把后端生成的 JWT token ...

  10. bootstrap1相关学习文档

    <em>Bootstrap 框架</em>                                                    //倾斜 4.对齐 //设置文 ...