注解,或者叫元数据,可以让我们在代码中添加一些java无法表达的信息,而且可以由编译器验证和检查. Java SE5预定义了一些 ,但一般还是需要自己添加新的注解 ,并且按自己的方式使用 .
Java内置注解:
- @Override 表示方法定义会覆盖超类方法,如果有拼写错误或方法签名对不上,编译器会发出错误提示
- @Deprecated 如果使用了该元素 编译器发出警告
- @SuppressWarnings 关闭编译器警告信息
基本语法
使用注解的方式与修饰符基本一样
@Test
public class XXX{
@test
public void testExcue(){
//doSomething
}
}
定义注解
package xx.xx;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test{}
注解定义类似接口,而且注解也会被编译为class文件
定义注解时需要一些元注解
- @Target :定义注解应用于什么地方,如方法或是字段
- @Retention: 定义注解在哪一级别可用,在源代码(source),类文件中(class),或是运行时(Runtime)
- @Documented :将此注解放在javaDoc中
- @Inherited: 允许子类继承父类中的注解
注解中可以包含一些元素表示某些值,当分析处理注解时可以使用,元素类似于接口中的方法,并且可以指定默认值.
标记注解: 没有元素的注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface useCase{
public int id();
public String description() default "no description";
}
编写注解处理器
注解比注释更有用体现在我们可以为注解编写注解处理器.
class PasswordUtils {
@useCase(id = 47 , description = "password mast have a number")
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 password need be different")
public boolean checkForNewPassbwordb(List<String> prePasswords , String password){
return !prePasswords.contains(password);
}
}
public class UseCaseTracker {
public static void main(String[] args){
List<Integer> useCases = new ArrayList<>();
Collections.addAll(useCases,47,48,49,50);
trackUseCases(useCases,PasswordUtils.class);
}
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 :miss use case "+i);
}
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface useCase{
public int id();
public String description() default "no description";
}
这里使用到的方法:
- getDeclaredMethods() :返回方法列表
- getAnnotations() : 返回指定类型的注解对象,如果没有指定类型注解返回null
他们都属于AnnotatedElement
接口 , Class,Method,Field类都实现了该接口
注解元素
可用的注解元素类型如下
- 所有基本类型
- string
- class
- enum
- annotation
- 以上类型的数组
由于注解也是可用类型,因此注解可以嵌套,这将是很有用的特性
默认值限制
编译器限制 , 注解的元素除基本类型之外不能由不确定的值,也就是说要么有默认值要么使用时提供值,其次,定义默认值时不能用null.
因此通常要自己定义-1或者空字符串""表示元素不存在.
注解代替外部文件
有些框架需要一些额外信息才能与代码协同工作 , 在有注解之前 , 普遍使用的时XML , 程序员必须忍受沉闷 , 将在源代码中已经提供过的信息再次提供 . 而且使用外部文件时 , 同一个类将会有两个信息源 , 这往往会导致代码同步问题 , 并且程序员需要同时能够编写Java和描述文件
这里写一个小例子
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {
public String name() default "";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraints {
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 "";
Constraints constraints() default @Constraints;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLInteger {
String name() default "";
Constraints constraints() default @Constraints;
}
注解处理器
public static void main(String[] args) throws ClassNotFoundException {
if(args.length<1){
System.out.println("arguments: annotated classes");
System.exit(0);
}
for(String className : args){
Class<?> cl = Class.forName(className);
DBTable dbTable = cl.getAnnotation(DBTable.class);
if(dbTable == null){
System.out.println("no dbTable annotations in class "+className);
continue;
}
String tableName = dbTable.name();
if(tableName.length()<1){
tableName = cl.getName().toUpperCase();
}
List<String> columnDefs = new ArrayList<>();
for(Field field : cl.getDeclaredFields()){
String columnName = null;
Annotation[] anns = field.getDeclaredAnnotations();
if(anns.length<1)
continue;
if(anns[0] instanceof SQLInteger){
SQLInteger sInt = (SQLInteger)anns[0];
if(sInt.name().length()<1){
columnName = field.getName().toUpperCase();
}
else
columnName = sInt.name();
columnDefs.add(columnName + " INT" +
getConstraints(sInt.constraints()));
}
if(anns[0] instanceof SQLString){
SQLString sstring = (SQLString)anns[0];
if(sstring.name().length()<1){
columnName = field.getName().toUpperCase();
}
else
columnName = sstring.name();
columnDefs.add(columnName + "VARCHAR(" +
sstring.value() +")" +
getConstraints(sstring.constraints()));
}
StringBuilder createCommand = new StringBuilder("CREATE TABLE " +
tableName +"(");
for(String columnDef :columnDefs){
createCommand .append("
" +
columnDef +".");
}
String tableCreate = createCommand.substring(0,createCommand.length()-1)+");";
System.out.println("table creation sql for "+ className + " is :" +
"
" +tableCreate);
}
}
}
private static String getConstraints(Constraints con){
StringBuilder constraints = new StringBuilder();
if(!con.allowNull())
constraints .append( " NOT NULL ");
if(con.primaryKey())
constraints .append(" PRIMARY KEY ");
if(con.unique())
constraints.append(" UNIQUE ");
return constraints.toString();
}
注解不支持继承
注解并不支持继承.