java注解
java注解起到给源代码加上标签的作用,加上和删除对源码没有影响。
外部工具访问标签(注解),然后做了处理。比如IDE识别到@Deprecated,则在代码方法上加上删除线。
识别标签的工具叫APT(Annotation Processing Tool)
元注解
用来给其他注解做注解的注解,一共有6个。
@Retention : 指定被此注解标注的注解保留时长
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
有一个属性value,返回一个RetentionPolicy枚举,有3种类型:
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
* 保留在源码中,编译器编译时删去
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
* 编译器编译到class文件中,在运行时删去
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
* 编译到class文件中,运行时也存在,可以用反射调用
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
@Target : 用于标注该注解可以注解的程序元素
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
有一个value(),返回可以注解的程序元素ElementType数组,包括:
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
// 类、接口(包括注解类型)、enum
TYPE,
/** Field declaration (includes enum constants) */
// 属性(包括enum元素)
FIELD,
/** Method declaration */
//方法
METHOD,
/** Formal parameter declaration */
// 参数
PARAMETER,
/** Constructor declaration */
// 构造方法
CONSTRUCTOR,
/** Local variable declaration */
// 局部变量
LOCAL_VARIABLE,
/** Annotation type declaration */
// 注解类型
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
@Documented : 将被标注的注解生成到javadoc中
@Inherited : 被其修饰的注解有继承能力。
@Inherited
public @interface InAnnotation{}
@InAnnotation
public class Base{}
//该子类即使没有写注解,也自动获得了父类的注解@InAnnotation
public class Son extends Base{}
@Repeatable : 被修饰的注解可以重复的注解某一个程序元素。
@ShuSheng(name="frank" age=20)
@ShuSheng(age=18)
public class AnnotationDemo{}
如何定义一个重复注解,首先需要定义一个容器,然后将其作为参数传入@Repeatable中
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.Type)
public @interface ShuShengs{
ShuShen[] value();
}
@Repeatable(ShuShengs.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ShuShen{
String name() default "ben"
int age();
}
java基本注解
java内置注解有5个。
@Override
编译器检查被标记的方法,保证其重写了父类的方法
@Retention(RetentionPolity.SOURCE)
@Target(ElementType.METHOD)
public @interface Override{
}
@Deprecated
被注释的程序元素被废弃了,不要再使用了
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
@SuppressWarnings
让编译器不要给出警告提示
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
其中,需要传入一个String数组类型的参数,根据传入的数组来确定取消哪些类型的警告。
deprecation:使用了不赞成使用的类或方法时的警告;
unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型;
fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;
path:在类路径、源文件路径等中有不存在的路径时的警告;
serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告;
finally:任何 finally 子句不能正常完成时的警告;
all:关于以上所有情况的警告。
@SafeVarargs
编译器在检测到你使用某些方法参数的使用有问题,会发出警告。使用该注解后,不让编译器警告。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface SafeVarargs {}
可以避免提示heap pollution
@SafeVarargs
private static void method(List<String>... strLists) {
List[] array = strLists;
List<Integer> tempList = new ArrayList<>();
array[0] = tempList; //非法操作,会警告,heap pollution
String s = strLists[0].get(0); //运行时会报错
}
@FunctionalInterface
编译器检查是否是函数接口,即只有一个方法,则可以用lambda表达式。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface Annotation1 {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Annotation2 {
String name() default "hujun";
}
@Annotation2(name = "hj")
public class Member {
@Annotation1
public void test(){
}
}
使用注解
注解不会自己起作用,需要APT,那APT怎么获取注解里的信息呢?用反射。
Class<?> cls = MemberSon.class;
//获取该类上的所有注解
Annotation[] annotations = cls.getAnnotations();
System.out.println( "getAnnotations: " + Arrays.asList(annotations).toString());
//检查是否有某注解
boolean present = cls.isAnnotationPresent(Annotation1.class);
System.out.println("getAnnotation: Annotation1 present :" + present);
//获取重复注解
Annotation1[] annotation1 = cls.getAnnotationsByType(Annotation1.class);
Method[] methods = Member.class.getMethods();
for (Method method: methods) {
System.out.println("method : "+method.getName()+",getAnnotation:"+Arrays.asList(method.getAnnotations()).toString());
}
构建自己的注解APT
利用APT来自动生成SQL的语句。
首先定义数据库相关的注解。
/**
* 注解表
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DBTable {
String name() default "";
}
/**
* 约束注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Constraints {
//是否作为主键
boolean primaryKey() default false;
//是否唯一
boolean unique() default false;
//是否允许为空
boolean allowNull() default false;
}
/**
* 注解String类型的字段
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString {
//对应数据库表的列名
String name() default "";
//列类型分配的长度,如varchar(30)的30
int value() default 0;
Constraints constraint() default @Constraints;
}
/**
* 注解Integer类型的字段
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SQLInteger {
//对应数据库表的列名
String name() default "";
//列类型分配的长度,例如varchar(30)的30
int value() default 0;
//约束条件
Constraints constraint() default @Constraints;
}
然后用自定义的数据库注解来注解要生成的数据库类。
@DBTable(name = "PERSON")
public class Person {
@SQLString(name = "ID",value = 50,constraint = @Constraints(primaryKey = true,unique = true))
private String id;
@SQLString(name = "NAME",value = 30,constraint = @Constraints(unique = true))
private String name;
@SQLInteger(name = "AGE")
private int age;
@SQLString(name = "INFO",value = 150,constraint = @Constraints(allowNull = true))
private String info;
}
其中使用了嵌套注解@Constraint,可以作为注解里的参数。
public class SQLAPT {
public static String createTableSql(String className) throws ClassNotFoundException {
Class<?> cl = Class.forName(className);
DBTable dbTable = cl.getAnnotation(DBTable.class);
//如果没有表注解,直接返回
if(dbTable == null) {
System.out.println(
"No DBTable annotations in class " + className);
return null;
}
String tableName = dbTable.name();
// If the name is empty, use the Class name:
if(tableName.length() < 1) {
tableName = cl.getName().toUpperCase();
}
List<String> columnDefs = new ArrayList<String>();
//通过Class类API获取到所有成员字段
for(Field field : cl.getDeclaredFields()) {
String columnName = null;
//获取字段上的注解
Annotation[] anns = field.getDeclaredAnnotations();
if(anns.length < 1) {
continue; // Not a db table column
}
//判断注解类型
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.constraint()));
}
//判断String类型
if(anns[0] instanceof SQLString) {
SQLString sString = (SQLString) anns[0];
// Use field name if name not specified.
if(sString.name().length() < 1) {
columnName = field.getName().toUpperCase();
} else {
columnName = sString.name();
}
columnDefs.add(columnName + " VARCHAR(" +
sString.value() + ")" +
getConstraints(sString.constraint()));
}
}
//数据库表构建语句
StringBuilder createCommand = new StringBuilder(
"CREATE TABLE " + tableName + "(");
for(String columnDef : columnDefs) {
createCommand.append("\n " + columnDef + ",");
}
// Remove trailing comma
String tableCreate = createCommand.substring(
0, createCommand.length() - 1) + ");";
return tableCreate;
}
/**
* 判断该字段是否有其他约束
* @param con
* @return
*/
private static String getConstraints(Constraints con) {
String constraints = "";
if(!con.allowNull()) {
constraints += " NOT NULL";
}
if(con.primaryKey()) {
constraints += " PRIMARY KEY";
}
if(con.unique()) {
constraints += " UNIQUE";
}
return constraints;
}
}