注解概念
Annotation是从JDK5.0开始引入的新技术
Annotation的作用:
- 可以对程序作出解释,这一点和注释comment类似
- 对程序进行检查和约束,例如@Override
- 可以被其他程序(比如:编译器等)读取
Annotation的格式:
- 注解是以“@注释名”在代码中存在的,还可以添加一些参数值,例如:@SuppressWarning(value=”unchecked”)
Annotation在哪里使用:
- 可以附加在package、class、method、field等上面,相等于给他们添加了额外的辅助信息,然后结合反射机制实现对这些元数据的访问
JAVA的内置注解
@Override
定义在java.lang包中,此注解只适用于修饰方法,表示该方法打算重写父类中的同名方法,并且具有检查作用
public class Person {
public void test () {
}
}
class Student extends Person {
@Override
public void test () {
}
}
@Deprecated
定义在java.lang包中,此注释可以修饰方法、属性、类,注释@Deprecated的程序元素是程序员不鼓励使用的程序元素,通常是因为它是危险的,或者因为它已经过时了,然后存在更好的替代方法,但是你使用也没有任何影响
表示test()方法已经失效。
@SupressWarnings
定义在java.lang包中,用来抑制编译时产生的黄色警告信息,虽然这些警告信息不会影响编译结果,但是看着不舒服,然后该注释和前两个注释有所不同,你需要添加一个参数才能正确使用。
具体参数信息如下所示:
- @SuppressWarnings(“all”) // 压制所有警告
- @SuppressWarnings(“unchecked”) // 压制"unchecked"警告
- @SuppressWarnings(value={“unchecked”, “deprecation”}) // 压制"unchecked", "deprecation"警告
注意:public @interface SuppressWarnings {String[] value();}
,其中value是参数名,而String[]是参数是类型,当注解只使用value这一个参数的时候,value可以省略,例如上面的@SuppressWarnings(“all”)和@SuppressWarnings(“unchecked”)就是省略了前面的value,但是仅限参数名是value,并且注解中只使用value这一个参数的情况才可以 省略value,下面也会提到。
JAVA中的元注解
什么是元注解?元注解就是对其他普通注解进行说明解释的注解。
java中定义了4个元注解,自动继承了java.lang.annotation.Annotation
@Target
@Target限定了一个注解的使用范围,作用域,默认可以在任何地方使用,也可以指定使用的范围。
以**@Override注解为例,我们打开@Override**注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
点击进入**@Target(ElementType.METHOD)**
@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();
}
我们看到注解需要传入一个key为value的数组ElementType[] value()。点开ElementType[]
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE, // 可用于类、接口或enum声明
/** Field declaration (includes enum constants) */
FIELD, // 字段属性声明
/** Method declaration */
METHOD, // 方法声明
/** Formal parameter declaration */
PARAMETER, // 参数声明
/** Constructor declaration */
CONSTRUCTOR, // 构造方法声明
/** Local variable declaration */
LOCAL_VARIABLE, // 局部变量声明
/** Annotation type declaration */
ANNOTATION_TYPE, // 注释类型声明
/** Package declaration */
PACKAGE, // 包声明
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER, // 类型参数声明
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE // 类型的使用
}
ElementType枚举定义了各种注解使用的范围。
测试:
缺失value
不传参数直接报错,应为注解内部定义了ElementType[] value()
将自定义注解的作用域设置到方法上,只能在方法上使用,在类上使用报错。
传入两个参数作用域,限制注解在方法和类上使用,我们发现注解可以在类上面正常使用了。
@Retention
@Retention注解声明了注解使用的周期,用于定义注解的存活阶段,可以存活在源码级别、编译级别(字节码级别)、运行时级别。
以**@Override注解为例,我们打开@Override**注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
进入**@Retention(RetentionPolicy.SOURCE)**
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
注解需要传入一个key为value的RetentionPolicy,进入RetentionPolicy
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE, // 源码级别,注解只存在源码中,一般用于和编译器交互,用于检测代码。如@Override, @SuppressWarings。注解将被编译器丢弃
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS, // 字节码级别,注解存在于源码和字节码文件中,主要用于编译时生成额外的文件,如XML,Java文件等,
// 但运行时无法获得。 如mybatis生成实体和映射文件,这个级别需要添加JVM加载时候的代理(javaagent)
// 使用代理来动态修改字节码文件。
// 注解在class文件中可用,但会被VM丢弃
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME // 运行时级别,注解存在于源码、字节码、java虚拟机中,主要用于运行时,可以使用反射获取相关的信息。
// VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息
}
RetentionPolicy枚举定义了三个时间周期级别,大小是源码级别 < 字节码级别 < 运行时级别
。
@Documented
将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。相当与@see,@param 等。
注解里面什么都没有,只是简单的标注
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
@Inherited
允许子类继承父类中的注解。也只是一个简单的标注。
以上四个元注解重点掌握@Target、@Retention
自定义注解
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation
接口。
@interface 用来声明一个注解,格式是:修饰符 @interface 注解名{定义内容}
- 内容中的每一个方法实际上是声明了一个配置参数,方法的名称就是参数的名称,方法的返回值类型就是参数的类型
- 返回值类型只能是基本类型、Class、String、enum,如果返回值是String[]的时候,赋值的时候要用{“X1”, “X2”}这种形式;
- 可以通过default来声明参数的默认值,声明默认值的可以不在自定义注解中赋值,反之必须赋值
- 如果只有一个参数成员,一般参数名使用value字段,这是因为当参数名称是value并且自定义注解中只需要写一个参数的时候,可以省略参数名称,只有value可以这样,其他的都不行
- 注解元素必须有值,我们定义注解元素时,经常使用空字符串、0、-1等作为默认值,当默认值为-1,代表不存在
我们来定义一个注解:
@MyAnnotation2(age = 18, name = "张三")
public class Demo02 {
}
/**
* @author HCAN
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2 {
// 注解的参数:参数类型 + 参数名称();这里的括号不是表示方法,而是定义了注解一个参数
String name() default "";
int age();
int id() default -1;
String[] schools() default {"清华大学", "北京大学"};
}
说明:
1、自定义注解内部定义的name()这个是表示的定义的注解参数,不是方法。
2、如果在注解的参数定义的时候设置了default默认值,在使用参数的时候可以不传,如果没有定义就必须传参数,否则会提示报错。
3、注解的参数传入顺序和定义参数的顺序无关。比如name()在age()前面,但是我们使用注解的时候是可以把age放在name前面的。
扩展:
我们发现前面我们在查看@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();
}
我们明明将注解的参数名定义成了value(),为什么我们使用的使用的方式是@Target(ElementType.TYPE),没有使用@Target(value = ElementType.TYPE)
这是应为当我们注解参数只有一个的时候,我们可以把参数的名称定义为value(只能是value)
。这样我们在使用注解的时候可以不写value,直接传入参数即可
鸣谢
本文为狂神说学习笔记
学习地址:https://www.bilibili.com/video/BV1p4411P7V3