Java注解简介

注解很重要,但是难以理解,刚开始学习的时候,就有很多的问题。
注解到底是什么?
我见过最精辟的理解是:注解可以看作标签。有了标签,我们对客观事物的理解会更透彻,理解起来也会比较容易。比如说这里有一群人,那我们心里肯定会犯嘀咕,这是一群什么人,在这里干什么?如果给这一群人打上一个标签,学生。是不是对这群人有了一定的了解,在这里可能是开会,春游,或者干其它的事情。
我加了一个注解之后,它是怎样做处理的?
我们可以使用反射,提取相关属性,从而进行处理。简单来说就是定义一个标签,贴到客观事物的身上,再对这个客观事物进行处理。
大体脉络就是这个,剩下的就是填充细节。

一,自定义注解
1)定义一个注解

public @interface Man {
    String name();
    int height();
    int weight();
}

注解的@interface特别像接口的interface,只是前面多了一个@。下面的nameheightweight便是注解的属性。需要注意的是,这些属性可以是8种基本类型 ,String类型 ,Enum类型 ,Annotation注解类型,以及Class类型,当然也可以是它们的数组。
当然,一个注解也可以没有属性,这样的注解称为标识注解

public @interface Han {
}
public @interface Tang {
}

2)使用一个注解
使用上面的Man注解为例

@Man(name = "LiSi", height = 180, weight = 120)
public class LiSi {

    @Man(name = "0", height = 0, weight = 0)
    int age;

    @Man(name = "0", height = 0, weight = 0)
    public void sayHello(){
        System.out.println("Hello");
    }
}

我们可以使用注解来修饰类,属性以及方法。但是看这代码感觉怪怪的,是不是有种想要限制注解使用范围的感觉,这是元注解要做的事,在后面我们会讲到。

@Tang
public class LiShiMing {
}

标识注解因为没有属性,所以我们可以省略括号直接使用。
还有一种注解,只有一个属性,且属性名字是value的话,我们可以直接使用。

public @interface Age {
   int value();
}
@Age(37)
public class BingBing {
}

当然也可以指定属性值来使用。

@Age(value = 61)
public class BenShan {
}

但是我又有了一个小问题,如果还是注解只有一个属性,但是这个属性名不为value,那还可以这样使用吗?

public @interface Sex {
    String sex();
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以清楚的看到,如果名称不为value,必须明确指定。

public @interface People {
    boolean genuis() default false;
}

当然一些属性有默认值,比如People注解的genuis属性,默认值为false,我们在使用这个注解时,可以明确指定这个genuis属性,也可以不指定,这样的话,它的值便是false

@People(genuis = true)
public class ZhangSan {
}
@People
public class MaLiu {
}

因为People这个注解只有一个属性,且这个属性是默认的,所以我们可以不加括号直接使用。所以,如果我们遇到一个注解,它使用时可以直接使用,不加括号,不要直接认为这个注解是没有属性的,也可能它的属性是全部默认的,根据不同情况再对属性值做出改变。
二,元注解
元注解的概念我们在上面提到了,可以用元注解来修饰的注解,对注解做修正或者限定。
元注解总共有5种:@Retention@Documented@Target@Inherited@Repeatable

注解类型含义
@Retention注解保留时长
@Documented注解保留到JavaDoc中
@Target注解修饰的范围
@Inherited注解能够被自动继承
@Repeatablejava1.8加进来的,新特性,表示注解的值可以同时取多个

1)@Retention
注解的保留时长,取决于RetentionPolicy,分三种。

保留时长含义
SOURCE只在源码阶段存在,编译器编译时将会丢失掉。
CLASS在编译阶段会保留,但是在运行时不会加载到JVM虚拟机中。当我们没有指定保留方式时,这是默认的保留方式。
RUNTIME注解在编译和运行时,会被保留,这是生命周期非常长的一种保留方式,我们可以通过反射读取注解的值。
@Retention(RetentionPolicy.SOURCE)
public @interface People {
    boolean genuis() default false;
}

将注解的保留时长为运行时,此时可以通过反射获取到的,但是一般不建议,因为运行时注解,通过反射获取会造成一定运行效率的损耗。
2)@Documented
注解是否被例如JavaDoc此类的工具文档化,只负责标记。在默认情况下,JavaDoc是不包括注解的,但如果声明注解时指定了 @Documented,则它会被 JavaDoc之类的工具处理,,注解类型信息也会被包括在生成的文档中。

@Documented()
@Retention(RetentionPolicy.SOURCE)
public @interface People {
    boolean genuis() default false;
}

3)@Target
注解修饰的范围,取决于ElementType,默认是包含全部范围。

元素类型含义
TYPE类,接口(包括注解),枚举的声明
FIELD属性字段(包括枚举常量) 声明
METHOD方法声明
PARAMETER参数申明
CONSTRUCTOR构造方法
LOCAL_VARIABLE局部变量声明
ANNOTATION_TYPE注解声明
PACKAGE包声明
TYPE_PARAMETER类型参数,1.8之后
TYPE_USE使用类型,1.8之后
@Target(ElementType.TYPE)
@Documented()
@Retention(RetentionPolicy.SOURCE)
public @interface People {
    boolean genuis() default false;
}

4)@Inherited
表示注解类型能够被自动继承,但它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited注解的注解注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。

@Inherited
@Target(ElementType.TYPE)
@Documented()
@Retention(RetentionPolicy.SOURCE)
public @interface People {
    boolean genuis() default false;
}
@People
public class TestOne {

    public class TestTwo {

    }
}

People注解TestOne类,且TestTwo没有被其它注解注解,那么他的子类TestTwo也被People注解了。
5)@Repeatable
这是Java1.8加进来的特性,一个注解可能有多个属性值,使用@Repeatable便可以达到。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Persons {
    Person[] value();
}
@Repeatable(Persons.class)
public @interface Person {
    String value();
}
@Person("Bad")
@Person("Good")
public class Man {
}

一个Man可能是好人,也可能是坏人,它具有多个Person注解,所有我们使用Repeatable修饰Person注解,而其中的内容便是一个Person数组,即Persons
三,提取注解

@Retention(RetentionPolicy.RUNTIME)
public @interface Explain {
    String value();
}
public class Main {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Man.class;
        //获取类的注解
        if (clazz.isAnnotationPresent(Explain.class)) {
            Explain Explain = clazz.getAnnotation(Explain.class);
            System.out.println(Explain.value());
        }

        //获取字段注解
        Field nameField = clazz.getDeclaredField("name");
        if (nameField.isAnnotationPresent(Explain.class)) {
            Explain nameFieldExplain = nameField.getAnnotation(Explain.class);
            System.out.println(nameFieldExplain.value());
        }

        //获取构造函数注解
        Constructor constructor = clazz.getConstructor(new Class[]{String.class});
        if (constructor.isAnnotationPresent(Explain.class)) {
            Explain constructorExplain = (Explain) constructor.getAnnotation(Explain.class);
            System.out.println(constructorExplain.value());
        }

        //获取方法注解
        Method getMethod = clazz.getMethod("getName", new Class[]{});
        if (getMethod.isAnnotationPresent(Explain.class)) {
            Explain getMethodExplain = getMethod.getAnnotation(Explain.class);
            System.out.println(getMethodExplain.value());
        }

        Method setMethod = clazz.getMethod("setName", new Class[]{String.class});
        if (setMethod.isAnnotationPresent(Explain.class)) {
            Explain setMethodExplain = setMethod.getAnnotation(Explain.class);
            System.out.println(setMethodExplain.value());
        }
    }
}

结果:

This is man
This is name
This is constructor
This is getName
This is setName

注解必须要有这个@Retention(RetentionPolicy.RUNTIME)这个元注解,否则无法通过反射获取。
isAnnotationPresent()顾名思义,判断是否是目标注解。
getAnnotation()则是获取注解,从而获取属性值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值