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 {
@Test
public 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()));
}
}
@Test
public 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===");
}
@MyTest
public void test2(){
System.out.println("===test2===");
}
@MyTest
public 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接口,所以,解析的时候,首先获得对象的对象,并调用判断方法判断注解是否存在,如果存在,则执行对应操作