一、什么是注释
说起注释,得先提一提什么是元数据(metadata)。所谓元数据就是数据的数据。也就是说,元数据是描述数据的。就象数据表中的字段一样,每个字段描述了这个字段下的数据的含义。而J2SE5.0中提供的注释就是java源代码的元数据,也就是说注释是描述java源代码的。在J2SE5.0中可以自定义注释。使用时在@后面跟注释的名字。
二、J2SE5.0中预定义的注释
在J2SE5.0的java.lang包中预定义了三个注释。它们是Override、Deprecated和SuppressWarnings。下面分别解释它们的含义。
1.Override注释:仅用于方法(不可用于类、包的生命或其他),指明注释的方法将覆盖超类中的方法(如果覆盖父类的方法而没有注
释就无法编译该类),注释还能确保注释父类方法的拼写是正确(错误的编写,编译器不认为是子类的新方法,而会报错)
2.@Deprecated注释:对不应再使用的方法进行注释,与正在声明为过时的方法放在同一行。使用被 Deprecated注释的方法,编译器会
提示方法过时警告(”Warring”)
3.@SuppressWarnings注释:单一注释,可以通过数组提供变量,变量值指明要阻止的特定类型警告(忽略某些警告)。数组中的变量指明要阻止的警告@SuppressWarnings(value={”unchecked”,”fallthrough”}))
三、自定义注释@interface
@interface:注释声明,定义注释类型(与默认的Override等三种注释类型类似)。请看下面实例:
注释类1:
package a.test;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FirstAnno {
String value() default "FirstAnno";
}
注释类2:
package a.test;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SecondAnnotation {
// 注释中含有两个参数
String name() default "Hrmzone";
String url() default "hrmzone.cn";
}
注释类3:
package a.test;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Kitto {
String value() default "kitto";
}
使用类:
package a.test;
@FirstAnno("http://hrmzone.cn")
public class Anno {
@Kitto("测试")
private String test = "";
// 不赋值注释中的参数,使用默认参数
@SecondAnnotation()
public String getDefault() {
return "get default Annotation";
}
@SecondAnnotation(name="desktophrm",url="desktophrm.com")
public String getDefine() {
return "get define Annotation";
}
}
测试类:
package a.test;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class AnnoTest {
public static void main(String[] args) throws ClassNotFoundException {
// 要使用到反射中的相关内容
Class c = Class.forName("a.test.Anno");
Method[] method = c.getMethods();
boolean flag = c.isAnnotationPresent(FirstAnno.class);
if (flag) {
FirstAnno first = (FirstAnno) c.getAnnotation(FirstAnno.class);
System.out.println("First Annotation:" + first.value() + "\n");
}
List<Method> list = new ArrayList<Method>();
for (int i = 0; i < method.length; i++) {
list.add(method[i]);
}
for (Method m : list) {
SecondAnnotation anno = m.getAnnotation(SecondAnnotation.class);
if(anno == null)
continue;
System.out.println("second annotation's\nname:\t" + anno.name()
+ "\nurl:\t" + anno.url());
}
List<Field> fieldList = new ArrayList<Field>();
for(Field f : c.getDeclaredFields()){//访问所有字段
Kitto k = f.getAnnotation(Kitto.class);
System.out.println("----kitto anno: " + k.value());
}
}
}
结合源文件中注释,想必对注释的应用有所了解。下面深入了解。
深入注释:
@Target:指定程序元定义的注释所使用的地方,它使用了另一个类:ElementType,是一个枚举类定义了注释类型可以应用到不同的程序元素以免使用者误用。看看java.lang.annotation 下的源代码:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
ElementType是一个枚举类型,指明注释可以使用的地方,看看ElementType类:
public enum ElementType {
TYPE, // 指定适用点为 class, interface, enum
FIELD, // 指定适用点为 field
METHOD, // 指定适用点为 method
PARAMETER, // 指定适用点为 method 的 parameter
CONSTRUCTOR, // 指定适用点为 constructor
LOCAL_VARIABLE, // 指定使用点为 局部变量
ANNOTATION_TYPE, //指定适用点为 annotation 类型
PACKAGE // 指定适用点为 package
}
@Retention:这个元注释和java编译器处理注释的注释类型方式相关,告诉编译器在处理自定义注释类型的几种不同的选择,需要使用RetentionPolicy枚举类。此枚举类只有一个成员变量,可以不用指明成名名称而赋值,看Retention的源代码:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
类中有个RetentionPolicy类,也是一个枚举类,具体看代码:
public enum RetentionPolicy {
SOURCE, // 编译器处理完Annotation后不存储在class中
CLASS, // 编译器把Annotation存储在class中,这是默认值
RUNTIME // 编译器把Annotation存储在class中,可以由虚拟机读取,反射需要
}
@Documented:是一个标记注释,表示注释应该出现在类的javadoc中,因为在默认情况下注释时不包括在javadoc中的。
所以如果花费了大量的时间定义一个注释类型,并想描述注释类型的作用,可以使用它。
注意他与@Retention(RetentionPolicy.RUNTIME)配合使用,因为只有将注释保留在编译后的类文件中由虚拟机加载,
然后javadoc才能将其抽取出来添加至javadoc中。
@Inherited:将注释同样继承至使用了该注释类型的方法中(表达有点问题,就是如果一个方法使用了的注释用了@inherited,
那么其子类的该方法同样继承了该注释)
注意事项:
1.所有的Annotation自动继承java.lang.annotation接口
2.自定义注释的成员变量访问类型只能是public、default;(所有的都能访问,源作者没用到函数:getDeclaredFields而已)
3.成员变量的只能使用基本类型(byte、short、int、char、long、double、float、boolean和String、Enum、Class、annotations以及该类型的数据)(没有限制,大家可以修改测试一下,就清楚)
4.如果只有一个成员变量,最好将参数名称设为value,赋值时不用制定名称而直接赋值
5.在实际应用中,还可以使用注释读取和设置Bean中的变量。
@interface是用来自定义JAVA Annotation的语法,普通的开发人员可能很少用到它,但是它的功能很强大,本文将具体介绍@interface的用法!
@interface是用来自定义注释类型的,如果你不了解Java注释,可以参阅上一篇文章:"JDK5.0注释(Annotation)的用法"。
一般的应用程序开发人员可能从不需要定义一个注释类型,但定义我们自己的注释类型并不复杂。注释类型的定义跟定义一个接口相似,我们需要在 interface这个关键字前面加上一个@符号,即@interface。注释中的每一个方法定义了这个注释类型的一个元素,注释中方法的声明中一定不能包含参数,也不能抛出异 常;方法的返回值被限制为简单类型、String、Class、emnus、注释,和这些类型的数组。方法可以有一个缺省值。这里是一个注释类型定义的例 子:
/**
* Describes the Request-For-Enhancement(RFE) that led
* to the presence of the annotated API element.
*/
public @interface RequestForEnhancement {
int id();
String synopsis();
String engineer() default "[unassigned]";
String date(); default "[unimplemented]";
}
一旦定义好了一个注释类型,你就可以用来作注释声明。注释一中特殊的修饰符,在其他修饰符(比如public,static,或者final等) 使用地方都可以使用。按照惯例,注释应该放在其他修饰符的前面。注释的声明用@符号后面跟上这个注释类型的名字,再后面跟上括号,括号中列出这个注释中元 素/方法的key-value对。值必须是常量。这里是一个例子,使用上面定义的注释类型:
@RequestForEnhancement(
id = 2868724,
synopsis = "Enable time-travel",
engineer = "Mr. Peabody",
date = "4/1/3007"
)
public static void travelThroughTime(Date destination) { ... }
没有元素/方法的注释被成为标记(marker)注释类型,例如
/**
* Indicates that the specification of the annotated API element
* is preliminary and subject to change.
*/
public @interface Preliminary { }
标记注释在使用的时候,其后面的括号可以省略,例如
@Preliminary public class TimeTravel { ... }
如果注释中仅包含一个元素,这个元素的名字应该为value,例如:
/**
* Associates a copyright notice with the annotated API element.
*/
public @interface Copyright { String value(); }
如果元素的名字为value,使用这个注释的时候,元素的名字和等号可以省略,如:
@Copyright("2002 Yoyodyne Propulsion Systems")
public class OscillationOverthruster { ... }
为了将上面提到的东西结合在一起,我们创建了一个简单的基于注释的测试框架。首先我们需要一个标记注释类型用以说明一个方法是一个测试方法,并被测试工具执行。
import java.lang.annotation.*;
/**
* Indicates that the annotated method is a test method.
* This annotation should be used only on parameterless static methods.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test { }
我们可以注意到这个注释类型本省也被注释了,这种注释叫做元注释。第一注释 (@Retention(RetentionPolicy.RUNTIME))表示这种类型的注释被VM保留从而使其能够通过反射在运行时读取;第二个注 释@Target(ElementType.METHOD)表示这种注释只能用来注释方法。
下面是一个简单的类,其中的几个方法被加了上面的注释:
public class Foo {
@Test public static void m1() { }
public static void m2() { }
@Test public static void m3() {
throw new RuntimeException("Boom");
}
public static void m4() { }
@Test public static void m5() { }
public static void m6() { }
@Test public static void m7() {
throw new RuntimeException("Crash");
}
public static void m8() { }
}
这里是测试工具:
import java.lang.reflect.*;
public class RunTests {
public static void main(String[] args) throws Exception {
int passed = 0, failed = 0;
for (Method m : Class.forName(args[0]).getMethods()) {
if (m.isAnnotationPresent(Test.class)) {
try {
m.invoke(null);
passed++;
} catch (Throwable ex) {
System.out.printf("Test %s failed: %s %n", m, ex.getCause());
failed++;
}
}
}
System.out.printf("Passed: %d, Failed %d%n", passed, failed);
}
}
这个工具用一个类名作为参数,遍历这个类中的所有方法,并调用其中被加了@Test注释的方法。如果一个方法抛出了一个异常,那么这个测试就失败了,最终的测试结果被打印了出来。下面是程序运行的结果:
$ java RunTests Foo
Test public static void Foo.m3() failed: java.lang.RuntimeException: Boom
Test public static void Foo.m7() failed: java.lang.RuntimeException: Crash
Passed: 2, Failed 2
虽然这个测试工具只是一个玩具,但他显示了注释的强大的功能。
下面是另一种介绍:
java用 @interface Annotation{ } 定义一个注解 @Annotation,一个注解是一个类。
@Override,@Deprecated,@SuppressWarnings为常见的3个注解。
注解相当于一种标记,在程序中加上了注解就等于为程序加上了某种标记,以后,
JAVAC编译器,开发工具和其他程序可以用反射来了解你的类以及各种元素上有无任何标记,看你有什么标记,就去干相应的事。
注解@Override用在方法上,当我们想重写一个方法时,在方法上加@Override,当我们方法
的名字出错时,编译器就会报错,如图:
注解@Deprecated,用来表示某个类的属性或方法已经过时,不想别人再用时,在属性和方法
上用@Deprecated修饰,如图:
注解@SuppressWarnings用来压制程序中出来的警告,比如在没有用泛型或是方法已经过时的时候,
如图:
![](http://hi.csdn.net/attachment/201111/3/0_1320341021mJWH.gif)
注解@Retention可以用来修饰注解,是注解的注解,称为元注解。
Retention注解有一个属性value,是RetentionPolicy类型的,Enum RetentionPolicy是一个枚举类型,
这个枚举决定了Retention注解应该如何去保持,也可理解为Rentention 搭配 RententionPolicy使用。RetentionPolicy有3个值:CLASS RUNTIME SOURCE
用@Retention(RetentionPolicy.CLASS)修饰的注解,表示注解的信息被保留在class文件(字节码文件)中当程序编译时,但不会被虚拟机读取在运行的时候;
用@Retention(RetentionPolicy.SOURCE )修饰的注解,表示注解的信息会被编译器抛弃,不会留在class文件中,注解的信息只会留在源文件中;
用@Retention(RetentionPolicy.RUNTIME )修饰的注解,表示注解的信息被保留在class文件(字节码文件)中当程序编译时,会被虚拟机保留在运行时,
所以他们可以用反射的方式读取。RetentionPolicy.RUNTIME 可以让你从JVM中读取Annotation注解的信息,以便在分析程序的时候使用.
- package com.self;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- @Retention(RetentionPolicy.RUNTIME)
- public @interface MyTarget
- { }
- 定义个一注解@MyTarget,用RetentionPolicy.RUNTIME修饰;
- package com.self;
- import java.lang.reflect.Method;
- public class MyTargetTest
- {
- @MyTarget
- public void doSomething()
- {
- System.out.println("hello world");
- }
- public static void main(String[] args) throws Exception
- {
- Method method = MyTargetTest.class.getMethod("doSomething",null);
- if(method.isAnnotationPresent(MyTarget.class))//如果doSomething方法上存在注解@MyTarget,则为true
- {
- System.out.println(method.getAnnotation(MyTarget.class));
- }
- }
- }
- 上面程序打印:@com.self.MyTarget(),如果RetentionPolicy值不为RUNTIME,则不打印。
- @Retention(RetentionPolicy.SOURCE )
- public @interface Override
- @Retention(RetentionPolicy.SOURCE )
- public @interface SuppressWarnings
- @Retention(RetentionPolicy.RUNTIME )
- public @interface Deprecated
- 由上可以看出,只有注解@Deprecated在运行时可以被JVM读取到
- 注解中可以定义属性,看例子:
- @Retention(RetentionPolicy.RUNTIME)
- public @interface MyAnnotation
- {
- String hello() default "gege";
- String world();
- int[] array() default { 2, 4, 5, 6 };
- EnumTest.TrafficLamp lamp() ;
- TestAnnotation lannotation() default @TestAnnotation(value = "ddd");
- Class style() default String.class;
- }
- 上面程序中,定义一个注解@MyAnnotation,定义了6个属性,他们的名字为:
- hello,world,array,lamp,lannotation,style.
- 属性hello类型为String,默认值为gege
- 属性world类型为String,没有默认值
- 属性array类型为数组,默认值为2,4,5,6
- 属性lamp类型为一个枚举,没有默认值
- 属性lannotation类型为注解,默认值为@TestAnnotation,注解里的属性是注解
- 属性style类型为Class,默认值为String类型的Class类型
- 看下面例子:定义了一个MyTest类,用注解@MyAnnotation修饰,注解@MyAnnotation定义的属性都赋了值
- @MyAnnotation(hello = "beijing", world="shanghai",array={},lamp=TrafficLamp.RED,style=int.class)
- public class MyTest
- {
- @MyAnnotation(lannotation=@TestAnnotation(value="baby"), world = "shanghai",array={1,2,3},lamp=TrafficLamp.YELLOW)
- @Deprecated
- @SuppressWarnings("")
- public void output()
- {
- System.out.println("output something!");
- }
- }
- 接着通过反射读取注解的信息:
- public class MyReflection
- {
- public static void main(String[] args) throws Exception
- {
- MyTest myTest = new MyTest();
- Class<MyTest> c = MyTest.class;
- Method method = c.getMethod("output", new Class[] {});
- //如果MyTest类名上有注解@MyAnnotation修饰,则为true
- if(MyTest.class.isAnnotationPresent(MyAnnotation.class))
- {
- System.out.println("have annotation");
- }
- if (method.isAnnotationPresent(MyAnnotation.class))
- {
- method.invoke(myTest, null); //调用output方法
- //获取方法上注解@MyAnnotation的信息
- MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
- String hello = myAnnotation.hello();
- String world = myAnnotation.world();
- System.out.println(hello + ", " + world);//打印属性hello和world的值
- System.out.println(myAnnotation.array().length);//打印属性array数组的长度
- System.out.println(myAnnotation.lannotation().value()); //打印属性lannotation的值
- System.out.println(myAnnotation.style());
- }
- //得到output方法上的所有注解,当然是被RetentionPolicy.RUNTIME修饰的
- Annotation[] annotations = method.getAnnotations();
- for (Annotation annotation : annotations)
- {
- System.out.println(annotation.annotationType().getName());
- }
- }
- }
- 上面程序打印:
- have annotation
- output something!
- gege, shanghai
- 3
- baby
- class java.lang.String
- com.heima.annotation.MyAnnotation
- java.lang.Deprecated
- 如果注解中有一个属性名字叫value,则在应用时可以省略属性名字不写。
- 可见,@Retention(RetentionPolicy.RUNTIME )注解中,RetentionPolicy.RUNTIME是注解属性值,属性名字是value,
- 属性的返回类型是RetentionPolicy,如下:
- public @interface MyTarget
- {
- String value();
- }
- 可以这样用:
- @MyTarget("aaa")
- public void doSomething()
- {
- System.out.println("hello world");
- }
- 注解@Target也是用来修饰注解的元注解,它有一个属性ElementType也是枚举类型,
- 值为:ANNOTATION_TYPE CONSTRUCTOR FIELD LOCAL_VARIABLE METHOD PACKAGE PARAMETER TYPE
- 如@Target(ElementType.METHOD) 修饰的注解表示该注解只能用来修饰在方法上。
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface MyTarget
- {
- String value() default "hahaha";
- }
- 如把@MyTarget修饰在类上,则程序报错,如:
- @MyTarget
- public class MyTargetTest
- 注解大都用在开发框架中吧,好了有关注解就学习那么多了,谢谢。