注解
定义
从JDK5开始,Java增加对元数据的支持,也就是注解,注解与注释是有一定区别的,可以把注解理解为代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过注解开发人员可以在不改变原有代码和逻辑的情况下在源代码中嵌入补充信息。
概念描述
- 编写文档:通过代码里的标识的注解生成文档[生成doc文档]
- 代码分析:通过代码里的标识的注解对代码进行分析
- 编译检查:通过代码里的标识的注解能让编译器实现简单的编译检查
常见的注解
- @Override:标识被该注解标识的方法是否是继承于父类的
- @Deprecated:标识目前的方法是过时的不建议使用
- @Suppresswarnings:压制警告注解,如果标识这个注解将会把改范围内的警告去除
自定义注解
注解格式
public @interface MyAnno1 {
}
//经过javaC javap 反编译后注解本质上就是一个接口
public interface Mynno1 extends java.langannotation.Annotation{
}
注解属性:接口中的抽象方法
public @interface MyAnno1 {
int show1();//基本数据类型
String show2();//String
User user(); //枚举
Myanno2 mynno2();//注解
String[] show3();//以上类型的数组
Myanno2[] show4();
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ga7EDbaj-1638320546341)(C:\Users\24913\AppData\Roaming\Typora\typora-user-images\image-20211126221256583.png)]
如果在注解中定义了属性,则必须赋值
public @interface MyAnno1 {
int age();//基本数据类型
}
@MyAnno1(age = 12)
public class Test2 {
}
如果不想赋值可以使用default关键字
public @interface MyAnno1 {
int age();//基本数据类型
String show2() default "张三";
}
//不赋值的情况下默认张三,如果赋值就是该值
@MyAnno1(age = 12)
public class Test2 {
}
如果有一个属性需要赋值,且名字为 value 则value可以省略,就可以直接定义
public @interface MyAnno1 {
int value();//只能有一个value
String show2() default "张三";
}
@MyAnno1(12)
public class Test2 {
}
枚举类型、注解类型、数组类型的赋值
public @interface MyAnno1 {
int show1();//基本数据类型
String show2() default "张三"; //String
User user(); //枚举
Myanno2 mynno2();//注解
String[] show3();//以上类型的数组
//
}
//数组可以使用大括号,但是如果只有一个就可以省略不写
@MyAnno1(show1 = 12,show2="李四",user = User.a,mynno2 = @Myanno2,show3={"a","b"})
public class Test2 {
}
元注解
元注解:描述注解的注解
-
@Target 描述注解能够作用的位置
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { //此处只有 ElementType类型的数组 ElementType[] value(); } public enum ElementType { TYPE,//类,接口(包括注释类型)或枚举声明 FIELD,//方法 METHOD,方法声明 PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE }
-
@Retention:描述注解被保留的阶段
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { RetentionPolicy value(); } public enum RetentionPolicy { SOURCE, CLASS,//会保留到class字节码文件中被jvm读取到 RUNTIME运行时 }
-
@Documented:描述注解是否被抽取到api文档中
-
@Inherited:描述注解知否被子类继承
如果使用了该注解,子类继承时会默认继承父类的其他注解
在程序中使用解析注解
程序如何从注解中获取对应的属性值?
//定义注解,在类上、运行时有效
@Target(value = {ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Pro {
String className();
String methodName();
}
//创建一个类,并写入一个方法
public class Demo1 {
public void show(){
System.out.println("demo1....show");
}
}
//在测试类中加入注解
@Pro(className = "com.shun.annotation.test.Demo1",methodName = "show")
public class ReflactTest {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
/*
不使用读取外部文件的情况下可以执行任意的方法
*/
//1注解解析
//1.1获取该类的字节码对象
Class<ReflactTest> reflactTestClass = ReflactTest.class;
//2获取该类上的注解对象
//下面这行代码其实就是在内存中生成了一个该注解接口的子类实现对象
Pro pro =reflactTestClass.getAnnotation(Pro.class);
//3调用注解对象中的抽象方法、获取返回值
String className = pro.className();
String methodName = pro.methodName();
Class clazz = Class.forName(className);
Object o = clazz.newInstance();
Method method = clazz.getMethod(methodName);
method.invoke(o);
}
}
//注意下面这行代码
//下面这行代码其实就是在内存中生成了一个该注解接口的子类实现对象
Pro pro =reflactTestClass.getAnnotation(Pro.class);
//相当于
public class ProImp implement Pro{
String className(){
return "com.shun.annotation.test.Demo1";
};
String methodName(){
return "show";
}
}