注解(也被称为 元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便地使用这些数据。
2、标准注解与元注解
Java内置了三种标准注解和四种元注解
3种标准注解(定义在java.lang中):
@Override 表示当前的方法定义将覆盖超类中的方法。如果你不小心拼写错误,或者方法签名对不上覆盖的方法,编译器就会发出错误提示。
@Deprecated 如果程序员使用了注解为它的元素,那么编译器会发出警告信息。
@SuppressWarning 关闭当前的编译器警告信息。
4种元注解(元注解专职负责注解其他的注解)
@Target 表示该注解可以用于什么地方。可能的ElementType参数包括:
CONSTRUCTOR:构造器的声明
FIELD:域声明(包括enum实例)
LOCAL_VARIABLE:局部变量声明
METHOD:方法声明
PACKAGE:包声明
PARAMETER:参数声明
TYPE:类、接口(包括注解类型)或enum声明
@Retention 表示需要在什么级别保存该注解信息。可选的RetentionPolicy参数包括:
SOURCE:注解将被编译器丢弃
CLASS:注解在class文件中可用,但会被VM丢弃
RUNTIME:VM将在运行期也保留注解,因此可以通过反射机制读取注解的信息
@Document 将此注解包含在Javadoc中
@Inherited 允许子类继承父类中的注解
3、自定义一个注解
自定义一个简单的注解(没有元素的注解称为标记注解)
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 Test {}
自定义一个包含元素的注解
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";
}
4、使用自定义注解
import java.util.List;
import com.zhangmingji.annotation.UseCase;
public class PasswordUtils {
@UseCase(id=47, description="password must contain at least one 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> prePasswords, String password){
return !prePasswords.contains(password);
}
}
5、编写注解处理器
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.zhangmingji.annotation.UseCase;
import com.zhangmingji.use.PasswordUtils;
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);
}
}
运行结果:
Found Use Case:47 password 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
6、注解元素
在前面我们已经定义了一个包含元素id、description的注解,
注解元素可用的类型如下:
所有基本类型(int,float,boolean等)
String
Class
enum
Annotation
以上类型的数组
如果你使用了其他类型,那么编译器就会报错;也不允许使用任何包装类型;注解也可以作为基本元素类型,也就是说注解可以嵌套
7、默认值限制
(1)、元素不能有不确定的值。也就是说,元素必须要么具有默认值,要么使用注解时提供元素的值。
(2)、对于非基本类型的元素,无论在源代码中声明时,或是在注解接口中定义默认值时,都不能以null作为其值。
(3)、注解不支持继承。
8、通过注解生成建表语句
package com.zhangmingji.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)//类、接口、或者ENUM声明
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {
public String name() default "";
}
package com.zhangmingji.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraints {
boolean primaryKey() default false;
boolean allowNull() default true;
boolean unique() default false;
}
package com.zhangmingji.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString {
int value() default 0;
String name() default "";
Constraints constraints() default @Constraints;
}
package com.zhangmingji.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLInteger {
String name() default "";
Constraints constraints() default @Constraints;
}
使用以上自定义的注解
package com.zhangmingji.use;
import com.zhangmingji.annotation.Constraints;
import com.zhangmingji.annotation.DBTable;
import com.zhangmingji.annotation.SQLInteger;
import com.zhangmingji.annotation.SQLString;
@DBTable(name="MEMBER")
public class Member {
@SQLString(30) String firstName;
@SQLString(50) String lastName;
@SQLInteger Integer age;
@SQLString(value=30, constraints=@Constraints(primaryKey=true))
String handle;
static int memberCount;
public String getHandle(){
return this.handle;
}
public String getFirstName(){
return this.firstName;
}
public String getLastName(){
return this.lastName;
}
public String toString(){
return handle;
}
public Integer getAge(){
return age;
}
}
实现处理器
package com.zhangmingji.process;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import com.zhangmingji.annotation.Constraints;
import com.zhangmingji.annotation.DBTable;
import com.zhangmingji.annotation.SQLInteger;
import com.zhangmingji.annotation.SQLString;
public class TableCreator {
public static void main(String[] args) throws Exception {
if(args.length < 1){
System.out.println("arguments: anotationed classes");
System.exit(0);
}
for(String className : args){
Class<?> cl = Class.forName(className);
DBTable dbTable = cl.getAnnotation(DBTable.class);
if(null == dbTable){
System.out.println("no DBTable anotations in class " + className);
continue;
}
String tableName = dbTable.name();
//如果名字为空,则使用类名
if(tableName.length() < 1){
tableName = cl.getName().toUpperCase();
}
List<String> columDefs = new ArrayList<String>();
for(Field field : cl.getDeclaredFields()){
String columName = 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){
columName = field.getName().toUpperCase();
}else{
columName = sInt.name();
}
columDefs.add(columName + " INT" + getConstraints(sInt.constraints()));
}
if(anns[0] instanceof SQLString){
SQLString sString = (SQLString) anns[0];
if(sString.name().length() < 1){
columName = field.getName().toUpperCase();
}else{
columName = sString.name();
}
columDefs.add(columName + " VARCHAR(" + sString.value() + ")"
+ getConstraints(sString.constraints()));
}
}
StringBuilder createCommand = new StringBuilder("CREATE TABLE " + tableName + "(");
for(String columDef : columDefs){
createCommand.append("\n " + columDef + "," );
}
String tableCreate = createCommand.substring(0, (createCommand.length() - 1)) + ");";
System.out.println("Table create SQL for " + className + " is :\n" + tableCreate);
}
}
/**
* 构造约束
* @param con
* @return
*/
public static String getConstraints(Constraints con){
String contraints = "";
if(!con.allowNull()){
contraints += " NOT NULL";
}
if(con.primaryKey()){
contraints += " PRIMARY KEY";
}
if(con.unique()){
contraints += " UNIQUE";
}
return contraints;
}
}
运行结果:
Table create SQL for com.zhangmingji.use.Member is :
CREATE TABLE MEMBER(
FIRSTNAME VARCHAR(30),
LASTNAME VARCHAR(50),
AGE INT,
HANDLE VARCHAR(30) PRIMARY KEY);
以上内容来自《Java编程思想》第四版,仅作为学习和总结。