??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲国产免费综合,久久久亚洲欧洲日产国码农村,亚洲gv猛男gv无码男同短文http://m.tkk7.com/herodby/articles/140571.htmlTue, 28 Aug 2007 08:22:00 GMThttp://m.tkk7.com/herodby/articles/140571.htmlhttp://m.tkk7.com/herodby/comments/140571.htmlhttp://m.tkk7.com/herodby/articles/140571.html#Feedback0http://m.tkk7.com/herodby/comments/commentRss/140571.htmlhttp://m.tkk7.com/herodby/services/trackbacks/140571.html版权声明Q本文可以自p{载,转蝲时请务必以超链接形式标明文章原始出处和作者信息及本声?/span>
原文:http://www.matrix.org.cn/resource/article/44/44055_Java+Annotation+Reflect.html
关键?java,annotation,reflect

阅读提示Q文中提到的E序成员或者程序元素是一个概念,指组成程序代码的单元Q如cR方法、成员变量?/span>

一、AnnotationI竟是什么?

Annotation提供了一条与E序元素兌M信息或者Q何元数据QmetadataQ的途径。从某些斚w看,annotation像修饰W一栯使用Qƈ应用于包、类型、构造方法、方法、成员变量、参数、本地变量的声明中。这些信息被存储在annotation?#8220;name=value”l构对中。annotationcd是一U接口,能够通过java反射API的方式提供对其信息的讉K?br>
annotation能被用来为某个程序元素(cR方法、成员变量等Q关联Q何的信息。需要注意的是,q里存在着一个基本的潜规则:annotaion不能影响E序代码的执行,无论增加、删除annotationQ代码都始终如一的执行。另外,管一些annotation通过java的反apiҎ在运行时被访问,而java语言解释器在工作时忽略了q些annotation。正是由于java虚拟机忽略了annotationQ导致了annotationcd在代码中?#8220;不v作用”的;只有通过某种配套的工h会对annotationcd中的信息q行讉K和处理。本文中涵盖标准的annotation和meta-annotationcdQ陪伴这些annotationcd的工hjava~译器(当然要以某种Ҏ的方式处理它们)?br>
׃上述原因Qannotation在用时十分ѝ一个本地变量可以被一个以NonNull命名的annotationcd所标注Q来作ؓ对这个本地变量不能被赋予null值的断言。而我们可以编写与之配套的一个annotation代码分析工具Q用它来对h前面变量的代码进行解析,q且试验证q个断言。当然这些代码ƈ不必自己~写。在JDK安装后,在JDK/bin目录中可以找到名?#8220;apt”的工P它提供了处理annotation的框Ӟ它启动后扫描源代码中的annotationQƈ调用我们定义好的annotation处理器完成我们所要完成的工作Q比如验证前面例子中的断aQ。说到这里,annotation的强大功能似乎可以替代XDocletq类的工具了Q随着我们的深入,大家会更加坚信这一炏V?br>注:详细描述请参看jsr250规范Q?br>http://www.jcp.org/aboutJava/communityprocess/pfd/jsr250/

二、Annotation的定义:

q段文字开始介lannotation相关技术。在此大家将看到java5.0的标准annotationcdQ这U标准类型就是前文中所说的“内徏”cdQ它们可以直接被javac支持。可喜的是,在java6.0beta版中的javac已经加入了对自定义annotation的支持?br>
1。Annotation的概念和语法Q?/span>

首先Q关键的概念是理解annotation是与一个程序元素相兌信息或者元数据的标注。它从不影响javaE序的执行,但是对例如编译器警告或者像文生成器等辅助工具产生影响?br>
下面是常用的annotation列表Q我们应该注意在annotation和annotationcd之间的不同:

A.annotationQ?/span>
annotation使用了在java5.0所带来的新语法Q它的行为十分类似public、finalq样的修饰符。每个annotationh一个名字和成员个数>=0。每个annotation的成员具有被UCؓname=value对的名字和|像javabean一PQname=value装蝲了annotation的信息?br>
B.annotationcdQ?/span>
annotationcd定义了annotation的名字、类型、成员默认倹{一个annotationcd可以说是一个特D的java接口Q它的成员变量是受限制的Q而声明annotationcd旉要用新语法。当我们通过java反射api讉KannotationӞq回值将是一个实C该annotationcd接口的对象,通过讉Kq个对象我们能方便的讉K到其annotation成员。后面的章节提到在java5.0的java.lang包里包含?个标准annotationcd?br>
C.annotation成员Q?/span>
annotation的成员在annotationcd中以无参数的Ҏ的Ş式被声明。其Ҏ名和q回值定义了该成员的名字和类型。在此有一个特定的默认语法Q允许声明Q何annotation成员的默认|一个annotation可以name=value对作为没有定义默认值的annotation成员的|当然也可以用name=valueҎ覆盖其它成员默认倹{这一Ҏ些近似类的承特性,父类的构造函数可以作为子cȝ默认构造函敎ͼ但是也可以被子类覆盖?br>
D.marker annotationcdQ?/span>
一个没有成员定义的annotationcd被称为marker annotation。这Uannotationcd仅用自w的存在与否来ؓ我们提供信息。如后面要说的Override?br>
E.meta-annotationQ?/span>
meta-annotation也称为元annotationQ它是被用来声明annotationcd的annotation。Java5.0提供了一些标准的?annotationcd。下面介l的target、retention是meta-annotation?br>
F.targetQ?/span>
annotation的target是一个被标注的程序元素。target说明了annotation所修饰的对象范_annotation可被用于packages、typesQ类、接口、枚举、annotationcdQ、类型成员(Ҏ、构造方法、成员变量、枚丑ր|、方法参数和本地变量Q如循环变量、catch参数Q。在annotationcd的声明中使用了target可更加明晰其修饰的目标?br>
G.retentionQ?/span>
annotation的retention定义了该annotation被保留的旉长短Q某些annotation仅出现在源代码中Q而被~译器丢弃;而另一些却被编译在class文g中;~译在class文g中的annotation可能会被虚拟机忽略,而另一些在class被装载时被dQ请注意q不影响class的执行,因ؓannotation与class在用上是被分离的)。用这个meta-annotation可以对annotation?#8220;生命周期”限制?br>
H.metadataQ?/span>
׃metadata被广泛用于各种计算机开发过E中Q所以当我们在这里谈论的metadata卛_数据通常指被annotation装蝲的信息或者annotation本n?br>
2。用标准AnnotationQ?/span>
java5.0在java.lang包中定义?U标准的annotationcdQ?br>
A.OverrideQ?/span>
java.lang.Override是一个marker annotationcdQ它被用作标注方法。它说明了被标注的方法重载了父类的方法,起到了断a的作用。如果我们用了q种annotation在一个没有覆盖父cL法的ҎӞjava~译器将以一个编译错误来警示?br>q个annotaton常常在我们试图覆盖父cL法而确又写错了Ҏ名时发挥威力?br>
使用Ҏ极其单:在用此annotation时只要在被修饰的Ҏ前面加上@Override?br>下面的代码是一个用@Override修饰一个企N载父cȝtoStringҎQ而又存在拼写错误的sampleQ?br>清单1Q?/strong>

@Override
public String toSting() {   // 注意Ҏ名拼写错?br>    return "[" + super.toString() + "]";
}


B.DeprecatedQ?/span>
同样Deprecated也是一个marker annotation。当一个类型或者类型成员用@Deprecated修饰的话Q编译器不鼓励使用q个被标注的E序元素。而且q种修饰h一定的“延箋?#8221;Q如果我们在代码中通过l承或者覆盖的方式使用了这个过时的cd或者成员,虽然l承或者覆盖后的类型或者成员ƈ不是被声明ؓ@DeprecatedQ但~译器仍然要报警?br>值得注意Q@Deprecatedq个annotationcd和javadoc中的@deprecatedq个tag是有区别的:前者是java~译器识别的Q而后者是被javadoc工具所识别用来生成文Q包含程序成员ؓ什么已l过时、它应当如何被禁止或者替代的描述Q?br>在java5.0Qjava~译器仍然象其从前版本那样寻找@deprecatedq个javadoc tagQƈ使用它们产生警告信息。但是这U状况将在后l版本中改变Q我们应在现在就开始用@Deprecated来修饰过时的Ҏ而不是@deprecated javadoc tag?br>清单2Q?/strong>

下面是一D用@Deprecated的代码:
/**
* q里是javadoc的@deprecated声明.
* @deprecated No one has players for this format any more.  Use VHS instead.
*/
@Deprecated public class Betamax { ... }


C.SuppressWarningsQ?/span>
@SuppressWarnings被用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告。在java5.0Qsun提供的javac~译器ؓ我们提供?Xlint选项来ɾ~译器对合法的程序代码提告,此种警告从某U程度上代表了程序错误。例如当我们使用一个generic collectionc而又没有提供它的cdӞ~译器将提示?unchecked warning"的警告?br>
通常当这U情况发生时Q我们就需要查扑ּ赯告的代码。如果它真的表示错误Q我们就需要纠正它。例如如果警告信息表明我们代码中的switch语句没有覆盖所有可能的caseQ那么我们就应增加一个默认的case来避免这U警告?br>总Q有时我们无法避免这U警告,例如Q我们用必d非generic的旧代码交互的generic collectioncLQ我们不能避免这个unchecked warning。此时@SuppressWarningpz上用场了,在调用的Ҏ前增加@SuppressWarnings修饰Q告诉编译器停止ҎҎ的警告?br>SuppressWarning不是一个marker annotation。它有一个类型ؓString[]的成员,q个成员的gؓ被禁止的警告名。对于javac~译器来Ԍ?Xlint选项有效的警告名也同样对@SuppressWarings有效Q同时编译器忽略掉无法识别的警告名?br>
annotation语法允许在annotation名后跟括P括号中是使用逗号分割的name=value对用于ؓannotation的成员赋|
清单3Q?/strong>

@SuppressWarnings(value={"unchecked","fallthrough"})
public void lintTrap() { /* sloppy method body omitted */ }


在这个例子中SuppressWarnings annotationcd只定义了一个单一的成员,所以只有一个简单的value={...}作ؓname=value寏V又׃成员值是一个数l,故用大括号来声明数l倹{?br>
注意Q我们可以在下面的情况中~写annotationQ当annotation只有单一成员Qƈ成员命名?value="。这时可以省?value="。比如将上面的SuppressWarnings annotationq行~写Q?br>清单4Q?/strong>

@SuppressWarnings({"unchecked","fallthrough"})

如果SuppressWarnings所声明的被止警告个数Z个时Q可以省d括号Q?br>

@SuppressWarnings("unchecked")


3。Annotation语法Q?/span>

在上一个章节中Q我们看C写marker annotation和单一成员annotation的语法。下面本人来介绍一下完整的语法Q?br>
annotation?#8220;@+annotationcd名称+(..逗号分割的name-value?..)”l成。其中成员可以按照Q何的序。如果annotationcd定义了某个成员的默认|则这个成员可以被省略。成员值必Mؓ~译时常量、内嵌的annotation或者数l?br>
下面我们定义一个annotationcd名ؓReviewsQ它有一个由@Review annotation数组构成的成员。这个@Review annotationcd有三个成员:"reviewer"是一个字W串Q?comment" 是一个具有默认值的可选的字符Ԍ"grade"是一个Review.Grade枚Dcd倹{?br>清单5Q?/strong>

@Reviews({  // Single-value annotation, so "value=" is omitted here
    @Review(grade=Review.Grade.EXCELLENT,
            reviewer="df"),
    @Review(grade=Review.Grade.UNSATISFACTORY,
            reviewer="eg",
            comment="This method needs an @Override annotation")
})

annotation语法的另一个重要规则是没有E序成员可以有多于一个的同一annotation实例。例如在一个类中简单的攄多个@Review annotation。这也是在上面代码中定义@Reviews annotationcd数组的原因?br>
4。Annotation成员cd和|

annotation成员必须是非I的~译时常量表辑ּ。可用的成员cd为:primitivecd? String, Class, enumeratedcd, annotationcd, 和前面类型的数组?br>
下面我们定义了一个名为UncheckedExceptions 的annotationcdQ它的成员是一个扩展了RuntimeExceptioncȝcLl?br>清单6Q?/strong>

@UncheckedExceptions({
    IllegalArgumentException.class, StringIndexOutOfBoundsException.class
})


5。Annotation的目标:

annotation通常被放在类型定义和成员定义的前面。然而它也出现在package、方法参数、本地变量的前面。下面,我们来讨Z下这些不大常用的写法Q?br>
package annotation出现在package声明的前面?br>下面的例子package-info.java中不包含M的公q型定义,却包含一个可选的javadoc注释?br>清单7Q?/strong>

/**
* This package holds my custom annotation types.
*/
@com.davidflanagan.annotations.Author("David Flanagan")
package com.davidflanagan.annotations;

当package-info.java文g被编译时Q它生名为包含annotationQ特D的接口Q声明的package-info.class的类。这个接口没有成员,它的名字package-info不是一个合法的java标识Q所以它不能用在java源代码中。这个接口的存在只是单的被看作一个ؓpackage annotation准备的占位符?br>
用于修饰Ҏ参数、catch参数、本地变量的annotation只是单的出现在这些程序成员的修饰W位|。javacL件格式没有ؓ本地变量或者catch参数存储annotation作准备,所以这些annotationL保留在源代码U别Qsource retentionQ;Ҏ参数annotation能够保存在类文g中,也可以在保留到运行时?br>
最后,h意,枚Dcd定义中不允许M的修饰符修饰其枚丑ր{?br>
6。Annotation和默认|
在Annotation中,没有默认值的成员必须有一个成员倹{而如何理解默认值是如何被处理就是一个很重要的细节:annotationcd所定义的成员默认D存储在class文g中,不被~译到annotation里面。如果我们修改一个annotationcd使其成员的默认值发生了改变Q这个改变对于所有此cd的annotation中没有明提供成员值的成员产生影响Q即修改了该成员的成员|。即使在annotationcd使其成员的默认D改变后annotation从没被重新编译过Q该cd的annotation(改变前已l被~译?也受到媄响?br>
三、Annotation工作原理Q?/span>

Annotation与反?/span>
在java5.0中Java.lang.reflect提供的反API被扩充了dq行时annotation的能力。让我们回顾一下前面所讲的Q一个annotationcd被定义ؓruntime retention后,它才是在q行时可见,当class文g被装载时被保存在class文g中的annotation才会被虚拟机d。那么reflect是如何帮助我们访问class中的annotation呢?

下文在java.lang.reflect用于annotation的新Ҏ,其中java.lang.reflect.AnnotatedElement是重要的接口Q它代表了提供查询annotation能力的程序成员。这个接口被java.lang.Package、java.lang.Class实现Qƈ间接地被MethodcRConstructorcRjava.lang.reflect的Fieldcd现。而annotation中的Ҏ参数可以通过MethodcRConstructorcȝgetParameterAnnotations()Ҏ获得?br>
下面的代码用了AnnotatedElementcȝisAnnotationPresent()Ҏ判断某个Ҏ是否h@Unstable annotationQ从而断a此方法是否稳定:
清单8Q?/strong>

import java.lang.reflect.*;

Class c = WhizzBangClass.class;                          
Method m = c.getMethod("whizzy", int.class, int.class);  
boolean unstable = m.isAnnotationPresent(Unstable.class);

isAnnotationPresent()Ҏ对于查marker annotation是十分有用的Q因为marker annotation没有成员变量Q所以我们只要知道class的方法是否用了annotation修饰可以了。而当处理h成员的annotationӞ我们通过使用getAnnotation()Ҏ来获得annotation的成员信息(成员名称、成员|。这里我们看C一套优的java annotationpȝQ如果annotation存在Q那么实C相应的annotationcd接口的对象将被getAnnotation()Ҏq回Q接着调用定义在annotationcd中的成员Ҏ可以方便地获得Q何成员倹{?br>
回想一下,前面介绍的@Reviews annotationQ如果这个annotationcd被声明ؓruntime retention的话Q我们通过下面的代码来讉K@Reviews annotation的成员|
清单9Q?/strong>

AnnotatedElement target = WhizzBangClass.class; //获得被查询的AnnotatedElement
// 查询AnnotatedElement的@Reviews annotation信息
Reviews annotation = target.getAnnotation(Reviews.class);
// 因ؓ@Reviews annotationcd的成员ؓ@Review annotationcd的数l,
// 所以下面声明了Review[] reviews保存@Reviews annotationcd的value成员倹{?br>Review[] reviews = annotation.value();
// 查询每个@Review annotation的成员信?br>for(Review r : reviews) {
    Review.Grade grade = r.grade();
    String reviewer = r.reviewer();
    String comment = r.comment();
    System.out.printf("%s assigned a grade of %s and comment '%s'%n",
                      reviewer, grade, comment);
}


四、如何自定义AnnotationQ?/span>

1Q详解annotation与接口的异同Q?/span>
因ؓannotationcd是一个非凡的接口Q所以两者之间存在着某些差异Q?br>
A.Annotationcd使用关键字@interface而不是interface?/span>
q个关键字声明隐含了一个信息:它是l承了java.lang.annotation.Annotation接口Qƈ非声明了一个interface?br>
B.Annotationcd、方法定义是独特的、受限制的?/span>
Annotationcd的方法必d明ؓ无参数、无异常抛出的。这些方法定义了annotation的成员:Ҏ名成Z成员名,而方法返回值成Z成员的类型。而方法返回值类型必Mؓprimitivecd、Classcd、枚丄型、annotationcd或者由前面cd之一作ؓ元素的一l数l。方法的后面可以使用default和一个默认数值来声明成员的默认|null不能作ؓ成员默认|q与我们在非annotationcd中定义方法有很大不同?br>Annotationcd和它的方法不能用annotationcd的参数、成员不能是generic。只有返回值类型是Class的方法可以在annotationcd中用genericQ因为此Ҏ能够用类转换各U类型{换ؓClass?br>
C.Annotationcd又与接口有着q似之处?/span>
它们可以定义帔R、静态成员类型(比如枚Dcd定义Q。Annotationcd也可以如接口一般被实现或者ѝ?br>
2Q实例:
下面Q我们将看到如何定义annotationcd的example。它展示了annotationcd声明以及@interface与interface之间的不同:
清单10Q?/strong>

package com.davidflanagan.annotations;
import java.lang.annotation.*;

/**
* 使用annotation来描q那些被标注的成员是不稳定的Q需要更?br>*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Unstable {}


下面的另一个example只定义了一个成员。ƈ通过这个成员命名ؓvalueQ我们可以方便的用这Uannotation的快捷声明方式:
清单11Q?/strong>

/**
* 使用Authorq个annotation定义在程序中指出代码的作?br> */
public @interface Author {
    /** q回作者名 */
    String value();
}


以下的example更加复杂。Reviews annotationcd只有一个成员,但是q个成员的类型是复杂的:由Review annotationl成的数l。Review annotationcd?个成员:枚Dcd成员grade、表CReview名称的字W串cd成员Reviewer、具有默认值的字符串类型成员Comment?br>清单12Q?/strong>

import java.lang.annotation.*;
        
/**
* Reviews annotationcd只有一个成员,
* 但是q个成员的类型是复杂的:由Review annotationl成的数l?br> */
@Retention(RetentionPolicy.RUNTIME)
public @interface Reviews {
    Review[] value();
}

/**
* Review annotationcd?个成员:
* 枚Dcd成员grade?br>  * 表示Review名称的字W串cd成员Reviewer?br>  * h默认值的字符串类型成员Comment?br> */
public @interface Review {
    // 内嵌的枚丄?br>    public static enum Grade { EXCELLENT, SATISFACTORY, UNSATISFACTORY };

    // 下面的方法定义了annotation的成?br>    Grade grade();                
    String reviewer();          
    String comment() default "";  
}


最后,我们来定义一个annotationҎ用于|列出类q行中所有的unchecked异常Q上文已l提到这U情况不一定是错误Q。这个annotationcd一个数l作Z唯一的成员。数l中的每个元素都是异常类。ؓ了加强对未检查的异常Q此cd帔R是在q行时抛出)q行报告Q我们可以在代码中对异常的类型进行限Ӟ
清单13Q?/strong>

public @interface UncheckedExceptions {
    Class<? extends RuntimeException>[] value();
}


五、Meta-Annotation

Annotationcd可以被它们自己所标注。Java5.0定义?个标准的meta-annotationcdQ它们被用来提供对其它annotationcd作说明。这些类型和它们所支持的类在java.lang.annotation包中可以扑ֈ。如果需要更详细的信息可以参考jdk5.0手册?br>
1Q再谈Target
作ؓmeta-annotationcd的Target,它描qCannotation所修饰的程序成员的cd。当一个annotationcd没有TargetӞ它将被作为普通的annotation看待。当它修饰一个特定的E序成员Ӟ它将发挥其应用的作用Q例如:Override用于修饰ҎӞ增加了@Targetq个meta-annotation׃ɾ~译器对annotation作检查,从而去掉修饰错误类型的Override?br>
Target meta-annotationcd有唯一的value作ؓ成员。这个成员的cd是java.lang.annotation.ElementType[]cd的,ElementTypecd是可以被标注的程序成员的枚Dcd?br>
2QRetention的用?/span>
我们在文章的开头曾l提到过RetentionQ但是没有详l讲解。Retention描述了annotation是否被编译器丢弃或者保留在class文gQ如果保留在class文g中,是否在class文g被装载时被虚拟机d。默认情况下Qannotation被保存在class文g中,但在q行时ƈ不能被反访问。Retentionh三个取|source、class、runtimeQ这些取值来自java.lang.annotation.RetentionPolicy的枚丄型倹{?br>
Retention meta-annotationcd有唯一的value作ؓ成员Q它的取值来自java.lang.annotation.RetentionPolicy的枚丄型倹{?br>
3QDocumented
Documented是一个meta-annotationcdQ用于描q其它类型的annotation应该被作标注的程序成员的公共APIQ因此可以被例如javadoc此类的工h化?br>
Documented是一个marker annotationQ没有成员?br>
4QInherited
@Inherited meta-annotation也是一个marker annotationQ它阐述了某个被标注的类型是被承的。如果一个用了@Inherited修饰的annotationcd被用于一个classQ则q个annotation被用于该class的子cR?br>
注意Q@Inherited annotationcd是被标注q的class的子cLl承。类q不从它所实现的接口承annotationQ方法ƈ不从它所重蝲的方法承annotation?br>
值得思考的是,当@Inherited annotationcd标注的annotation的Retention是RetentionPolicy.RUNTIMEQ则反射API增强了这U承性。如果我们用java.lang.reflectL询一个@Inherited annotationcd的annotationӞ反射代码查将展开工作Q检查class和其父类Q直到发现指定的annotationcd被发玎ͼ或者到辄l承l构的顶层?br>
六、ȝQ?/span>

本文几乎覆盖了所有的Annotation的概念和知识点,从annotation的定义、语法到工作原理、如何自定义annotationQ直至meta-annotation。其中也h一些配套的代码片断可参考,虽然不是很多Q但是可谓言意赅、着光点,本h认ؓ用好annotation的关键还在于使用。希望本手册能够帮助大家用好annotationQ这也是本h的最大快乐?br>

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=658824



2007-08-28 16:22 发表评论
]]>
վ֩ģ壺 Ƶۿwww| պѸƵ| Ƶ㶮| רVAV| ٸԼA| ҹƷһ߲ŷ| ٶ˽ȫֱ| ޹aۺ| պ߹ۿƵ| ޳avƬۿ| fc2˳ΪƵ| þ޹Ʒ123| һ| þþƷһ| **ëƬѹۿþþƷ| Ӱ| ëƬѹۿƵ| A˵߹ۿ| ĻþƷѹۿ| ĻѸƵ | ͵12p| ĻƵ| ޾Ʒ͵Բ| ƬƵۿ| ݾ߹ۿվ| þþþþþþ| ߿Ƭ˳Ƶ| AVһɫ| avַ| ƷվƵ| ŷպһ| jlzzjlzz߲Ƶ| պѸ岥| ŷպٲ| ޾Ʒ۲ӰԺ| պƵ| AVպAVһ| AVһ| Ƶһ| ߵƵ߹ۿ| Ʒ456߲|