注解(也被称为元数据),为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便的使用这些数据。(通过反射的方式)
java中已经有的注解:
1 @override:表示当前的方法将覆盖超类中的方法(重写方法)
2 @Deprecated:表示已经废弃的方法,使用就会报错
3 @SuppressWarnings:关闭编译器警告信息
基本语法
注解的定义看起来像接口的定义,并且也会被编译称为class文件
@Target(ElementType.METHOD)//表示可以标注的位置信息,此处为方法前@Retention(RetentionPolicy.RUNTIME)//表示运行时可以获取,通过反射可以获取public @interface Test {}
public class Testable {public void execute(){System.out.println("Executing---");}@Test void test(){execute();}public static void main(String[] args) {final Testable testable = new Testable();testable.test();}}
@Target:用来定义注解放在什么地方(比如放在一个方法,或者是一个域)
@Retention:用来定义注解应用在哪一个级别(SOURCE表示在源代码中,CLASS表示在类文件中,RUNTIME,在运行时)
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface UseCase {public int id();public String description() default "no description";}
id和desctrition的定义类似于方法,但是description元素拥有一个default值,如果在注解某个方法时内有给出description的值,此时就会使用default值
public class PasswordUtils {@UseCase(id = 47, description = "密码必须至少有一位")public boolean validatePassword(String password) {return (password.matches("\\w*\\d\\w*"));}@UseCase(id = 48)//在这里并没有给出description的值,所以处理时会使用该元素的默认值public String encryptPassword(String password) {return new StringBuilder(password).reverse().toString();//}@UseCase(id = 49, description = "不能够使用旧密码")public boolean checkForNewPassword(List<String> prePassword,String newPassword) {return !prePassword.contains(newPassword);}}
元注解
元注解专职负责其他的注解:
@Retention:SOURCE:被修饰的类,被编译后,丢弃该注解。CLASS:通过反射获取不到
编写注解处理器
在使用注解的过程中,很重要的一点就是使用注解处理器
public class UseCaseTracker {public static void trackUseCases(List<Integer> useCases, Class<?> cl) {for (Method m : cl.getDeclaredMethods()) {//获取cl的所有的方法UseCase uc = m.getAnnotation(UseCase.class);//返回指定类型的注解对象if (uc != null){System.out.println("Fount Use Case: " + uc.id() + " " + uc.description());useCases.remove(new Integer(uc.id()));}}for (Integer i : useCases) {System.out.println("Not Fount Use Case " + i);}}public static void main(String[] args) {List<Integer> useCases = new ArrayList<>();Collections.addAll(useCases, 45, 46, 47, 48, 49, 50);System.out.println(useCases);trackUseCases(useCases, PasswordUtils.class);}}
注解元素
注解元素可用的类型:所有的基本类型,String,Class,enum,Annotation还有以上类型的数组。
但是不允许使用任何的包装器类型。注解也可以作为元素的类型,也就是说注解可以嵌套
默认值限制
元素不能有不确定的值,也就是说,元素必须要么具有默认值,要么在时用注解的时候提供元素的值
生成外部文件
如果希望提供一些基本的的对象/关系映射功能,能够自动生成数据库,用以存储对象,如果使用注解的话,就可以将所有的信息都保存在源文件中,为此我们需要一些新的注解,用来定义相关联的数据库表的名字,以及与属性相关联的列的名字和SQL类型。
注解的定义:
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface DBTable {public String name() default "";}
在@Target注解中指定的每一个ElementType就是一个约束,它告诉编译器,这个自定义的注解只能应用于该类型
@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface Constrains {boolean primaryKey() default false;boolean allowNull() default true;boolean unique() default false;}@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface SQLString {int value() default 0;String name() default "";Constrains constrains() default @Constrains;}@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface SQLInteger {String name() default "";Constrains constrains() default @Constrains;}
注解处理器通过@Constraints注解提取出数据库表的元数据,虽然对于数据库所能提供的所有约束而言,@Constraints注解值表示了它的一个很小的子集,primaryKey(),allowNull(),unique(),元素明智的提供了默认值
这些SQL类型具有name()元素和constraints()元素,后者利用了嵌套注解的功能,constraints()元素的默认值时@Constraints,由于在@Constraints注解类型之后,没有在括号中指明@Constraints中元素的值,因此constraints()元素的默认值实际上就是一个所有元素都为默认值的@Constraint注解
@DBTable(name = "MEMBER")public class Member {@SQLString(30)String firstName;@SQLString(50)String lastName;@SQLIntegerInteger age;@SQLString(value = 30, constrains = @Constrains(primaryKey = true)) String Handle;static int memberCount;public String getFirstName() {return firstName;}public String getLastName() {return lastName;}public Integer getAge() {return age;}public String getHandle() {return Handle;}}
这些注解第一:都使用了嵌入的@Constraint注解的默认值;第二:他们都是用了快捷方式(如果在注解中定义了名字为value的元素,并且在应用该注解的时候,如果该元素时唯一需要赋值的一个元素,那么此时无需使用名-值对的这种语法,只需要在括号内给出value元素所需要的值就可以了)
直接不支持继承
java中的注解时不可以使用extands关键字来继承某个@interface的;
