Java注解学习

        之前有段时间非常想学一下Java的注解,但一直都比较懒,没有完整的学下来。但最近在B站上看到了一个非常好的视频,看完后觉得非常好,因此写下文档作为积累。(视频时黑马程序员的双元课程基础班的视频,具体是谁的并不知道。)

简介

        注解(Annotation),也叫元数据,是代码级别的说明(注释)。它是JDK 1.5版本以后引入的一个特性,与类、接口、枚举是同一个层次。它可以生命在包、类、字段、方法、方法参数等的前面,用来对这些元素进行说明、注释。

        注解可以理解为标注解释的简写,其作用就像一个标签信息,而单纯的标签并无意义,还需有解析标签的机制,标注+解析,两个操作组合起来,注解的作用就发挥出来了。

        注解的作用主要有三点:

        1.编写文档:通过代码里标识的元数据生成文档[javadoc文档]

        2.代码分析:通过代码里标识的元数据对代码进行分析(使用反射)

        3.编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查(eg. Override)

使用注解

内置注解

        Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。Java 7后新增了三个注解。(2个在java.lang,1个在java.lang.annotation)

        作用在代码上的注解有3个(java.lang)中:

        代码位置:libcore/ojluni/src/main/java/java/lang

        1.@Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。

        2.@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。

        3.@SuppressWarnings - 指示编译器去忽略注解中声明的警告。

                一般传参all,如: @SuppressWarnings("all")

        4.@SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。

        5.@FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。(lambda表达式必备?)

        作用在注解上的注解(Java.lang.annotation),也称为元注解:

        代码位置:libcore/ojluni/src/main/java/java/lang/annotation

        1.@Retention - 标识这个注解被保留的阶段。

                参数取值:RetentionPolicy类型数据

                        SOURCE:源码阶段,一般给编译器使用,自定义注解很少会使用这个。

                        CLASS:字节码阶段,但虚拟机不会去加载。(这个是否可被用于字节码插桩中使用?)

                        RUNTIME:运行时阶段,虚拟机会加载。一般自定义注解使用此阶段。

        2.@Documented - 标记这些注解是否包含在用户文档中。使用该元注解的注解,可在生成的javadoc上看到自定义注解的描述。

        3.Target - 标记这个注解应该是哪种 Java 元素上。

                参数取值:ElementType[]

                        TYPE:注解作用在Java类型上,包括class、interface(含annotation)、枚举类型。

                        FIELD:注解作用在Java字段上,包括枚举类型的枚举值。

                        METHOD:注解作用在方法上。

                        PARAMETER:注解作用在参数上。

                        CONSTRUCTOR:注解作用在构造方法上。(METHOD是否包括这个?)

                        LOCAL_VARIABLE:注解作用在局部变量上。

                        ANNOTATION_TYPE:注解作用在注解类型上。(TYPE是否包含这个)

                        PACKAGE:注解作用在包上。

                        TYPE_PARAMETER:注解作用在Java类型的参数上,Java 8引入。

                        TYPE_USE:Use of a type?Java 8 后引入。

        4.@Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

                说明:使用这个标记的注解,如果某个具有父子类关系的两个类中,父类使用了注解(@Inherited标记的注解),则子类会继承了父类的这个注解。验证方式:1.如下面的参考博客写代码验证。2.注解使用@Documented标注,然后生成父子类的doc进行验证。

                参考:@Inherited详解_qq_43390895的博客-CSDN博客@Inherited是一个标识,用来修饰注解如果一个类用上了@Inherited修饰的注解,那么其子类也会继承这个注解注意:接口用上个@Inherited修饰的注解,其实现类不会继承这个注解父类的方法用了@Inherited修饰的注解,子类也不会继承这个注解当用了@Inherited修饰的注解的@Retention是RetentionPolicy.RUNTIME,则增强了继承性,在反...https://blog.csdn.net/qq_43390895/article/details/100175330        5.@Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

               说明:@Repeatable 注解是用于声明其它类型注解的元注解,来表示这个声明的注解是可重复的。@Repeatable的值是另一个注解,其可以通过这个另一个注解的值来包含这个可重复的注解。

        参考:java中@Repeatable的理解_看山也看水的博客-CSDN博客_@repeatable@Repeatable是jdk8中新增的注解,使用如Spring中的@ComponentScan注解。在没有@Repeatable注解的的注解中,在同一个地方使用相同的注解会报错,有了此元注解注解的注解,就可以在同一个地方使用相同的注解。其官方文档如下The annotation type {@code java.lang.annotation.Repeatable} is used to ...https://blog.csdn.net/weixin_42245133/article/details/99678509

自定义注解

        上面学完用于描述注解的元注解后,可以看看注解是怎么写的了。以SuppressWarnings注解为例说明,说一下带有参数的自定义注解是如何编写和使用的。

package java.lang;

import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;


@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    /**
     * The set of warnings that are to be suppressed by the compiler in the
     * annotated element.  Duplicate names are permitted.  The second and
     * successive occurrences of a name are ignored.  The presence of
     * unrecognized warning names is <i>not</i> an error: Compilers must
     * ignore any warning names they do not recognize.  They are, however,
     * free to emit a warning if an annotation contains an unrecognized
     * warning name.
     *
     * <p> The string {@code "unchecked"} is used to suppress
     * unchecked warnings. Compiler vendors should document the
     * additional warning names they support in conjunction with this
     * annotation type. They are encouraged to cooperate to ensure
     * that the same names work across multiple compilers.
     * @return the set of warnings to be suppressed
     */
    String[] value();
}

        注解使用@interface来进行声明定义,在注解的定义上,会使用一些元注解来对我们自定义的注解进行描述。

        抽象出来就是:

元注解
public @interface 注解名称() {
    返回值   属性名称()
}

注解属性

        注解内可添加我们需要的属性,默认使用value进行命名,也可使用其他的名字命名。

        注解的属性并不是单纯的字段或方法,在传参时,可将属性看作是一个字段,使用value=“all”这种方式传参,而在声明时,其又像方法,带有括号和返回值。

        属性的返回值类型(也是数据类型)有以下取值:

        1.基本数据类型

        2.String类型

        3.枚举

        4.注解

        5.以上类型的数组。

        使用时的注意点:

        1.如果定义属性时,使用default关键字给属性默认初始值,则使用注解时可以不进行属性的赋值。

        2.如果只有一个属性需要赋值,并且属性的名字为value,则value可以省略,直接赋值。如SuppressWarnings 的Target元注解的赋值。

        3.数组赋值时,值使用{}包裹,如果数组中只有一个值,{}可以省略。

注解本质

        注解的本质其实就是一个特殊接口,如果Retention为RUNTIME时,虚拟机会为其创建一个匿名的实例。

        还是以SuppressWarnings为例,我们将其编译成class后再使用javap反编译,得出以下结果:

D:\Android\Android11\libcore\ojluni\src\main\java\java\lang>javac SuppressWarnings.java

D:\Android\Android11\libcore\ojluni\src\main\java\java\lang>javap SuppressWarnings.class
Compiled from "SuppressWarnings.java"
public interface java.lang.SuppressWarnings extends java.lang.annotation.Annotation {
  public abstract java.lang.String[] value();
}

        可以看到,虽然我们在定义注解时,并没有继承于任何东西,但实际上编译器会将@inerface自动继承了 java.lang.annotation.Annotation这个接口。因此注解的本质就是个接口。

注解的解析

        开篇也就说了,单纯的注解是并无意义的,只能说是一个标签用于标记,即便是内置的注解,如果没有IDE或者编译器去解析使用,也就无任何意义。

        因此,如果希望注解有意义,则必须要有去解析自定义注解的机制,两者相结合,才能发挥注解的作用。在代码中,一般注解都是使用反射的机制去进行解析处理的。

        本章主要是使用一个简单的例子进行说明,以作参考:

import java.lang.annotation.Annotation;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Inherited;
import java.lang.reflect.Method;

/**
 * Annotation在反射函数中的使用示例
 */
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
    String[] value() default "unknown";
}

/**
 * Person类。它会使用MyAnnotation注解。
 */
class Person {
   
    /**
     * empty()方法同时被 "@Deprecated" 和 "@MyAnnotation(value={"a","b"})"所标注
     * (01) @Deprecated,意味着empty()方法,不再被建议使用
     * (02) @MyAnnotation, 意味着empty() 方法对应的MyAnnotation的value值是默认值"unknown"
     */
    @MyAnnotation
    @Deprecated
    public void empty(){
        System.out.println("\nempty");
    }
   
    /**
     * sombody() 被 @MyAnnotation(value={"girl","boy"}) 所标注,
     * @MyAnnotation(value={"girl","boy"}), 意味着MyAnnotation的value值是{"girl","boy"}
     */
    @MyAnnotation(value={"girl","boy"})
    public void somebody(String name, int age){
        System.out.println("\nsomebody: "+name+", "+age);
    }
}

public class AnnotationTest {

    public static void main(String[] args) throws Exception {
       
        // 新建Person
        Person person = new Person();
        // 获取Person的Class实例
        Class<Person> c = Person.class;
        // 获取 somebody() 方法的Method实例
        Method mSomebody = c.getMethod("somebody", new Class[]{String.class, int.class});
        // 执行该方法
        mSomebody.invoke(person, new Object[]{"lily", 18});
        iteratorAnnotations(mSomebody);
       

        // 获取 somebody() 方法的Method实例
        Method mEmpty = c.getMethod("empty", new Class[]{});
        // 执行该方法
        mEmpty.invoke(person, new Object[]{});        
        iteratorAnnotations(mEmpty);
    }
   
    public static void iteratorAnnotations(Method method) {

        // 判断 somebody() 方法是否包含MyAnnotation注解
        if(method.isAnnotationPresent(MyAnnotation.class)){
            // 获取该方法的MyAnnotation注解实例
            MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
            // 获取 myAnnotation的值,并打印出来
            String[] values = myAnnotation.value();   // 方法对应了注解的属性声明
            for (String str:values)
                System.out.printf(str+", ");
            System.out.println();
        }
       
        // 获取方法上的所有注解,并打印出来
        Annotation[] annotations = method.getAnnotations();
        for(Annotation annotation : annotations){
            System.out.println(annotation);
        }
    }
}

        运行结果如下:

somebody: lily, 18
girl, boy, 
@com.skywang.annotation.MyAnnotation(value=[girl, boy])

empty
unknown, 
@com.skywang.annotation.MyAnnotation(value=[unknown])
@java.lang.Deprecated()

         以上代码来自于菜鸟教程。

参考
nullJava 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。 Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。 网上很多关于 Java Annotation 的文章,看得人眼..https://www.runoob.com/w3cnote/java-annotation.htmljava中@Repeatable的理解_看山也看水的博客-CSDN博客_@repeatable@Repeatable是jdk8中新增的注解,使用如Spring中的@ComponentScan注解。在没有@Repeatable注解的的注解中,在同一个地方使用相同的注解会报错,有了此元注解注解的注解,就可以在同一个地方使用相同的注解。其官方文档如下The annotation type {@code java.lang.annotation.Repeatable} is used to ...https://blog.csdn.net/weixin_42245133/article/details/99678509
@Inherited详解_qq_43390895的博客-CSDN博客@Inherited是一个标识,用来修饰注解如果一个类用上了@Inherited修饰的注解,那么其子类也会继承这个注解注意:接口用上个@Inherited修饰的注解,其实现类不会继承这个注解父类的方法用了@Inherited修饰的注解,子类也不会继承这个注解当用了@Inherited修饰的注解的@Retention是RetentionPolicy.RUNTIME,则增强了继承性,在反...https://blog.csdn.net/qq_43390895/article/details/100175330

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值