首先来说注释有三种:
//
/* */
/** */
前两种编译器直接跳过,从来不阅读,第三种编译器是可以看懂的,当你使用javadoc这样的命令时会用到,用来生成api时用的。
Annotations是一种元数据,其作用在于提供程序本身以外的一些数据信息,也就是说Annotation他不会属于程序代码本身,不参与逻辑运算,故而不会对原程序代码的操作产生直接的影响。
元数据是关于数据的数据。在编程语言上下文中,元数据是添加到程序元素如方法、字段、类和包上的额外信息。
Java 元数据(Annotation)是 J2SE 5.0 (研发历时近三年,于2004年9月30日正式发布,代号为“Tiger”)新增加的功能之一,它在JSR-175规范中有详细定义。该机制允许在 Java 代码中添加自定义注释,并允许通过反射(reflection),以编程方式访问元数据注释。通过提供为程序元素附加额外数据的标准方法,元数据功能具有简化和改进许多应用程序开发领域的潜在能力,其中包括配置管理、框架实现和代码生成。
元数据的作用:
元数据甚至使我们可以不用修改核心语言,就能够在 Java 语言中添加新功能,使核心语言成为一种开放式语言。在纯面向对象的语言中实现AOP就是使用元数据进行语言扩展的一个很好的例子。AspectWerkz、JBoss AOP以及AspectJ5 使用元数据将类的语义转换为一个aspect、将数据字段转换为一个pointcut、将方法转换为一个advice,等等。
@Override
public String toString() {
return"This is String Representation of current object.";
}
上面的代码中,我重写了toString()方法并使用了@Override注解。但是,即使我不使用@Override注解标记代码,程序也能够正常执行。
那么,该注解表示什么?这么写有什么好处吗?
事实上,@Override告诉编译器这个方法是一个重写方法(描述方法的元数据),如果父类中不存在该方法,编译器便会报错,提示该方法没有重写父类中的方法。如果我不小心拼写错误,例如将toString()写成了toStrring(){double r},而且我也没有使用@Override注解,那程序依然能编译运行。但运行结果会和我期望的大不相同。
现在我们了解了什么是注解,并且使用注解有助于阅读程序。 当然这只是注解的一种使用场景而已
对于@Override注解你可能有些疑问,它什么都没做,那它是如何检查在父类中有一个同名的函数呢?
我们需要尤其注意的是,注解只是元数据,也就是说它只是一种类似vo的存储对象实例,无论想要完成任何逻辑上的处理,都需要其他代码的处理
也就是说,必然某处存在对@Override的处理
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public@interface Override {
}
J2SE5.0版本在 java.lang.annotation提供了四种元注解,专门注解其他的注解:
@Documented –注解是否将包含在JavaDoc中,表示是否将注解信息添加在java文档中
@Retention –什么时候使用该注解
@Target –注解用于什么地方
@Inherited – 是否允许子类继承该注解
@Retention定义了该Annotation被保留的时间长短:
使用这个meta-Annotation可以对 Annotation的“生命周期”限制,一般来说Annotation有如下三种使用情形:
- Information for the compiler — Annotations can be used by the compiler to detect errors or suppress * warnings.
- Compile-time and deployment-time processing — Software tools can process annotation information to generate code, XML files, and so forth.
- Runtime processing — Some annotations are available to be examined at runtime.
SOURCE 标记一些信息,为编译器提供辅助信息。
CLASS 编译时动态处理。
RUNTIME 运行时动态处理。
@Target说明了Annotation所修饰的对象范围:Annotation可被用于
表示该注解用于什么地方。如果不明确指出,该注解可以放在任何地方。以下是一些可用的参数。需要说明的是:属性的注解是兼容的,如果你想给7个属性都添加注解,仅仅排除一个属性,那么你需要在定义target包含所有的属性。
ElementType | exp |
---|---|
ElementType.PACKAGE | 用于记录java文件的package信息 |
ElementType.TYPE | 用于描述类、接口或enum声明 |
ElementType.CONSTRUCTOR | 构造方法声明 |
ElementType.METHOD | 构造方法声明 |
ElementType.PARAMETER | 参数声明 |
ElementType.FIELD | 字段声明(包括枚举常量),用于描述成员变量 |
ElementType.LOCAL_VARIABLE | 局部变量声明 |
ElementType.ANNOTATION_TYPE | 另一个注释 |
表示该注解类型被自动继承。
如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。
如果用户在当前类中查询这个元注解类型并且当前类的声明中不包含这个元注解类型,那么也将自动查询当前类的父类是否存在Inherited元注解,这个动作将被重复执行知道这个标注类型被找到,或者是查询到顶层的父类
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Todo {
public enum Priority {LOW, MEDIUM, HIGH}
public enum Status {STARTED, NOT_STARTED}
String author() default"Yash";
Priority priority() defaultPriority.LOW;
Status status() defaultStatus.NOT_STARTED;
}
@Todo(priority = Todo.Priority.MEDIUM, author = "Yashwant", status = Todo.Status.STARTED)
public void incompleteMethod1() {
//Some business logic is written
//But it’s not complete yet
}
如果注解中只有一个属性,可以直接命名为“value”,使用时无需再标明属性名。
@interface Author{
String value();
}
@Author("Yashwant")
public void someMethod() {
}
注解参数的可支持数据类型:
Annotation类型里面的参数该怎么设定:
注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null。
因此, 使用空字符串或0作为默认值是一种常用的做法。
当然,这个约束使得处理器很难表现一个元素的存在或缺失的状态,因为每个注解的声明中,所有元素都存在,并且都具有相应的值,为了绕开这个约束,我们只能定义一些特殊的值(-1,”“),例如空字符串或者负数,一次表示某个元素不存在,在定义注解时,这已经成为一个习惯用法
现在我们需要写一个用户程序调用我们的注解。这里我们需要使用反射机制。如果你熟悉反射代码,就会知道反射可以提供类名、方法和实例变量对象。所有这些对象都有getAnnotation()这个方法用来返回注解信息。我们需要把这个对象转换为我们自定义的注释(使用 instanceOf()检查之后),同时也可以调用自定义注释里面的方法。
Class businessLogicClass = BusinessLogic.class;
for(Method method : businessLogicClass.getMethods()) {
Todo todoAnnotation = (Todo)method.getAnnotation(Todo.class);
if(todoAnnotation != null) {
System.out.println(" Method Name : " + method.getName());
System.out.println(" Author : " + todoAnnotation.author());
System.out.println(" Priority : " + todoAnnotation.priority());
System.out.println(" Status : " + todoAnnotation.status());
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyType {
String authorName();
String lastModified();
String bugFixes() default "ok";
}
@MyType(authorName="hadeslee",lastModified="20061207")
public class Test1 {
/** Creates a new instance of Test1 */
public Test1() {
}
@Deprecated
@MyType(authorName="hadeslee",lastModified="20061207",bugFixes="what")
public void doSth(){
}
@MyType(authorName="hadeslee",lastModified="20061207",bugFixes="what")
public void doAnother(){
}
}
public class GetAnno {
/** Creates a new instance of GetAnno */
public GetAnno() {
}
public static void main(String[] args)throws Exception {
Test1 t=new Test1();
Class c=Test1.class ;
Annotation[] as= c.getDeclaredAnnotations();
for(Annotation an:as){
System.out.println("类Test1的注释"+an);
}
Method med=c.getDeclaredMethod("doSth");
Annotation[] ass=med.getDeclaredAnnotations();
for(Annotation an:ass){
Classextends Annotation> clazz=an.annotationType();
Annotation[] ased=clazz.getAnnotations();
for(Annotation ad:ased){
System.out.println("注释的注释:"+ad);
}
System.out.println("方法doSth的注释:"+an);
}
}
}
此程序输出如下
类Test1的注释@testAnno.MyType(bugFixes=ok, authorName=hadeslee, lastModified=20061207)
注释的注释:@java.lang.annotation.Documented()
注释的注释:@java.lang.annotation.Retention(value=RUNTIME)
方法doSth的注释:@java.lang.Deprecated()
注释的注释:@java.lang.annotation.Retention(value=RUNTIME)
注释的注释:@java.lang.annotation.Target(value=[TYPE, METHOD])
方法doSth的注释:@testAnno.MyType(bugFixes=what, authorName=hadeslee, lastModified=20061207)
注解的语法比较简单,除了@符号的使用外,他基本与Java固有的语法一致,JavaSE中内置三个标准注解,定义在java.lang中:
@Override,限定重写父类方法:
@Override 是一个标记注解类型,它被用作标注方法。它说明了被标注的方法重载了父类的方法,起到了断言的作用。如果我们使用了这种Annotation在一个没有覆盖父类方法的方法时,java编译器将以一个编译错误来警示。这个annotaton常常在我们试图覆盖父类方法而确又写错了方法名时发挥威力。使用方法极其简单:在使用此annotation时只要在被修饰的方法前面加上@Override即可
同样Deprecated也是一个标记注解。
当一个类型或者类型成员使用@Deprecated修饰的话,编译器将不鼓励使用这个被标注的程序元素。而且这种修饰具有一定的 “延续性”:如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员,虽然继承或者覆盖后的类型或者成员并不是被声明为 @Deprecated,但编译器仍然要报警
值得注意,@Deprecated这个annotation类型和javadoc中的 @deprecated这个tag是有区别的:前者是java编译器识别的,而后者是被javadoc工具所识别用来生成文档(包含程序成员为什么已经过 时、它应当如何被禁止或者替代的描述)。
在java5.0,java编译器仍然象其从前版本那样寻找@deprecated这个javadoc tag,并使用它们产生警告信息。但是这种状况将在后续版本中改变,我们应在现在就开始使用@Deprecated来修饰过时的方法而不是 @deprecated javadoc tag。
@SuppressWarnings 被用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告。在java5.0,sun提供的javac编译器为我们提供了-Xlint选项来使编译器对合法的程序代码提出警告,此种警告从某种程度上代表了程序错误。例如当我们使用一个generic collection类而又没有提供它的类型时,编译器将提示出”unchecked warning”的警告。通常当这种情况发生时,我们就需要查找引起警告的代码。如果它真的表示错误,我们就需要纠正它。例如如果警告信息表明我们代码中的switch语句没有覆盖所有可能的case,那么我们就应增加一个默认的case来避免这种警告。
有时我们无法避免这种警告,例如,我们使用必须和非generic的旧代码交互的generic collection类时,我们不能避免这个unchecked warning。此时@SuppressWarning就要派上用场了,在调用的方法前增加@SuppressWarnings修饰,告诉编译器停止对此方法的警告。
SuppressWarning不是一个标记注解。它有一个类型为String[]的成员,这个成员的值为被禁止的警告名。对于javac编译器来讲,被-Xlint选项有效的警告 名也同样对@SuppressWarings有效,同时编译器忽略掉无法识别的警告名。
annotation语法允许在annotation名后跟括号,括号中是使用逗号分割的name=value对用于为annotation的成员赋值。