annotation的定义与使用
annotation定义
- 注解,或者叫做注释类型,英文单词是:Annotation
- 注解Annotation是一种引用数据类型。编译之后也是生成xxx.class文件。
annotation的语法格式
[修饰符列表] @interface 注解类型名{
}
idea中创建注解类:
类的内容:
package com.hyg.annotation;
public @interface MyAnnotation {
}
annotation可以使用在的地方
注解可以使用在类上、属性上、方法上、变量上等,也可以使用在注解类型上。默认情况下注解可以出现在任何地方。
package com.hyg;
import com.hyg.annotation.MyAnnotation;
//使用在类上
@MyAnnotation
public class MyNanotationTest {
//使用在属性上
@MyAnnotation
private int i;
//使用在构造方法上
@MyAnnotation
public MyNanotationTest(){}
//使用在静态方法上
@MyAnnotation
public static void test(){
//使用在局部变量上
@MyAnnotation
int j=100;
}
//使用在成员方法上、形参上
@MyAnnotation
public void test1(@MyAnnotation int a){
}
}
//使用在接口上
@MyAnnotation
interface MyInterface{
}
//使用在枚举上
@MyAnnotation
enum Season{
SPRING,SUMMER,AUTUMN,WINTER
}
package com.hyg.annotation;
//使用在注解上
@MyAnnotation
public @interface OtherAnnotation {
}
JDK内置的注解和元注解
java.lang包下的注释类型:
- Deprecated 用 @Deprecated 注释的程序元素,不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。
Deprecated 表示注解的方法已过时,可以使用在方法和类上,构造函数上
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
public class DeprecatedTest {
@Deprecated
public void test(){
System.out.println("deprecated");
}
}
在运行时编译器会有提示:
Warning:(6, 19) java: com.hyg.DeprecatedTest中的test()已过时
- Override 表示一个方法声明打算重写超类中的另一个方法声明。
@Override
public String toString(){
return "test";
}
Override 可以看到这个注解里什么都没有,他就是用来给编译器检查使用的,和运行阶段没有关系,只能出现在方法上,用来检查是否是重写父类的方法,如果这个方法不是重写父类的方法,编译器就会报错。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
- SuppressWarnings 指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告。
元注解
一个注解用来修饰注解类型的一般称为元注解
常见的元注解:
Target:这个注解是用来标注被标识的注解可以出现在哪些位置上。
@Target(ElementType.METHOD) //表示被修饰的注解只能出现在方法上
ElementType是一个枚举类型,里面的属性都表示可以出现的位置
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** 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
}
Retention:这个注解是来表示被标注的注解最终保存到哪里
RetentionPolicy枚举类
@Retention(RetentionPolicy.SOURCE)//表示该注解只被保留在java源文件中,在编译成class文件后就不会存在于class文件中
@Retention(RetentionPolicy.CLASS)//表示该注解被保留在class文件中
@Retention(RetentionPolicy.RUNTIME)//表示该注解被保留在class文件中,并且可以被放射读取
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* 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,
/**
* 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
}
注解中的属性
我们通常在注解中自定义属性,下面的name就是MyAnotation的属性,看着像方法,实际上称之为属性name。
public @interface MyAnnotation {
String name();
}
使用:
注意当我们的注解中有属性的时候,在使用它就必须为属性赋值,否则报错
赋值方式就是(属性名=属性值),至于属性值是什么类型取决于注解中属性的类型,有多个属性就得意义赋值
@MyAnnotation(name = "myTest")
public void doTest(){
}
多个属性的使用:
public @interface MyAnnotation {
String name();
int age();
}
@MyAnnotation(name = "myTest",age=18)
public void doTest(){
}
给属性指定默认值,当我们给属性指定默认值之后,在使用时可以不加上已经指定默认值的属性。
public @interface MyAnnotation {
String name();
int age() default 18;
}
@MyAnnotation(name = "myTest")
public void doTest(){
}
关于value的使用:如果注解中的属性名字为value,并且只有一个属性的话,在使用的使用value属性名可以省略不写,只有属性名为value才可以省略,其他的属性名不可以
public @interface OtherAnnotation {
String value();
}
@OtherAnnotation(value = "hahaha")
public void doOther(){
}
@OtherAnnotation("hahaha")
public void doOther1(){
}
再来看看,当一个注解中属性名有value并且也有多个属性的情况下value的属性名是否可以省略不写,从下面的截图中可以看到报错了,所以说明当注解中仅有一个属性并且属性名必须为value在使用的时候才可以省略属性名。
public @interface OtherAnnotation {
String value();
int age();
}
注解的属性类型
注解中的属性类型:
byte、short、int、long、float、double、boolean、char、String、Calss、枚举类型以及以上类型的数组形式。
public @interface MyAnnotation {
int age() default 18;
int[] value();
String name();
String[] value1();
Season value2();
Season[] value3();
Class paramterType();
Class[] paramterTypes();
}
public enum Season {
SPRING,SUMMER,AUTUMN,WINTER
}
属性是数组的使用:
public @interface OtherAnnotation {
String[] value();
int age();
}
当数组中只有一个值时,大括号可以省略
@OtherAnnotation(value = {"hahaha","hehehehe"},age=18)
public void doOther(){
}
@OtherAnnotation(value = "hahaha",age=18)
public void doOther1(){
}
属性是枚举类型
public @interface OtherAnnotation {
String[] value();
int age();
Season[] season();
}
@OtherAnnotation(value = {"hahaha","hehehehe"},age=18,season = Season.AUTUMN)
public void doOther(){
}
@OtherAnnotation(value = "hahaha",age=18,season = {Season.AUTUMN,Season.SPRING,Season.SUMMER})
public void doOther1(){
}
@OtherAnnotation(value = "hahaha",age=18,season = {AUTUMN,SPRING,SUMMER})
public void doOther2(){
}
annotation的使用
经过上面的讲解,我们应该会怎么去定义和在使用注解了,现在就往下看看注解在项目中的真正的用法。
- 反射判断类上是否存在注解
首先在注解上声明该类可以使用的地方和保存的方式,注意只有注解上@Retention(RetentionPolicy.RUNTIME) 才可以被放射获取到
@Target(value = {TYPE,FIELD,METHOD,})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
int age() default 18;
String name();
}
@MyAnnotation(name = "test")
public class MyTest {
@MyAnnotation(name = "myTest")
public void doTest(){
}
@OtherAnnotation(value = {"hahaha","hehehehe"},age=18,season = AUTUMN)
public void doOther(){
}
@OtherAnnotation(value = "hahaha",age=18,season = {AUTUMN,SPRING,SUMMER})
public void doOther1(){
}
}
测试反射获取判断注解是否存在
public class ReflectTest {
public static void main(String[] args) throws ClassNotFoundException {
//先获取class对象
Class c=Class.forName("com.hyg.MyTest");
//判断该类上是否有@MyAnnotation注解
if(c.isAnnotationPresent(MyAnnotation.class)){
//做相关的逻辑处理
System.out.println("做相关的逻辑处理");
}
else {
System.out.println("该类上没有@MyAnnotation注解");
}
}
}
结果
获取类上的注解对象和注解的属性:
public class ReflectTest {
public static void main(String[] args) throws ClassNotFoundException {
//先获取class对象
Class c=Class.forName("com.hyg.MyTest");
//判断该类上是否有@MyAnnotation注解
if(c.isAnnotationPresent(MyAnnotation.class)){
//获取类上的注解,可以获取多个 Annotation[] annotations = c.getAnnotations();
MyAnnotation annotation = (MyAnnotation) c.getAnnotation(MyAnnotation.class);
System.out.println("类上的注解对象"+annotation);
//获取注解的属性,跟调用接口一样
String name = annotation.name();
int age = annotation.age();
System.out.println(name+age);
//做相关的逻辑处理
System.out.println("做相关的逻辑处理");
}
else {
System.out.println("该类上没有@MyAnnotation注解");
}
}
}
获取方法上注解的相关信息:
public class ReflectMethod {
public static void main(String[] args) throws Exception {
//先获取类
Class c=Class.forName("com.hyg.MyTest");
//在获取方法
Method doTest = c.getDeclaredMethod("doTest");
//判断方法上是否有@MyAnnotation注解
if (doTest.isAnnotationPresent(MyAnnotation.class)){
//获取方法上的注解
MyAnnotation annotation = (MyAnnotation) doTest.getAnnotation(MyAnnotation.class);
System.out.println("方法上的注解对象"+annotation);
//获取注解的属性
String name = annotation.name();
int age = annotation.age();
System.out.println(name+age);
//做相关的逻辑处理
System.out.println("做相关的逻辑处理");
}
else {
System.out.println("该类上没有@MyAnnotation注解");
}
}
}
结果: