注解
文章目录
1 注解的概述
1.1 注解是什么
注解是JDK5的新特性,注解本身没有什么功能,但是javac编译器、开发工具或其他程序可以通过反射来查看你的程序是否有注解,也就是标记。根据不同的标记会做对应的事。注解可以用在包、类、构造器、成员变量、局部变量、方法、参数上。
1.2 常见的注解
@Override
:标明此注解的方法IDEA/Eclipse等开发工具会帮助我们检查重写方法的语法。@Deprecated
:标明此注解的方法、变量等代码在使用的时候IDEA/Eclipse等开发工具会将使用的方法或变量等划掉。@SuppressWarnings
:标明此注解的代码IDEA/Eclipse等开发工具抑制编译时的警告,就等于告诉编译器这个代码是安全的,不必警告。但是需要注意的是,有可能会把真的错误给隐藏了,用的时候尽量不要大范围使用。@Author
:用于生成文档的作者信息的。@Version
:用于生成文档的版本信息的。@WebServlet
:用于配置Servlet的,代替web.xml配置文件。@FunctionalInterface
:用于表明此接口是函数式接口,即有且只有一个抽象方法的接口,允许有默认方法(JDK8)、静态方法(JDK8)和私有方法(JDK9)。
2 自定义注解
好了,基本了解的注解的一些概念和常见的注解,我们来自定义我们自己的注解。
2.1 定义注解的格式
定义格式
@元注解
public @interfcae 注解名{
属性类型 属性名() default 值;
...
}
注:中括号里面的default值可写可不写。
2.2 注解的数据类型
注解的数据类型必须是以下类型:
- 基本数据类型
- String类型
- 枚举类型
- 注解类型
- Class类型
- 以上数据类型的一维数组类型。
2.3 元注解
上面定义的是简单的注解,不难发现上面定义的注解在类、成员变量、成员方法等地方都可以使用,如果我们想要让注解在那个位置使用,那就需要使用元注解来约束了。
常见的元注解:
@Target
@Retention
2.3.1 元注解:@Target
@Target
注解是用来限定注解的使用位置,它的取值如下表所示
注解取值 | 功能描述 |
---|---|
ElementType.TYPE | 类,接口或者枚举,包括注解声明 |
ElementType.FIELD | 字段声明 |
ElementType.METHOD | 方法声明 |
ElementType.PARAMETER | 参数声明 |
ElementType.CONSTRUCTOR | 构造器声明 |
ElementType.LOCAL_VARIABLE | 局部变量声明 |
ElementType.ANNOTATION_TYPE | 注解类型声明 |
ElementType.PACKAGE | 包声明 |
ElementType.TYPE_PARAMETER | 类型变量的声明语句前,@since 1.8 |
ElementType.TYPE_USE | 应用于所有使用类型的任何语句(如:泛型,类型转换等),@since 1.8 |
ElementType.MODULE | Moduel声明,@since 9 |
注意:
-
ElementType.ANNOTATION_TYPE
表示的是用此注解的注解A用于对注解B进行声明。例如@Target
注解就是注解A,查看其源码即可看出来。@Target
源码:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
- 鉴于有的老哥说
ElementType.TYPE
里没有注解声明,我贴一下JDK9源码,大家自行甄别,如果有错误请指出,^_^
/** Class, interface (including annotation type), or enum declaration */
TYPE,
-
JDK8新增的两个值,类型注解
ElementType.TYPE_USE
,从源码中字面意思可以看成使用类型的地方,例如(泛型,类型转换、有返回值的方法,implements和throws语句)。ElementType.TYPE_PARAMETER
,表示此注解可以在Type的声明语句前,可用在定义类或方法的泛型前面。
//在有返回值的方法
@MyAnnotation
public double run(){
return 1.0;
}
//在泛型上
List<@MyAnnotation String> list = new ArrayList<>();
//在throws语句
public void run()throws @MyAnnotation Exception{
System.out.println("--");
}
//类型转换
int i = (@MyAnnotation int)10.6;
//implements后
public class task implements @MyAnnotation Runnable
//new对象时
Thread t = new @MyAnnotation Thread();
2.3.2 元注解:@Retention
该注解表示自定义的注解保留到什么阶段,也就是生命周期。它的取值有三种,如下表所示。
注解取值 | 功能描述 |
---|---|
RetentionPolicy.SOURCE | 注解保留到源码阶段 |
RetentionPolicy.CLASS | 注解保留到字节码阶段 |
RetentionPolicy.RUNTIME | 注解保留到运行阶段,程序可以通过反射获取该注解 |
3 使用注解
2.1 自定义简单注解
了解到这里我们先不管元注解来创建一个简单的注解,元注解后面讲解。
自定义注解代码:
public @interface MyAnnotation {
//int类型的属性,没有默认值
int id() ;
//double类型的属性,有默认值
double salary() default 10000.0;
//String数组类型的属性,没有默认值
String[] info();
}
使用注解代码:
package com.gsgb.MyAnnotation;
@MyAnnotation(id=1,salary = 15000,info={"guo","li"})
public class AnnotationTest {
private String name;
@MyAnnotation(id=1,info={"guo","li"})
public AnnotationTest(){ }
@MyAnnotation(id=1,info={"guo","li"})
public void run(String nickname){
int num = 8 ;
System.out.println(nickname+"run...");
}
public static void main(String[] args) {
AnnotationTest annotation = new AnnotationTest();
annotation.run("老王");
}
}
通过上面的代码,我们不难发现,带有默认值的属性salary可写可不写,因此:
- 有默认值的属性在使用注解的时候可以不赋值。
- 没有默认值的属性在使用注解的时候必须赋值。
注:此注解因为没有约束,所以在包、类、字段、构造器、方法、参数、成员变量等位置都可以使用
2.2 特殊的属性名value
注解有一个特殊的属性名叫value,该属性在使用注解的时候在有些情况下可以不写value名称,我们分情况讲解。
-
当自定义注解只有value属性名时,在使用注解的时候可以省略value名称只写值。例如:
属性: String value();
@MyAnnotation("hello") public void run(String nickname){ int num = 8 ; System.out.println(nickname+"run..."); }
-
当自定义注解除了value还有别的属性的时候,其他属性至少有一个没有默认值的时候,value不可省略。
属性: String value(); int id();
@MyAnnotation(value="hello",id=100) public void run(String nickname){ int num = 8 ; System.out.println(nickname+"run..."); }
-
当自定义注解除了value还有别的属性的时候,其他属性有默认值的时候,value可省略。
属性: String value(); int id() default 50;
@MyAnnotation("hello") public void run(String nickname){ int num = 8 ; System.out.println(nickname+"run..."); }
2.3 元注解实例
自定义注解代码
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.CLASS)//表示注解保留到字节码文件
@Target(ElementType.METHOD)//表示注解是用于方法上的
public @interface MyAnnotation {
//int类型的属性,没有默认值
int id() ;
//double类型的属性,有默认值
double salary() default 10000.0;
//String数组类型的属性,没有默认值
String[] info();
}
自定义注解测试类
public class AnnotationTest {
private String name;
public AnnotationTest(){ }
//该注解只能用在方法上,用在其他地方会报错
//而且由于设置了RetentionPolicy.CLASS,所以此注解会保留到字节码文件
@MyAnnotation(id=1,info={"guo","li"})
public void run(String nickname){
int num = 8 ;
System.out.println(nickname+"run...");
}
public static void main(String[] args) {
AnnotationTest annotation = new AnnotationTest();
annotation.run("老王");
}
}
4 注解解析
4.1 注解解析概念和相关方法
注解解析就是通过代码获取注解数据的过程。
与注解解析相关的接口有Annotation
和AnnotatedElement
:
Annotation
:是所有注解类型的公共接口。AnnotatedElement
:定义了注解解析相关的方法,如下表所示
方法 | 功能描述 |
---|---|
<T extends Annotation> T getAnnotation(Class<T> annotationClass) | 获得当前对象指定的注解,如果没有则返回空。 |
Annotation[] getAnnotations() | 返回此元素上存在的所有注解。(包括从父类继承的注解) |
Annotation[] getDeclaredAnnotations() | 返回此元素上存在的所有注解。(不包括从父类继承的注解) |
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) | 判断当前对象是否有指定的注解,有则返回 true,否则返回 false。 |
Class
, Constructor
, Field
, Method
等都是AnnotatedElement
接口的实现类,所以都有上面的方法。
4.2 注解解析案例
注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface MyAnnotation {
String game();
String[] Company() default {"Tencent","Roit"};
}
使用注解
@MyAnnotation(game="DOTA2")
public class MyAnnotationTest {
@MyAnnotation(game="LOL")
public void play(){
System.out.println("玩游戏..");
}
}
解析注解
public class GetAnnotationTest {
//获取类上的注解
@Test
public void getAnnotationOnClass(){
Class<MyAnnotationTest> clazz = MyAnnotationTest.class;
if(clazz.isAnnotationPresent(MyAnnotation.class)){
MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class);
System.out.println("游戏名:"+annotation.game());
System.out.println("公司:"+annotation.Company()[0]+"和"+annotation.Company()[1]);
}
}
/*
运行结果:
游戏名:DOTA2
公司:Tencent和Roit
*/
//获取方法上的注解
@Test
public void getAnnotationOnMethod() throws NoSuchMethodException {
Class<MyAnnotationTest> clazz = MyAnnotationTest.class;
Method playMethod = clazz.getMethod("play");
if(playMethod.isAnnotationPresent(MyAnnotation.class)){
MyAnnotation annotation = playMethod.getAnnotation(MyAnnotation.class);
System.out.println("游戏名:"+annotation.game());
System.out.println("公司:"+annotation.Company()[0]+"和"+annotation.Company()[1]);
}
}
}
/*
运行结果:
游戏名:LOL
公司:Tencent和Roit
*/