java中的注脚
注解
注解是为程序元素提供元数据的表示方法。
系统注解
Java 5SE内置了3种标准注解和4种元注解,其中元注解专门负责新注解的创建,我们下面会讲到。先来看看3种内置注解
-
@Overview
用于检测覆盖超类中的方法的准确性,如果方法签名或者参数与超类中的发方法不一致的话就会发出错误提示。 -
@Deprecate
为不推荐使用的方法使用该注解,当程序员使用了该方法时,编译器就会发出警告。 -
@SuppressWarnings
忽略编译器警告.
元注解(meta-annotation)
元注解的作用就是用来注解其他新的注解,Java SE5 中定义了4中元注解,可以在java.lang.annotation包种找到:
-
@Target
表示注解可以用在什么地方,可能得ElementType参数取值包括:- CONSTRUCTOR:构造器声明
- FIELD:域声明,包括enum
- LOCAL_VARIABLE:局部变量声明
- METHOD:方法声明
- PACKAGE:包声明
- PARAMETER:参数声明
- TYPE:类、接口(包括注解类型)或enum声明
-
@Retention
表示在什么级别保存该注解信息。可选的RetentionPolicy参数包括:- SOURCE:注解只在源文件中有效,编译时忽略该注解
- CLASS:在注解在编译时有效,在程序运行时将被忽略
- RUNTIME:在运行时有效,因此可以通过反射机制读取注解的信息
-
@Documented
将此注解包含在javadoc中 -
@Interited
允许子类继承父类中的注解
定义注解
(1)使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。
(2)在定义注解时,不能继承其他的注解或接口。
(3)@interface用来声明一个注解,接口定义中的每个方法名对应注解的参数名称,方法的返回值类型对应参数的类型(返回值类型只能是_基本类型、Class、String、enum)。可以通过default来声明参数的默认值。
(4)注解参数支持的数据类型:
- 所有基本类型(int,float,boolean,byte,double,char,long,short)
- String类型
- Class类型
- enum类型
- Annotation类型
- 以上所有类型的数组
(5)只能用public或者默认(default)这两个访问权修饰接口方法 (6)如果只有一个接口方法,最好把该方法取名为value()
一个自定义注解的例子
为一个javabean中的各个元素添加相关注解,将其映射成为数据库中的一张表。用类名表示数据库中的表名,用属性名做为数据库字段名,并定义相关的主键、是否为空等注解。
(1)首先我们定义一个用来描述数据库表名的注解,该注解用在类名之上。
@Target(ElementType.TYPE) //该注解只能应用于类之上
@Retention(RetentionPolicy.RUNTIME) //在运行时获取注解信息
public @interface DBTable {
public String name() default ""; //定义数据库表名
}
(2)定义为修饰javabean域上的注解:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraints {
public boolean primaryKey() default false; //是否为主键
public boolean allowNull() default true; //是否为空
public 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; //主键约束等
}
(3)应用注解
@DBTable(name="TMember")
public class Member {
@SQLInteger(constraints=@Constraints(primaryKey = true)) //嵌套注解
Integer id;
@SQLString(50)
String name;
@SQLInteger
Integer age;
//getter and setter...
}
注解处理器
如果没有用来读取注解的方法和工作,那么注解也就不会比注释更有用处了。使用注解的过程中,很重要的一部分就是创建于使用注解处理器。
我们上面提到过,只有当注解的保存级别为RUNTIME级别时,才能在运行时被识别。AnnotatedElement
接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的如下四个个方法来访问Annotation信息:
-
<T extends Annotation> T getAnnotation(Class<T> annotationClass)
返回改程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。 -
Annotation[] getAnnotations()
返回该程序元素上存在的所有注解。 -
boolean isAnnotationPresent(Class<?extends Annotation> annotationClass)
判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false. -
Annotation[] getDeclaredAnnotations()
返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响.
下面实现一个注解处理器,读取上面已被注解过的Menber类,检测其上的数据库注解,生成用来创建数据库表的SQL脚本
public class TableCreator {
public static void main(String[] args) {
String[] classes = new String[]{"annotationtest.Member"};
for(String className : classes){
Class<?> cl = null;
try {
cl = Class.forName(className);
} catch (ClassNotFoundException e) {
System.err.println("class "+className+" has not found!");
continue;
}
DBTable dbTable = (DBTable)cl.getAnnotation(DBTable.class);
if (dbTable == null){
System.err.println("No DBTable annotation in class "+className);
continue;
}
String tableName = dbTable.name();
if (tableName.length()<1){
tableName = cl.getName().toUpperCase();
}
List<String> coloumDefs = new ArrayList<String>();
for(Field field : cl.getDeclaredFields()){
String coloumName = null;
//field.isAnnotationPresent(SQLString.class);//判断该属性上是否存在该注解
//Annotation[] anns = field.getAnnotations(); //获取属性域上的注解,包含继承注解
Annotation[] anns = field.getDeclaredAnnotations();//获取属性域上的注解,不包含继承注解
if(anns.length < 1){
continue;
}
if(anns[0] instanceof SQLInteger){
SQLInteger sInt = (SQLInteger)anns[0];
if (sInt.name().length() > 1) {
coloumName = sInt.name();
}else{
coloumName = field.getName().toUpperCase();
}
coloumDefs.add(coloumName + " INT" + getConstraints(sInt.constraints()));
}
if(anns[0] instanceof SQLString){
SQLString sString = (SQLString)anns[0];
if(sString.name().length() > 1){
coloumName = sString.name();
}else{
coloumName = field.getName().toUpperCase();
}
coloumDefs.add(coloumName+" VARCHAR("+sString.value()+")"+ getConstraints(sString.constraints()));
}
}
StringBuilder sql = new StringBuilder(
"CREATE TABLE " + tableName +"(");
for(String coloumDef : coloumDefs){
sql.append("\n " + coloumDef + ",") ;
}
String tableCreate = sql.substring(0,sql.length()-1) + ");";
System.out.println(tableCreate);
}
}
private static String getConstraints(Constraints con){
String constraints = "";
constraints += con.allowNull() ? "" : " NOT NULL ";
constraints += con.primaryKey() ? " PRIMARY KEY " : "";
constraints += con.unique() ? " UNIIQUE " : "";
return constraints;
}
}
输出:
CREATE TABLE TMember(
ID INT PRIMARY KEY ,
NAME VARCHAR(50),
AGE INT);