一.java内置的七个注解
三个标准注解
- @Override : 表示当前的方法定义将覆盖超类中的方法。
- @Deprecated : 如果程序员使用了注解为它的元素,那么编译器会发出警告信息。
- @SuppressWarnings : 关闭不当的编译器警告信息
四个元注解:元注解专职负责注解其他的注解
1.@Target : 表示该注解可以用于什么地方。接受参数包括:
ElementType.CONSTRUCTOR : 构造器的声明
ElementType.FIELD : 域声明(包括enum实例)
ElementType.LOCAL_VARIABLE : 局部变量声明
ElementType.METHOD : 方法声明
ElementType.PACKAGE : 包声明
ElementType.PARAMETER : 参数声明
ElementType.TYPE : 类、接口(包括注解类型) 或 enum声明
ElementType本质上就是个枚举(enum)
如果@Target要指定多个值,以“,”分隔,用法如下:
@Target({ElementType.TYPE,ElementType.METHOD})
如果注解应用于所有类型,那么不加@Target,即可
2.@Retention : 表示需要在什么级别保存该注解信息。接受参数包括:
RetentionPolicy.SOURCE : 注解将被编译器丢弃。
RetentionPolicy.CLASS : 注解在class文件中可用,但会被VM丢弃。
RetentionPolicy.RUNTIME : VM将在运行期也保留注解,因此可以通过反射机制读取注解的信息
3.@Documented : 将此注解包含在Javadoc中。
4.@Inherited : 允许子类继承父类中的注解
二.基本语法
1.定义注解
注解的定义看起来像接口的定义。事实上,与其他任何java接口一样,直接也将会编译成class文件
package annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD) //这个注解作用于方法上
@Retention(RetentionPolicy.RUNTIME) //这个注解作用于运行时
public @interface UseCase {
public int id();
public String description() default "no description";
// default 如果在注解某个方法时没有给出description的值,则该注解的处理器就会使用此元素的默认值
}
注解元素说明:
注解@UseCase由UseCase.java定义,其中包含int元素id,以及一个String元素description。
注解元素可用的类型如下所示:
*所有基本类型:int,float,boolean等
*String
*Class
*enum
*Annotation
*以上类型的数组
如果你使用了其他类型,那么编译器就会报错。注意,也不允许使用任何包装类型,不过由于自动装箱的存在,这算不是什么限制。注解也可以作为元素的类型,也就是说注解可以嵌套。
2.注解的使用
package annotations;
import java.util.List;
public class PasswordUtils {
@UseCase(id = 47, description = "Passwords must contain at least on numeric")
public boolean validatePassword(String password) {
return (password.matches("\\w*\\d\\w*"));
}
@UseCase(id = 48)
public String encryptPassword(String 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);
}
}
3.编写注解处理器
如果没有用来读取注解的工具,那么注解也不会比注释更有用。使用注解的过程中,很重要的一部分就是创建与使用注解处理器。java SE5扩展了反射机制的API,以帮助程序员构造这类工具。
下面是一个非常简单的注解处理器,并使用反射机制查找 @UseCase标记。
package annotations;//: annotations/UseCaseTracker.java
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class UseCaseTracker {
public static void
trackUseCases(List<Integer> useCases, Class<?> cl) {
for (Method m : cl.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 ArrayList<Integer>();
Collections.addAll(useCases, 47, 48, 49, 50);
trackUseCases(useCases, PasswordUtils.class);
}
}
/* Output:
Found Use Case:47 Passwords must contain at least one numeric
Found Use Case:48 no description
Found Use Case:49 New passwords can't equal previously used ones
Warning: Missing use case-50
*///:~
三.说明
1.编译器对元素的默认值有些过分挑剔。首先,元素不能有不确定的值。也就是说,元素必须要么有默认值,要么在使用注解时提供元素的值。
其次,对于非基本类型的元素,无论是在源代码中声明时,或是在注解接口中定义默认值时,都不能以null作为其值。这个约束使得处理器很难表现一个元素的存在或缺失的状态,因为在每个注解的声明中,所有的元素都存在,并且都具有相应的值。为了绕开这个约束,我们只能自己定义一些特殊的值,例如空字符串或负数,以此表示某个元素不存在
eg:
package annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SimulatingNull {
public int id() default -1;
public String description() default "";
}
2.当注解元素为value时,使用注解可以直接传值