什么是注解
Java1.5引入了注解,注解作为程序的元数据嵌入到程序中。注解可以被一些解析工具或者编译工具进行解析。我们也可以声明注解在编译过程或者执行时产生作用。
注解的使用
@注解名(成员=值)
将注解放在方法,类,接口,字段,参数等的上面或者前面
例如
@Test(desc=”down”)
@Base(“up”)
例如jdk自带的注解
1.@Override—-当我们想要复写父类的方法时,我们需要使用注解去告诉编译器我们想要复写这个方法。这样一来当父类中的方法移除或者发生更改时编译器将提示错误信息。
2.@Deprecated—-当我们希望编译器知道某一个方法不建议使用时,我们应该使用这个注解。java在javadoc中推荐使用该注解,我们应该提供为什么该方法不推荐使用以及替代的方法。
3.@SuppressWarnings—-这个仅仅是告诉编译器忽略特定的警告信息,例如在泛型中使用原生数据类型。他的保留策略是SOURCE并且被编译器丢弃。
注解的分类
按照运行机制来分
1.源码注解
注解只在源码中存在,编译成.class文件就不存在了
2.编译时注解
注解在源码和.class文件都存在
例如@Override @Deprecated @SuppressWarnings(“”)
3.运行时注解
在运行阶段还起作用,甚至会影响运行逻辑的注解
4.元注解 注解的注解
按使用来分
1.元注解 注解的注解
2.jdk自带的注解
3.自定义注解
注解的定义
注解定义看起来很像接口的定义。事实上,与其他任何接口一样,注解也将会编译成class文件。
1.注解方法不能带有参数。
2.注解方法返回值类型限定为:基本类型、String、Enums、Annotation或者这些类型的数组。
3.注解方法可以有默认值。
4.注解本身能够包含元注解,元注解被用来注解其他注解。
@Target(ElementType.Method)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
String desc();
String auth();
int age() default 18;
}
除了@符号以外,@Test的定义很像一个空的接口,使用@interface关键字来定义,成员以无参无异常的方式定义,可以使用default指定一个默认值。
当注解只有一个成员时,可以定义成value,使用时可以直接赋值(成员值),不必(成员名=成员值)
public @interface Test {
String value();
}
@Test(“zhangsan”)
默认值限制
编译器对元素的默认值有些过分挑剔。首先,元素不能有不确定的值。也就是说,元素必须要么具有默认值,要么在使用注解时提供元素的值。
其次,对于非基本类型的元素,无论是在源代码中声明,还是在注解接口中定义默认值,都不能以null作为值。这就是限制,这就造成处理器很难表现一个元素的存在或缺失状态,因为每个注解的声明中,所有的元素都存在,并且都具有相应的值。为了绕开这个限制,只能定义一些特殊的值,例如空字符串或负数,表示某个元素不存在。
元注解
定义注解时,需要一些元注解(meta-annotation),如@Target和@Retention
@Target用来定义注解将应用于什么地方(如一个方法或者一个域)
@Retention用来定义注解在哪一个级别可用,在源代码中(source),类文件中(class)或者运行时(runtime)
在注解中,一般都会包含一些元素以表示某些值。当分析处理注解时,程序可以利用这些值。没有元素的注解称为标记注解(marker annotation)
元注解的种类
四种元注解,元注解专职负责注解其他的注解,所以这四种注解的Target值都是ElementType.ANNOTATION_TYPE
注解 | 说明 |
---|---|
@Target | 表示该注解可以用在什么地方,由ElementType枚举定义 CONSTRUCTOR:构造器的声明 FIELD:域声明(包括enum实例) LOCAL_VARIABLE:局部变量声明 METHOD:方法声明 PACKAGE:包声明 PARAMETER:参数声明 TYPE:类、接口(包括注解类型)或enum声明 ANNOTATION_TYPE:注解声明(应用于另一个注解上) TYPE_PARAMETER:类型参数声明(1.8新加入) TYPE_USE:类型使用声明(1.8新加入) PS:当注解未指定Target值时,此注解可以使用任何元素之上,就是上面的类型 |
@Retention | 表示需要在什么级别保存该注解信息,由RetentionPolicy枚举定义 SOURCE:注解将被编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃,不会保留在编译好的class文件里) CLASS:注解在class文件中可用,但会被VM丢弃(该类型的注解信息会保留在源码里和class文件里,在执行的时候,不会加载到虚拟机(JVM)中) RUNTIME:VM将在运行期也保留注解信息,因此可以通过反射机制读取注解的信息(源码、class文件和执行的时候都有注解的信息) PS:当注解未定义Retention值时,默认值是CLASS |
@Documented | 指明拥有这个注解的元素可以被javadoc此类的工具文档化。这种类型应该用于注解那些影响客户使用带注释的元素声明的类型。如果一种声明使用Documented进行注解,这种类型的注解被作为被标注的程序成员的公共API。 |
@Inherited | 指明该注解类型被自动继承。如果用户在当前类中查询这个元注解类型并且当前类的声明中不包含这个元注解类型,那么也将自动查询当前类的父类是否存在Inherited元注解,这个动作将被重复执行直到这个标注类型被找到,或者是查询到顶层的父类。
PS:是类而非接口。 |
@Repeatable | 1.8新增,可重复的,可以在同一个位置重复相同的注解 |
注解成员的类型
注解元素可用的类型如下:
- 所有基本类型(int,float,boolean,byte,double,char,long,short)
- String
- Class
- enum
- Annotation
- 或以上类型的数组
@Repeatable注解
@Repeatable注解是JDK1.8新加入的,,可重复的,可以在同一个位置重复相同的注解。举例
定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Filter {
String [] value();
}
使用和注解
@Filter({“/admin”,”/main”})
public class MainFilter { }
换一种风格的话可以这样定义使用
@Filter(“/admin”)
@Filter(“/main”)
public class MainFilter {}
定义如下:
package anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Filters.class)
public @interface Filter {
String value() default "";
}
package anno;
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 Filters {
Filter [] value();
}
使用@Repeatable时告诉编译器,使用@Filters来作为收集重复注解的容器,而每个@Filter存储各自指定的字符串值。
@Repeatable注解的获取
JDK1.8在AnnotatedElement接口新增了getDeclaredAnnotationsByType和getAnnotationsByType,在指定@Repeatable的注解时,会寻找重复注解的容器中。相对于,getDeclaredAnnotation和getAnnotation就不会处理@Repeatable注解。
java中get与getDeclared方法的区别,get包括自身public + 继承public的字段,方法或者自身public构造函数或者继承注解,getDeclared只包括自身所有字段,方法,构造函数,注解
使用Filter注解
package anno;
@Filter("filter1")
@Filter("filter2")
public class Annotest {
public static void main(String args[]) {
try {
//使用类加载器加载类
//Class<?> clazz=Class.forName("anno.Annotest");
Class<?> clazz=Class.forName("test.Hello");
//拿到注解实例
Filter[] annotations = clazz.getAnnotationsByType(Filter.class);
if (annotations != null) {
for (Filter filter : annotations) {
System.out.println(filter.value());
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package anno;
@Filter("filter1")
@Filter("filter2")
public class Annotest {
public static void main(String args[]) {
try {
//使用类加载器加载类
//Class<?> clazz=Class.forName("anno.Annotest");
Class<?> clazz=Class.forName("test.Hello");
//拿到注解实例
Filter[] annotations = clazz.getAnnotationsByType(Filter.class);
if (annotations != null) {
for (Filter filter : annotations) {
System.out.println(filter.value());
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
输出
filter1
filter2
普通类方法注解的获取
注解定义
package anno;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//描述注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Test {
String value();
}
注解的获取
package anno;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
@Test("This is Class Annotation")
public class TestAnno {
@Test("This is method1 Annotation")
public void test() {}
@Test("This is method2 Annotation")
public static void main(String[] args) {
try {
Class<?> clazz=Class.forName("anno.TestAnno");
//找到类上的注解
if(clazz.isAnnotationPresent(Test.class)){
Test test=clazz.getAnnotation(Test.class);
System.out.println(test.value());
}
//方法1:找到方法上的注解
Method[] methods = clazz.getMethods();
for (Method method : methods) {
//检测方法上面的Test注解是否存在
boolean isExit = method.isAnnotationPresent(Test.class);
if (isExit) {
//拿到Test注解实例
Test annotation = method.getAnnotation(Test.class);
String value = annotation.value();
System.out.println("value==" + value);
}
}
//方法2:找到方法的注解
for (Method method : methods) {
//获取方法的所有注解
Annotation[] annos= method.getAnnotations();
for(Annotation anno:annos){
//逐个查询注解是否是Test注解
if(anno instanceof Test){
String value = ((Test) anno).value();
System.out.println("value==" + value);
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出
This is Class Annotation
value==This is method2 Annotation
value==This is method1 Annotation
value==This is method2 Annotation
value==This is method1 Annotation
参考
Java,get与getDeclared的区别,@Inherited的作用 https://blog.csdn.net/u010002184/article/details/79173138
Java注解全面解析 https://www.cnblogs.com/longshiyVip/p/5189525.html
Java注解解析 整理 https://blog.csdn.net/jianjiaqqq001/article/details/73440822
Java注解详解 https://www.cnblogs.com/yili-2013/p/5133319.html