文章目录
一、介绍
1 概念
注解(Annotation),也叫元数据。一种代码级别的说明。它是Java 5新增的技术。要区别注释,注解是代码里的一种特殊标记,可在编译前、编译后、运行时等不同的时期被读取,并作出相应的处理。
2 原则
由于注解的代码是附属信息,它要遵循一个基本原则:注解不能直接干扰代码的运行,无论增加或者删除注解,代码都能正常的运行。
二、如何定义注解
1 创建
定义新的注解使用@interface关键字
public @interface MyAnnotation {
}
1.1 成员变量
Annotation只有成员变量。其成员变量以“无形参的方法”形式来声明。例:
public @interface MyAnnotation {
//String定义了成员类型,name定义了成员名
String name();
//也可以在定义成员变量时指定默认值
int age() default 18;
}
该注解如何使用呢?
class AnnoTest{
//age已经为其指定默认值,那么在使用的时候就可以不为它指定值,age就会使用这个指定的默认值
@MyAnnotation(name = "张三")
public void test() {
}
}
若是Annotation只有一个成员变量,定义是该成员名必须为value
public @interface MyAnnotation {
String value();
}
该注解使用时也可以省略成员名和“=”号,例:
class AnnoTest{
@MyAnnotation( "张三")
public void test() {
}
}
根据Annotation包含的成员数目可以将其分为两类:
- 标记注解:没有成员变量,如@Override
- 元数据注解:包含成员变量的注解,如@SuppressWarnings
1.2 元注解
我们自定义注解时可以使用jdk自带的4个元注解来修饰定义的注解
- @Retention
- @Target
- @Documented
- @Inherited
1.2.1 @Retention
作用:设置注解保留的截止时间
public @interface Retention {
//保留策略,该变量是个枚举类型
RetentionPolicy value();
}
public enum RetentionPolicy {
//只保留在源代码中,编译后丢失
SOURCE,
//保留在源代码和class文件中,程序运行运行时丢失
CLASS,
//保留在源代码、class文件中,程序运行时jvm依然会保留该注解信息,可以通过反射回去该注解的信息
RUNTIME
}
例:
//该注解在程序运行时可以利用反射获取value值
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "";
}
1.2.2 @Target
作用:指定该注解可以修饰哪些元素
public @interface Target {
//指定注解修饰的元素类型,该成员是个枚举数组,一次可以指定多个可修饰元素
ElementType[] value();
}
public enum ElementType {
/** 能修饰类、接口或枚举类型 */
TYPE,
/** 能修饰成员变量 */
FIELD,
/** 能修饰方法 */
METHOD,
/** 能修饰参数 */
PARAMETER,
/** 能修饰构造器 */
CONSTRUCTOR,
/** 能修饰局部变量 */
LOCAL_VARIABLE,
/** 能修饰注解 */
ANNOTATION_TYPE,
/** 能修饰包 */
PACKAGE,
/**
* 可以用在 Type 的声明式前
* @since 1.8
*/
TYPE_PARAMETER,
/**
*可以用在所有使用 Type 的地方(如:泛型,类型转换等)
* @since 1.8
*/
TYPE_USE
}
示例1:单个ElementType
@Target(ElementType.FIELD)
public @interface MyAnnotation {
String value() default "";
}
示例2:多个ElementType
@Target({ElementType.FIELD,ElementType.METHOD})
public @interface MyAnnotation {
String value() default "";
}
1.2.3 @Documented
使用了@Documented修饰自定义注解A,在使用javadoc命令生成API文档后,所有使用A注解修饰的程序元素,将会包含注解A的说明。
1.2.4 @Inherited
使用了@Inherited修饰注解可以被继承。
三、注解的提取
Java注解可以修饰类、类属性、类方法、类构造器等等,这些属性被注解修饰后不会自动的生效,需要我们把这些信息提取出来,加上处理代码,下面列举一些注解的一些常见的使用场景。
1 修饰类
1.1 定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Entity{
String name() default "";
String description() default "这里可以对表模型进行一些描述";
}
1.2 使用注解
@Entity(name = "user")
class User{
}
1.3 提取注解信息
public class AnnoTest {
public static void main(String[] args) {
//判断User类是否被@Entity注解修饰
boolean present = User.class.isAnnotationPresent(Entity.class);
if (present) {
Entity annotation = User.class.getAnnotation(Entity.class);
System.out.println("表名:"+annotation.name()+"
"+"描述:"+annotation.description());
}
}
}
2 修饰类属性
2.1 定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Field{
String name() default "";
boolean exist() default true;
}
2.2 使用注解
class User{
@Field(name = "username")
private String username;
}
2.3 提取注解信息
public class AnnoTest {
public static void main(String[] args) throws NoSuchFieldException {
Class<User> c = User.class;
//根据属性名获取类属性
java.lang.reflect.Field field = c.getDeclaredField("username");
Field anno = field.getAnnotation(Field.class);
if (anno != null) {
System.out.println("表字段名:"+anno.name());
System.out.println("是否存在该字段:"+anno.exist());
}
}
}
3 修饰类方法
3.1 定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Log{
String user() default "";
String operation() default "he";
}
3.2 使用注解
class User{
@Log(user = "张三",operation = "删除文章")
public void deleteArticle() {
}
}
3.3 提取注解信息
public class AnnoTest {
public static void main(String[] args) throws NoSuchFieldException {
Class<User> c = User.class;
//获取该类所有方法
Method[] methods = c.getDeclaredMethods();
if (methods.length>0) {
for (Method m:methods) {
if (m.getName().equals("deleteArticle")) {
Log anno = m.getDeclaredAnnotation(Log.class);
if (anno != null) {
StringBuffer sb = new StringBuffer();
LocalDate now = LocalDate.now();
sb.append(anno.user()).append("于").append(now.toString()).append(",调用了方法“"+m.getName()+"”,进行了"+anno.operation()+"操作!");
System.out.println(sb.toString());
}
}
}
}
}
}
四、注解的本质
1 新建一个注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Log{
@Deprecated
String user() default "";
String operation() default "喝茶";
}
2 编译注解
打开dos命令框,在该文件目录下输入命令
javac Log.java
得到Log.class文件
3 反编译log.class文件
输入命令
javap -verbose -c Log.class > Log.txt
得到Log.txt字节码文件:
总结
- 从反编译的信息里可以知道,注解就是一个继承java.lang.annotation.Annotation接口的接口。
- 注解的成员变量会被编译为同名的抽象方法,成员类型就是方法返回值类型,通过调用该注解对象的方法就能得到该方法对应的成员变量值。