Java学习笔记 16、注解

简介: Java学习笔记 16、注解

一、认识注解


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的部署访问。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YlCwnrSA-1613984978679)(https://gitee.com/changluJava/picture-bed/raw/master/mouse/20210221231848.png)]


通过使用@WebServlet("/login")来代替掉下面的配置文件(减少xml配置)。

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




二、自定义注解


2.1、自定义注解说明


对于自定义注解的说明:


定义Annotation时需要使用@interface关键字。

自定义注解自动继承了java.lang.annotation.Annotation接口,包含四个方法equals()、hashCode()、toString()、annotationType()。

Annoation中的成员变量以无参数方法的形式来声明。

其方法名和返回值定义了该成员的名字和类型,称之为配置参数。其类型只能是8种基本数据类型、String类型、Class类型、enum类型、Annotation类型以及上面所有类型的数组。

可以在定义成员变量时指定参数值,可在成员变量后使用default 默认值来进行初始化。

若是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 会保留注释。程序可以通过反射获取该注释。
}


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zmdpvsuI-1613984978684)(https://gitee.com/changluJava/picture-bed/raw/master/mouse/20210222151009.png)]


该注解使用不同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实现了该接口,才会有获取注解的相关方法:


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h8XjEarH-1613984978686)(https://gitee.com/changluJava/picture-bed/raw/master/mouse/20210222155559.png)]


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

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

getAnnotations():获取该元素上的所有注解实例。

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

Class类的继承图:


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9ZaYFZCA-1613984978690)(https://gitee.com/changluJava/picture-bed/raw/master/mouse/20210222160004.png)]



四、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枚举类中添加了两个实例:



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

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

区别说明:在Java 8之前,注解只能是在声明的地方所使用,Java8开始,注解可以应用在任何地方(可直接使用ElementType.TYPE_USE)。



相关文章
|
1天前
|
Java 编译器 API
Java中的注解:原理与实战
Java中的注解:原理与实战
|
1天前
|
Java API 数据安全/隐私保护
Java中使用注解的最佳实践
Java中使用注解的最佳实践
|
2天前
|
设计模式 安全 Oracle
Java学习笔记:从入门到精通
Java学习笔记:从入门到精通
|
3天前
|
Java Spring
JAVA注解:传统与现代的完美结合,你的代码值得拥有!
【6月更文挑战第29天】Java注解,作为连接传统与现代的编程工具,简化企业级应用开发,提升代码可读性和维护性。通过自定义注解如`@Loggable`,可以将行为(如日志记录)与方法实现分离,减少模板代码。使用AOP(如Spring)处理注解,实现行为拦截,增强代码灵活性和可扩展性。拥抱Java注解,让代码更现代、更高效!
28 16
|
3天前
|
IDE Java 程序员
JAVA注解大揭秘:为何程序员都爱它如命?
【6月更文挑战第29天】Java注解是元数据机制,用于在代码中嵌入信息供编译器、IDE和工具使用。它们以`@`标识,可用于类、方法等,用于编译时检查、代码生成(如Lombok的`@Getter`、`@Setter`)、框架集成(如Spring的`@Autowired`)。程序员喜欢注解因其简洁性、可读性和可扩展性,能减少冗余代码并增强代码的可理解性。
23 15
|
3天前
|
IDE Java 数据库连接
JAVA注解:元数据,代码的“身份证”?!
【6月更文挑战第29天】Java注解,作为代码的“身份证”,提供元数据,用于编译时检查、自动生成代码和框架集成。例如,@Override确保方法重写正确,@Deprecated标记过时,@Autowired在Spring中实现依赖注入。Lombok的@Getter/@Setter简化getter/setter。注解提升代码质量和效率,是现代Java开发的关键实践。
7 0
|
3天前
|
IDE Java 编译器
JAVA注解,你的代码需要的一次“心灵按摩”!
【6月更文挑战第29天】Java注解是提升代码可维护性的关键,它们是编译器和IDE理解代码意图的特殊标记,不同于仅作解释的注释。注解可用于编译时检查(如@Override、@NotNull)、自动生成代码(Lombok的@Getter、@Setter)、框架集成(Spring的@Autowired、MyBatis的@Mapper)。通过注解,代码变得更简洁、功能更强大,为项目带来效率提升。尝试使用注解,赋予代码新生命!
21 12
|
3天前
|
IDE Java 数据库连接
JAVA注解:那些年,我们错过的代码元数据!
【6月更文挑战第29天】Java注解,非执行代码的元数据,常被误解为注释。其实,它们支持编译时检查(如@Override)、自动生成代码、依赖注入(如Spring)和ORM映射(如Hibernate)。通过自定义注解,如示例中的`@MyAnnotation`,可在运行时通过反射增强方法功能。别再错过注解的力量,让它们提升代码的灵活性和可维护性!
9 0
|
3天前
|
IDE Java 编译器
深入解析JAVA注解:元数据如何改变编程世界
【6月更文挑战第29天】Java注解,作为元数据机制,为代码增添上下文信息,改变编程方式。注解标记在类、方法等上,不直接影响执行,但为编译器等提供额外信息。分为元注解、编译时和运行时注解,用于元数据提供、代码简化、提高可读性及自动化。示例展示了定义`@Loggable`注解来标记日志记录方法。注解广泛应用于依赖注入、ORM、Web服务等,提升效率和灵活性,是现代Java开发的关键。未来其应用将更广泛。
14 3
|
3天前
|
Java 编译器 数据库连接
JAVA注解:代码界的“隐形翅膀”?!
【6月更文挑战第29天】Java注解,编程的“隐形翅膀”,提供编译检查、框架集成及自定义元数据功能。如@Override确保方法重写正确,@Autowired在Spring中自动装配Bean。通过自定义注解,开发者能创造独特代码逻辑。例如,定义@SpecialProcessing注解标记需特殊处理的方法,增强代码可读性和可扩展性。利用注解,让代码飞翔在更广阔的世界。
11 1