首先Java注解是什么?
注解就是一个标签。通过注解你可以给类or方法or变量添加一个标签。除此之外它没有其他作用了。你所见到的那些关于注解的神奇用法其实都是Java反射和编译器的功劳。
1.元注解
(一切注解的老祖宗:用了给注解贴标签的注解)
- @Documente
表示拥有该注解的元素可通过javadoc此类的工具进行文档化。该类型应用于注解那些影响客户使用带注释(comment)的元素声明的类型。如果类型声明是用Documented来注解的,这种类型的注解被作为被标注的程序成员的公共API。
- @Target
用来约束注解可以应用的地方(如方法、类或字段),其中ElementType是枚举类型,也代表可能的取值范围。请注意,当注解未指定Target值时,则此注解可以用于任何元素之上,多个值使用{}包含并用逗号隔开
- @Retention
用来约束注解的生命周期,分别有三个值,源码级别(source),类文件级别(class)或者运行时级别(runtime)。请注意,当注解未定义Retention值时,默认值是CLASS,如Java内置注解,@Override、@Deprecated、@SuppressWarnning等
- @Inherited
表示该注解类型被自动继承(即:子类能够继承父类的注解)。如果在当前类中查询(通过反射)这个元注解类型并且当前类的声明中不包含这个元注解类型,那么就会自动查询当前类的父类是否存在Inherited元注解,这个动作将被重复执行直到这个标注类型被找到,或者是查询到顶层的父类。
2.注解的属性
注解中也可以添加属性的。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
int id() default 1;
String msg();
}
注解属性支持的类型:
- 所有基本类型(int,float,boolean,byte,double,char,long,short)
- String
- Class
- enum
- Annotation
- 上述类型的数组
注解不可以继承
但注解可以嵌套,类似于Java内部类
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
@interface Pro {
String color();
}
String name() default "";
}
使用:
@MyTest.Pro(color = "red")
public int add() {
return a + b;
}
不过这样做目前没有发现太大的存在意义。因为Pro也只是相当于MyTest注解的一个属性,没有其他特殊功能了。通过反射查找的时候反而要多一层罢了。
3.如何让注解发挥作用
开篇就写了,如果没有反射和编译器的配合的话,注解没有任何特殊效果,它就是个标签。那么是如何让注解发挥作用的呢?
1).反射
Junit单元测试中的各种注解。@Test
最终是通过Junit框架的来发挥@Test的作用的。
2)编译器
比如这个注解,@Deprecated编译器在编译阶段遇到这个注解时会发出提醒警告
4.举个栗子
Mytes.java
//自定义注解
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}
Calculate.java
public class Calculate {
private int a;
private int b;
public Calculate(int a, int b) {
this.a = a;
this.b = b;
}
//添加注解
@MyTest
public int add() {
return a + b;
}
@MyTest
public int subtract() {
return a - b;
}
@MyTest
public int multiply() {
return a * b;
}
@MyTest
public int divide() {
return a / b;
}
}
然后写一个测试类Test.java
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) {
Calculate calculate = new Calculate(2, 0);
Class clazz = calculate.getClass();
Method[] methods = clazz.getDeclaredMethods();
int testNum = 0;
int successNum = 0;
int errNum = 0;
for (Method method : methods) {
testNum++;
if (method.isAnnotationPresent(MyTest.class)) {
String methodName = method.getName();
try {
Object ret = method.invoke(calculate);
System.out.println("Method:" + methodName + ", Test Success, Result:" + ret);
successNum++;
} catch (Exception e) {
String err = "Method:" + methodName + ", Test Failed"
+ "\n caused by:" + e.getCause().getClass()
+ "\n " + e.getCause().getMessage();
System.out.println(err);
errNum++;
}
}
}
System.out.println("MyTest Done. All MyTest:" + testNum + "; SuccessNum:" + successNum + "; ErrNum:" + errNum);
}
}
5.使用注解的基本套路
//自定义一个color注解
@Target({ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Color {
String[] colors();
int num();
}
Method[] methods = clazz.getDeclaredMethods();
Field field=clazz.getField("age");//借助反射
if (field.isAnnotationPresent(Color.class)){//判断是否使用注解
Annotation annotation=field.getAnnotation(Color.class);//获取注解
String[] colors=((Color) annotation).colors();//获取注解某个属性的值
//接下来就是根据或得到的注解值colors做一些你想做的操作
//比如colors中包含red,你就咋咋咋
//比如colors中包含black,你就把它赋值给某个变量
}
文中代码以上传到github:https://github.com/zhaominso/java-annotation