1 概述
注解用于对Java中类、方法、成员变量做标记,然后进行特殊处理,至于到底做何种处理由业务需求来决定。
例如,JUnit框架中,标记了注解@Test的方法就可以被当做测试方法进程执行
2 自定义注解
public @interface 注解名称 {public 属性类型 属性名() default 默认值;
}
属性类型支持Java中的所有数据类型
自定义注解
public @interface MyBook {String name();String[] authors();double price();
}
使用注解
@MyBook(name = "《Java基础1》", authors = "Bill", price = 123)
public class Main {public static void main(@MyBook(name = "《Java基础2》", authors = "Bill", price = 123) String[] args) {@MyBook(name = "《Java基础3》", authors = "Bill", price = 123)int i = 3;System.out.println("Hello world!");}@MyBook(name = "《Java基础4》", authors = "Bill", price = 123)private Main() {}
}
注解可以标注类、形参、变量、方法。
3 注解的特殊属性
注解中有一个value属性,如果只有一个value属性的情况下,使用value属性的时候可以省略名称不写。
但是如果有多个属性,且多个属性没有默认值,那么value名称是不能省略的。
public @interface MyAnnotatin {String value();
}
使用时
@MyAnnotatin(value = "test")
public class Annotation {public static void main(String[] args) {System.out.println("test");}
}
可以省略value
@MyAnnotatin("test")
public class Annotation {public static void main(String[] args) {System.out.println("test");}
}
如果与value共存的有多个属性,则会报错
public @interface MyAnnotatin {String value();String name();
}
上面使用的代码会报错
如果name具有默认值,则又可以省略value,因为此时name是有值的,使用默认值。
public @interface MyAnnotatin {String value();String name() default "MyAnnotaion";
}
4 元注解
元注解就是注解注解的注解,元注解一共有四种。
- @Target:描述注解的使用范围
- @Retention:申明注解的生命周期
- @Documented:描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息
- @Inherited:被他修饰的注解具有继承属性
4.1 Target注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;@Target({ElementType.METHOD, ElementType.FIELD})
public @interface MyTest {
}
只能在方法和字段中使用,不能用于类等其他地方。
4.2 Retention注解
5 注解解析
注解的解析就是判断是否存在注解,存在注解就解析出内容
与注解解析相关的接口
- Annotation:注解的顶级接口,注解都是Annotation类型的对象
- AnnotatedElement:该接口定义了与注解解析相关的解析方法
所有的类成分Class,Method,Field,Constructor都实现了AnnotatedElement接口,他们都有解析注解的能力。
解析技巧: - 注解在哪个成分上,我们就先拿哪个对象成分
- 比如注解作用在成员方法,则现货区该方法对应的Method对象,再拿上面的注解
- 注解在类上,先拿Class对象,再拿注解
- 注解在成员变量上,先拿成员变量对应的Field对象,再拿注解
定义注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Bookk {String value();double price() default 100;String[] author();
}
解析注解
import org.junit.Test;import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;/**目标:完成注解的解析*/
public class AnnotationDemo3 {@Testpublic void parseClass(){// a.先得到类对象Class c = BookStore.class;// b.判断这个类上面是否存在这个注解if(c.isAnnotationPresent(Bookk.class)){//c.直接获取该注解对象Bookk book = (Bookk) c.getDeclaredAnnotation(Bookk.class);System.out.println(book.value());System.out.println(book.price());System.out.println(Arrays.toString(book.author()));}}@Testpublic void parseMethod() throws NoSuchMethodException {// a.先得到类对象Class c = BookStore.class;Method m = c.getDeclaredMethod("test");// b.判断这个类上面是否存在这个注解if(m.isAnnotationPresent(Bookk.class)){//c.直接获取该注解对象Bookk book = (Bookk) m.getDeclaredAnnotation(Bookk.class);System.out.println(book.value());System.out.println(book.price());System.out.println(Arrays.toString(book.author()));}}
}@Bookk(value = "《情深深雨濛濛》", price = 99.9, author = {"琼瑶", "dlei"})
class BookStore{@Bookk(value = "《三少爷的剑》", price = 399.9, author = {"古龙", "熊耀华"})public void test(){}
}
先拿对应对象,再拿注解属性,并进行对应的操作。
6 模拟Junit
注解
@Target({ElementType.METHOD}) // 元注解
@Retention(RetentionPolicy.RUNTIME) // 一直活着,在运行阶段这个注解也不消失
public @interface MyTest {
}
注解只作用于方法上,并且一直存活。
测试类
public class AnnotationDemo4 {public void test1(){System.out.println("===test1===");}@MyTestpublic void test2(){System.out.println("===test2===");}@MyTestpublic void test3(){System.out.println("===test3===");}/**启动菜单:有注解的才被调用。*/public static void main(String[] args) throws Exception {AnnotationDemo4 t = new AnnotationDemo4();// a.获取类对象Class c = AnnotationDemo4.class;// b.提取全部方法Method[] methods = c.getDeclaredMethods();// c.遍历方法,看是否有MyTest注解,有就跑它for (Method method : methods) {if(method.isAnnotationPresent(MyTest.class)){// 跑它method.invoke(t);}}}
}
这里main方法作为一个启动按钮,因为junit与idea有合作,所以标识了@Test的都有一个启动按钮,而我们的注解没有,所以这里模拟一下。启动后获取方法列表,并判断哪个方法上有MyTest注解,有的则执行方法。
7 总结
- 注解作用是对类、方法、成员变量等做标记,并进行特殊处理
- 元注解是注解注解的注解,有四种元注解,分别为Target、Retention、Documented、Inherited。
- Target标识注解作用的对象
- Retention申明注解的生命周期,编译、字节吗还是运行时期一直存在。
- Documented标识使用javadoc生成说明时保留注解
- Inherited修饰的注解具有继承属性
- 注解解析就是对注解内容进行解析。类相关的类,比如Class、Method、Filed等都实现了AnnotatedElement接口,所以,解析的时候,首先获得对象的对象,并调用判断方法判断注解是否存在,如果存在,则执行对应操作