目录
一、注解是什么?
注释大家都知道,就是对程序起解释说明作用的,而注解同样是对程序起解释说明作用的,只不过注释是给人看的,而注解是给计算机看的。
注解本质上是一个接口,该接口默认继承Annotation接口。
二、预定义注解
预定义注解就是Java中已经定义好的注解,比如@Override。
几个常用的预定义注解:
1.@Override:检测该注解修饰的方法是否是继承自父类(接口);
2.@Deprecated:表示该注解修饰的内容是已过时的;
3.@SuppressWarnings:压制警告,忽略警告,一般传递参数“all”,@SuppressWarnings("all"),并且放在类上面,压制整个类的所有警告,
也可以放在特定的位置指定要忽略的警告,@SuppressWarnings({"uncheck","指定警告"})
比如下图,放在mid()方法上,压制mid()方法中的已过时警告;
三、自定义注解和元注解
1.元注解
在Java中我们可以自己定义注解,在自定义注解时,需要涉及到元注解,元注解是什么呢?元注解就是用于描述注解的注解。
四种元注解:
1.@Target:用来描述注解能够作用的位置。
(1) @Target(ElementType.TYPE):修饰的注解只能作用于类上
(2) @Target(ElementType.METHOD):修饰的注解只能作用于方法上
(3) @Target(ElementType.FIELD):修饰的注解只能作用于字段上
注意:当注解未指定Target时,注解可以作用于任何元素上。多个值使用{ }包含并用都好隔开。如下:
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
2.@Retention:描述注解被保留的阶段,用来约束注解的生命周期。
(1)@Rentention(RetentionPolicy.SOURCE):当前注解在源码编译之前就会被编译器抛弃(源码时期);
(2)@Rentention(RetentionPolicy.CLASS):当前注解可以保留到编译期,在class文件中可用,但在加载进内存之前就会被JVM丢弃(编译时期);
(3)@Rentention(RetentionPolicy.RUNTIME):当前注解可以保留到运行期,即保留到class字节码文件并可以被JVM读取到,因此可以通过反射机制获取到该注解的信息(运行时期);
注意:当注解未指定Rentention值时,默认值是CLASS;
3.@Documented:描述注解是否被抽取到api文档中。
示例:
定义两个注解如下:
@Documented
public @interface MyAnno01 {
}
public @interface MyAnno02 {
}
在类中使用这两个注解:
public class Anno01 {
public static void main(String[] args) {
@MyAnno01
@MyAnno02
public void big(){
System.out.println("Test...");
}
}
}
将上面java文件(Anno01.java、MyAnno01.java、MyAnno02.java)放到一个文件夹中,
win+R打开cmd,然后在cmd中打开上面这个java文件所在文件夹,输入javadoc指令+空格+类名,如下:
C:\Users\Administrator\Desktop\xinjian>javadoc Anno01.java
之后回车即可生成api文档。
在文件中打开生成的index.html,可以发现
只有@MyAnno01注解被保留到api文档中,而@MyAnno02注解则没有。
4.@Inherited:可以让注解被继承,但这并不是真的继承,只是通过使用@Inherited,可以让子类Class对象使用getAnnotations()获取父类被@Inherited修饰的注解。
当然,要通过此方法获取注解信息,@Inherited修饰的注解必须被@Rentention(RetentionPolicy.RUNTIME)修饰。
(注意:注解本身是不支持继承的)
//定义注解
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno01 {
}
//使用注解
@MyAnno01
class Mid{
}
//创建子类
class Small extends Mid{
}
public class Big {
public static void main(String[] args) {
Mid mid=new Small();
System.out.println(Arrays.toString(mid.getClass().getAnnotations()));
/*
运行结果:[@cn.Hello.MyAnno01()]
*/
}
}
2.自定义注解
自定义注解的格式为:
元注解
public @interface 注解名称 {
属性列表;
}
属性列表:接口中可以定义的抽象方法。
要求:
属性的返回值类型有下列取值:
- 基本数据类型
- String
- 枚举
- 注解
- 以上类型的数组
定义了属性,在使用时需要给属性赋值。
- 如果定义属性时,使用default关键字给属性值默认初始值,则使用注解时,可以不进行属性的赋值;
- 如果只有一个属性需要赋值,且该属性名称为value,则value可以省略,直接定义即可;
- 数组赋值时,值使用{ }包裹,如果数组只有一个值,则{ }可以省略。
比如:
@MyAnno01(num={1,2,3})
四、在程序中使用(解析)注解
在程序中使用(解析)注解:获取注解中定义的属性值,
步骤:
-
创建注解修饰元素的类对象;
-
用类对象调用getAnnotation()方法获取注解对象;
-
调用注解中的抽象方法,获取配置的属性值。
案例如下:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Pro {
String className();
String methodName();
}
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/*
创建一个“框架”类,可以创建任意类的对象,执行任意类中的方法
只需改动注解中的类名和方法名即可实现。
*/
@Pro(className = "cn._17_Annotation.Worker",methodName = "work")
public class ReflectTestPro {
public static void main(String[] args) throws Exception {
//解析注解:获取注解中定义的属性值
//1.创建注解修饰元素的类对象
Class<ReflectTestPro> proClass = ReflectTestPro.class;
//2.用类对象调用getAnnotation()方法获取注解对象
Pro anno = proClass.getAnnotation(Pro.class);
//3.调用注解中的抽象方法,获取配置的属性值
String className = anno.className();
String methodName = anno.methodName();
//通过反射机制创建对象调用方法
Class cls = Class.forName(className);
Object obj = cls.newInstance();
Method method = cls.getMethod(methodName);
method.invoke(obj);
}
}
五、总结
注解是放在Java源码的类、方法、字段、参数前的一种特殊“注释”
注解的作用:
- 通过代码里标识的注解生成帮助文档;
- 通过代码里标识的注解让编译器能够实现基本的编译检查;(例如:@Override修饰的方法如果不是继承自父类/接口,就会产生异常)
- 可以在反射中解析并使用Annotation。