天天看点

Java学习笔记 16、注解

文章目录

  • ​​前言​​
  • ​​一、认识注解​​
  • ​​1.1、介绍注解​​
  • ​​1.2、开发中常见注解使用​​
  • ​​二、自定义注解​​
  • ​​2.1、自定义注解说明​​
  • ​​2.2、JDK1.5提供的四个元注解​​
  • ​​三、利用反射获取注解信息​​
  • ​​四、JDK8中注解的新特性​​
  • ​​4.1、可重复注解(两种方式)​​
  • ​​两种方式实现​​
  • ​​获取可重复注解的值(两种方式)​​
  • ​​4.2、新增类型注解(2个)​​
  • ​​参考文章​​

前言

      本篇文章是对之前学习java基础知识的再整理,通过看视频以及查阅博客进行汇总整理。部分图引用的是尚硅谷的教案,觉得比较好,若有侵权,请联系我删除。

      博客文章汇总:博客目录索引(持续更新)

一、认识注解

1.1、介绍注解

​注解​

​​(Annotation):从​

​JDK5.0​

​开始,Java增加了对元数据(MetaData)的支持,及注解。

​Annotation​

​就是代码中的特殊标记,这些标记可以通过设置在编译时、类加载时、运行时读取来执行相应的处理。通过注解可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。并且代码分析工具、开发工具和部署工具都可以通过这些补充信息进行验证部署。

  • ​Annoation​

    ​可以像修饰符一样使用,可用于修饰包、类、构造器、方法…之前,并且能够设置指定的值保存在Annoation中的属性里,例如name=value。

目的:在JavaSE中,注解的使用目的例如标记过时的功能(​

​@Deprecated​

​​), 忽略警告(​

​@SuppressWarnings​

​​;在JavaEE/Android开发中注解占据了更重要的角色,例如​

​Spring​

​​框架中的​

​AOP​

​通过注解来配置任意方法的切面,使用注解来代替JavaEE旧版中的繁冗代码与XML配置。

  • 未来的开发模式都是基于注解的,例如:​

    ​Spring2.5​

    ​​以上都是基于注解,​

    ​JPA​

    ​​,​

    ​Hibernate3.X​

    ​​,​

    ​Struts2​

    ​。
  • ​框架=注解+反射+设计模式​

1.2、开发中常见注解使用

使用注解前需要在前面加上​

​@​

​符号,可以将其当做一个修饰符来使用。

1、用于生成文档的相关注解

一般使用于注释中,用于在使用​

​javadoc​

​工具来生成文档

  • 类注释中:
  • ​@author​

    ​​:标明开发该类模块的作者,多个作者之间可使用​

    ​,​

    ​分割。
  • ​@version​

    ​:标明该类模块的版本。
  • ​@since​

    ​:从哪个版本开始增加的,我们常看到源代码注释中会标注。
  • ​@see​

    ​:参考转向,也就是相关主题。
  • 一般用于方法中:
  • ​@param​

    ​:用于在方法注释中,对方法中某参数的说明,如果没有参数就不能写。
  • ​@return​

    ​:用于在方法注释中,对方法返回值的说明,如果方法的返回值类型是void就不能写。
  • ​@exception​

    ​:对方法可能抛出的异常进行说明 ,如果方法没有用throws显式抛出的异常就不能写 其中
2、在编译时进行格式检查(JDK内置的三个基本注解)

一般修饰在方法:

  • ​@Override​

    ​: 限定重写父类方法,该注解只能用于方法,可以来检查是否为重写的方法。
  • ​@Deprecated​

    ​: 用于表示所修饰的元素(类, 方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择。
  • ​@SuppressWarnings​

    ​: 抑制编译器警告。
3、跟踪代码依赖性,实现替代配置文件功能

①Servlet3.0中提供的注解,使得不再需要在web.xml中进行Servlet的部署访问。

Java学习笔记 16、注解
  • 通过使用​

    ​@WebServlet("/login")​

    ​来代替掉下面的配置文件(减少xml配置)。

②​

​Spring​

​中事务的配置,只用一个注解来替换掉了配置文件

Java学习笔记 16、注解

二、自定义注解

2.1、自定义注解说明

对于自定义注解的说明:

  1. 定义​

    ​Annotation​

    ​​时需要使用​

    ​@interface​

    ​关键字。
  2. 自定义注解自动继承了​

    ​java.lang.annotation.Annotation​

    ​​接口,包含四个方法​

    ​equals()​

    ​​、​

    ​hashCode()​

    ​​、​

    ​toString()​

    ​​、​

    ​annotationType()​

    ​。
  3. ​Annoation​

    ​中的成员变量以无参数方法的形式来声明。
  • 其方法名和返回值定义了该成员的名字和类型,称之为配置参数。其类型只能是​

    ​8种基本数据类型​

    ​​、​

    ​String类型​

    ​​、​

    ​Class类型​

    ​​、​

    ​enum类型​

    ​​、​

    ​Annotation类型​

    ​​以及​

    ​上面所有类型的数组​

    ​。
  1. 可以在定义成员变量时指定参数值,可在成员变量后使用​

    ​default 默认值​

    ​来进行初始化。
  2. 若是​

    ​Annoation​

    ​​中只有一个参数成员,建议使用参数名为​

    ​value​

    ​。

使用说明:若是使用的注解中含有配置参数(即成员变量),那么使用时需要在()中指定参数值(对于有默认值的可以不写因为会自动赋值),例如:​

​@注解(参数=值)​

​​;若是​

​Annotation​

​​中只有一个成员那么我们在使用时可以省略​

​参数=​

​​,​

​例如:@注解(值)​

​。

注意:没有成员定义的Annotation称为​

​标记​

​​;包含成员变量的Annotation称为​

​元数据​

​。

自定义注解
public @interface MyAnnotation {
    String value() default "changlu";
}

@MyAnnotation(value = "changlu")  //或 @MyAnnotation("liner")   或  @MyAnnotation() 
class Person{
}      
  • ​@MyAnnotation(value = "changlu")​

    ​​与​

    ​@MyAnnotation("liner")​

    ​都相当于对其中的value重新赋值了,由于注解中只有一个成员所以可以省略参数直接填值。
  • ​@MyAnnotation() ​

    ​:会默认使用注解成员中的值。

2.2、JDK1.5提供的四个元注解

四个标准的元注解:​

​Retention​

​​、​

​Target​

​​、​

​Documented​

​​、​

​Inherited​

@Retention

​@Retention​

​:用来指定该Annotation的生命周期。

@Documented
@Retention(RetentionPolicy.RUNTIME)  //生命周期为运行时
@Target(ElementType.ANNOTATION_TYPE) //只可标注在注解类型上
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

//@Retention的成员返回值RetentionPolicy,一个枚举类
public enum RetentionPolicy {
    SOURCE,//在源文件中有效(仅在源文件中保留),编译器编译时会忽略该注解
    CLASS,//仅在class文件中有效(即class保留),当运行Java程序时,JVM不会保留注解。
    RUNTIME//:在运行时有效(即运行时保留),当运行 Java 程序时, JVM 会保留注释。程序可以通过反射获取该注释。
}      
Java学习笔记 16、注解
  • 该注解使用不同​

    ​RetentionPolicy​

    ​的枚举实例表示的不同生命周期
@Target

​@Target​

​:用于指定被修饰的Annotation能用于修饰哪些程序元素(如方法、类、包、参数…)。

@Documented
@Retention(RetentionPolicy.RUNTIME) //生命周期为运行时
@Target(ElementType.ANNOTATION_TYPE)//只可标注在注解类型上
public @interface Target {
    
    //是一个数组枚举类
    ElementType[] value();
}

//@Target的成员返回值是一个注解
public enum ElementType {
    TYPE,//使用于描述类、接口(包括注解类型)或enum声明
    FIELD,//使用于属性(包含注解内容)
    METHOD,//使用于方法
    PARAMETER,//参数
    CONSTRUCTOR,//用于描述构造器
    LOCAL_VARIABLE,//描述局部变量
    ANNOTATION_TYPE,//描述注解类型声明
    PACKAGE,//包
    TYPE_PARAMETER,//jdk1.8新增,表示该注解能写在类型变量的声明语句中(如:泛型声明)
    TYPE_USE//jdk1.8新增,表示该注解能写在使用类型的任何语句中
}      
  • ​@Tatget({ElementType.TYPE_USE})​

    ​:即可在任何语句中添加注解。
@Documented

​@Documented​

​: 用于指定被 修饰的 Annotation 类能够被 javadoc工具提取成文档。默认情况下,javadoc是不包括注解的。

  • 若定义了​

    ​@Documented​

    ​​的注解务必设置​

    ​@Retention​

    ​​的值为​

    ​Runtime​

    ​(生命周期为运行时)。
@Documented
@Retention(RetentionPolicy.RUNTIME) //生命周期是运行时
@Target(ElementType.ANNOTATION_TYPE) //只可标注在注解类型上
public @interface Documented {
}      
@Inherited

​@Inherited​

​:被其修饰的注解具有继承性,若某个类使用了被@Inherited修饰的注解,则其子类将自动具有该注解。

  • 实际使用较少。
@Documented
@Retention(RetentionPolicy.RUNTIME)//生命周期是运行时
@Target(ElementType.ANNOTATION_TYPE)//只可标注在注解类型上
public @interface Inherited {
}      

三、利用反射获取注解信息

自定义注解:

@Documented
@Target({ElementType.ANNOTATION_TYPE,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)//运行时
@Inherited
public @interface MyAnnotation {
    String value() default "changlu";
}      

通过反射获取到类的注解以及注解值:

@MyAnnotation("clle")
class Person{
}

class Student extends Person{
}

public class Test {
    public static void main(String[] args) {
        Class<Person> clazz = Person.class;
        //获取该类上指定的注解类型
        MyAnnotation anno = clazz.getAnnotation(MyAnnotation.class);
        System.out.println(anno.value());//clle

        //查看Student是否继承了Person的注解
        Annotation[] annotations = Student.class.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);//@xyz.changlu.MyAnnotation(value=clle)
        }
    }
}      
认识一下​

​AnnotatedElement​

​接口

Class实现了该接口,才会有获取注解的相关方法:

Java学习笔记 16、注解
  • ​isAnnotationPresent()​

    ​:此元素上是否存在指定注解类型,若存在返回true,不存在返回false。
  • ​getAnnotation()​

    ​:获取该元素上指定注解类型的实例。
  • ​getAnnotations()​

    ​:获取该元素上的所有注解实例。
  • ​getAnnotationsByType​

    ​:获取该元素上指定注解类的所有实例。

​Class​

​类的继承图:

Java学习笔记 16、注解

四、JDK8中注解的新特性

4.1、可重复注解(两种方式)

两种方式实现

什么是可重复注解呢?就是在同一个部分上定义两个或多个相同的注解,其中的值可不一样。

①jdk1.8之前,需要通过再定义一个注解,其中包含我们想要使用的注解数组
@Target({ElementType.ANNOTATION_TYPE,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)//运行时
@Inherited
public @interface MyAnnotation {
    String value() default "changlu";
}

//该注解中我们定义@MyAnnotation数组
@Target({ElementType.ANNOTATION_TYPE,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)//运行时
@Inherited
@interface MyAnnotations{
    //定义注解数组
    MyAnnotation[] value();
}      
@MyAnnotations({@MyAnnotation("changlu"),@MyAnnotation("liner")})
class Person{
}      
  • 我们通过定义​

    ​@MyAnnotations​

    ​注解的成员为注解数组形式来实现可重复注解。
②jdk1.8新增​

​@Repeatable​

​注解
@Target({ElementType.ANNOTATION_TYPE,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Repeatable(MyAnnotations.class)//注意:这里新增使用@Repeatable()其中值为一个Class类就是下面定义的MyAnnotations
public @interface MyAnnotation {
    String value() default "changlu";
}

@Target({ElementType.ANNOTATION_TYPE,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)//运行时
@Inherited
@interface MyAnnotations{
    //定义注解数组
    MyAnnotation[] value();
}      
  • 在​

    ​@MyAnnotation​

    ​​中使用了​

    ​@Repeatable(MyAnnotations.class)​

    ​注解,其中定义了指定一个容器注解。
@MyAnnotation("changlu")
@MyAnnotation("liner")
class Person{
}      

这样我们就能够直接在一个类上定义多个相同注解了,相较于第一种方式需要先声明​

​@MyAnnotations​

​,其中再包裹两个注解。

我们再看一下​

​@Repeatable​

​注解:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    
    Class<? extends Annotation> value();//其中成员的返回值为一个Class类型
}      

获取可重复注解的值(两种方式)

获取上面使用​

​@Repeatable​

​注解方法的设置的重复注解:

@MyAnnotation("changlu")
@MyAnnotation("liner")
class Person{
}      
public class AnnotationTest {
    public static void main(String[] args) {
        //方式一:通过getAnnotation()获取MyAnnotations容器注解的实例,接着遍历
        MyAnnotations annotation = Person.class.getAnnotation(MyAnnotations.class);
        MyAnnotation[] value = annotation.value();
        for (MyAnnotation myAnnotation : value) {
            System.out.println(myAnnotation.value());
        }
        //changlu
        //liner
    }

    //方式二:通过使用getAnnotationsByType()获取MyAnnotation注解数组,接着重复遍历即可
    @Test
    public void test(){
        MyAnnotation[] annos = Person.class.getAnnotationsByType(MyAnnotation.class);
        for (MyAnnotation anno : annos) {
            System.out.println(anno.value());
        }
        //changlu
        //liner
    }
}      
  • 通过查阅博客得知使用新增的​

    ​@Repeatable​

    ​,在编译器编译时实际上也是转换为使用的​

    ​MyAnnotations​

    ​注解表示。
@MyAnnotation("changlu")
@MyAnnotation("liner")      
  转换后
@MyAnnotations(value=[@MyAnnotation("changlu"),@MyAnnotation("liner")])
//并且在字节码中数组使用[]来表示      

对于获取单个注解值使用​

​getAnnotation()​

​​方法,获取重复注解使用​

​getAnnotationsByType()​

​。

详细内容可看我下面的参考文章[1]。

4.2、新增类型注解(2个)

也就是在​

​@Target​

​注解中的成员参数​

​ElementType​

​枚举类中添加了两个实例:

Java学习笔记 16、注解
  • ​ElementType.TYPE_PARAMETER​

    ​ :表示该注解能写在类型变量的声明语 句中(如:泛型声明)。
  • ​ElementType.TYPE_USE​

    ​:表示该注解能写在使用类型的任何语句中。

区别说明:在​

​Java 8​

​​之前,注解只能是在声明的地方所使用,​

​Java8​

​​开始,注解可以应用在任何地方(可直接使用​

​ElementType.TYPE_USE​

​)。

参考文章

[1]. ​​Java 8 可重复注解的理解与应用​​

我是长路,感谢你的阅读,如有问题请指出,我会听取建议并进行修正。

欢迎关注我的公众号:长路Java,其中会包含软件安装等其他一些资料,包含一些视频教程以及学习路径分享。

学习讨论qq群:891507813 我们可以一起探讨学习

注明:转载可,需要附带上文章链接