框架的底层都是注解和反射。
1、什么是注解
英文:Annotation
官方:java注解用于为java代码提供元数据。
注解像类和接口一样新建,如建立一个叫ThisIsAnnotation的注解。
public @interface ThisIsAnnotation {
...
}
注解使用@+注解名可以标记在类、方法、属性甚至方法参数上。
其中的@interface是一个关键字,这个关键字表示:这个东西是继承了java.lang.annotation.Annotation接口,表示它是一个注解。
2、内置注解
@Override 表示重写
@Deprecated 表示已被废弃,不推荐使用
@SuppressWarnings 用来抑制编译时的警告信息
@FunctionalInterface 表示函数式接口
@SafeVarargs 专门为抑制“堆污染”警告提供的
3、元注解
元注解就是用来修饰注解的注解,用来使该注解附带别的功能。比如我们看@overrid的源码:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
上面就有两个元注解。元注解有五种:
-
@Retention
意为保留、保持。即指定注解保留到什么阶段,是源码(编译)、字节码(类加载)还是运行阶段。
// 注解仅存在于源码中,在class字节码文件中不包含 @Retention(RetentionPolicy.SOURCE) // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得 @Retention(RetentionPolicy.CLASS) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到 @Retention(RetentionPolicy.RUNTIME)
如overrid注解只是一个标记作用,只是给阅读代码的人看的,所以只需要保留在源码阶段即可。
-
@Target
即注解的目标是谁。有这些:
@Target(ElementType.TYPE) //作用接口、类、枚举、注解 @Target(ElementType.FIELD) //作用属性字段、枚举的常量 @Target(ElementType.METHOD) //作用方法 @Target(ElementType.PARAMETER) //作用方法参数 @Target(ElementType.CONSTRUCTOR) //作用构造函数 @Target(ElementType.LOCAL_VARIABLE)//作用局部变量 @Target(ElementType.ANNOTATION_TYPE)//作用于注解 @Target(ElementType.PACKAGE) //作用于包 @Target(ElementType.TYPE_PARAMETER)//作用于泛型,即泛型方法类接口, 1.8加入 @Target(ElementType.TYPE_USE) //可以用于标注任意类型除了class, 1.8加入
-
@Document
它的唯一作用是能够将注解中的元素包含到 Javadoc 中去。
-
@Inherited
表示这个注解具有继承性。即,如果一个类被具有继承性的注解标记了,则子类会继承父类的注解。
-
@Repeatable
表示这个注解具有重复性,即可以重复作用于一个对象多次,但每次注解的属性不同。
4、自定义注解
通过@interface标记该类是一个注解,假设自定义了一个注解如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
String name();
int id() default 101;
String[] friend() default {"Alis","Bob"};
}
4.1、属性
注解里可以有它的属性,如上面的name、id、friend。属性写法要加上括号,后面还可以加上默认值。如果没有默认值,使用该注解时就要指定该属性的值。
属性的类型可以为以下六种:基本数据类型,String,枚举类型,注解类型,Class类型,以上类型的一维数组类型。
4.2、使用
使用时在可以标注的位置使用该注解,并给出必要的参数。如上例,id、friend有默认参数,就可以只给出name。
@Test(name = "cc")
5、通过反射获取注解属性的值
要获取某个注解属性的值就要从它修饰的东西入手(类、方法或属性),先通过反射获取到注解修饰的东西。反射获取到的对象可以使用下面几个方法:
/**是否存在对应 Annotation 对象*/
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return GenericDeclaration.super.isAnnotationPresent(annotationClass);
}
/**获取 Annotation 对象(如果只有一个)*/
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
Objects.requireNonNull(annotationClass);
return (A) annotationData().annotations.get(annotationClass);
}
/**获取所有 Annotation 对象*/
public Annotation[] getAnnotations() {
return AnnotationParser.toArray(annotationData().annotations);
}
仍以上述的Test注解为例,假设我们有一个User类,想通过注解给类传值进行参数设置:
@Test(name = "cui")
public class User {
public String name;
public User(){
try {
//获取注解修饰的类
Class cls = Class.forName("com.User");
//判断该类有没有注解修饰
if(cls.isAnnotationPresent(JustTest.class)){
//拿到这个注解
JustTest annotation = (JustTest) cls.getAnnotation(JustTest.class);
//获取注解中的属性并为name赋值
this.name = annotation.name();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}