一、概念
Annotation(注解)是JDK1.5及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。注解是以‘@注解名’在代码中存在的,根据注解参数的个数,我们可以将注解分为:标记注解、单值注解、完整注解三类。它们都不会直接影响到程序的语义,只是作为注解(标识)存在,我们可以通过反射机制编程实现对这些元数据(用来描述数据的数据)的访问。另外,你可以在编译时选择代码里的注解是否只存在于源代码级,或者它也能在class文件、或者运行时中出现(SOURCE/CLASS/RUNTIME)。
简单来说,java中的注解是附加在代码上的原信息,用于一些工具在编译、运行时解析使用,起到了说明、配置的作用。它不会影响代码的逻辑,仅仅是起到了辅助性的作用。
二、用处
如果要对于元数据的作用进行分类,还没有明确的定义,不过我们可以根据它所起的作用,大致可分为三类:
编写文档:通过代码里标识的元数据生成文档。
代码分析:通过代码里标识的元数据对代码进行分析。
编译检查:通过代码里标识的元数据让编译器能实现基本的编译检查
三、内置注解
1、@Override
它的作用是对覆盖父类中方法的方法进行标记,如果被标记的方法并没有实际覆盖父类中的方法,则编译器会发出错误警告。
@Override
public String toString() {
return "重写toString()";
}
2、@Deprecated
它的作用是对不应该再使用的方法添加注解,当编程人员使用这些方法时,将会在编译时显示提示信息。
class Example{
@Deprecated
public static void print(){
}
}
3、@SuppressWarnings
SuppressWarning不是一个标记类型注解。它有一个类型为String[]的成员,这个成员的值为被禁止的警告名。
其参数有:
-
deprecation,使用了过时的类或方法时的警告
-
unchecked,执行了未检查的转换时的警告
-
fallthrough,当 switch 程序块直接通往下一种情况而没有 break 时的警告
-
path,在类路径、源文件路径等中有不存在的路径时的警告
-
serial,当在可序列化的类上缺少serialVersionUID 定义时的警告
-
finally ,任何 finally 子句不能正常完成时的警告
-
all,关于以上所有情况的警告
public static List list = new ArrayList(); @SuppressWarnings("unchecked") public void add(String data) { list.add(data); }
四、元注解
java.lang.annotation提供了四种元注解,专门注解其他的注解(在自定义注解的时候,需要使用到元注解):
@Documented –注解是否将包含在JavaDoc中
@Retention –什么时候使用该注解
@Target –注解用于什么地方
@Inherited – 是否允许子类继承该注解
1、@Documented
将注解信息加入到java文档中,Documented是一个标记注解,没有成员。
2、@Retention-用于描述注解的生命周期
● RetentionPolicy.SOURCE : 在编译阶段丢弃,即在源文件中有效(源文件保留)。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。
● RetentionPolicy.CLASS : 在类加载的时候丢弃,在class文件中有效(class保留)在字节码文件的处理中有用。注解默认使用这种方式
● RetentionPolicy.RUNTIME : 始终不会丢弃,运行期也保留该注解(运行时保留),因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
3、@Target – 表示该注解的使用范围
默认值为任何元素,表示该注解用于什么地方。可用的ElementType参数包括
● ElementType.CONSTRUCTOR:用于描述构造器
● ElementType.FIELD:成员变量、对象、属性(包括enum实例)
● ElementType.LOCAL_VARIABLE:用于描述局部变量
● ElementType.METHOD:用于描述方法
● ElementType.PACKAGE:用于描述包
● ElementType.PARAMETER:用于描述参数
● ElementType.TYPE:用于描述类、接口(包括注解类型) 或enum声明
4、@Inherited
@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注类型是被继承的。如果一个使用了@Inherited修饰的annotation类型用于一个class,则这个annotation将被用于该class的子类。
五、自定义注解
自定义注解类编写的一些规则:
-
Annotation型定义为 @interface , 所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口。
-
参数成员只能用public或默认(default)这两个访问权修饰
-
参数成员只能使用byte,short,char,int,long,float,double,boolean八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这一些类型的数组.
-
要获取类方法和字段的注解信息,必须通过Java的反射技术来获取 Annotation对象,因为你除此之外没有别的获取注解对象的方法
-
注解也可以没有定义成员,自定义注解需要使用到元注解
代码案例:
自定义注解
package com.annnotation;
import java.lang.annotation.*;
//元注解,设置自定义注解的生命周期
@Retention(RetentionPolicy.RUNTIME)
//元注解,设置自定义注解的参数类型
@Target(ElementType.FIELD)
@Documented
@Inherited
public @interface ColumnName {
//该自定义注解里面只有一个变量成员名称
public String name() default "";
}
创建实体了,并使用自定义注解
package com.annnotation;
public class Student {
@ColumnName(name = "stuName")
String name;
@ColumnName(name = "stuAge")
int age;
@ColumnName(name = "stuSex")
String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
测试类,使用反射获得一条sql查询语句
public static void main(String[] args) throws ClassNotFoundException {
Class<?> demo=Class.forName("com.annnotation.Student");
Field[] fields=demo.getDeclaredFields();
String sql="select ";
String link="";
for (Field field : fields) {
//获得每个成员的注解
// Annotation[] annotations = field.getAnnotations();
// for (int i = 0; i < annotations.length; i++) {
// System.out.println(annotations[i]);
// }
//获得自定义注解对象
ColumnName columnName=field.getAnnotation(ColumnName.class);
//判断注解是不是自定义注解类型
if(field.isAnnotationPresent(ColumnName.class) && columnName!=null){
sql+=link+columnName.name();
}else{
sql+=link+field.getName();
}
link=",";
}
sql+=" from Student";
System.out.println(sql);
}
}
结果