【Java新特性学习 一】JDK5: 注解的概念和使用

Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。Java 语言中的类、方法、变量、参数和包等都可以被标注

  • 注解和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。
  • 注解也支持自定义,即自定义Java标注。

通俗的说注解就是给所标注的对象打标签,对所标注对象进行描述、限制、验证等一系列操作。

注解基本概念

日常开发中新建Java类,我们使用class、interface比较多,而注解和它们一样,也是一种类的类型,修饰符为 @interface

创建注解

我们通过IDEA创建一个注解:
在这里插入图片描述
这样一个注解就被创建出来了:

package com.example.MyFirstJavaWeb.annotation;

public @interface MyFirstAnnotation {
}

注解属性

注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以无形参的方法形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。

public @interface MyFirstAnnotation {
    int id() default 1;
    String msg() default "hello Annotation";
}

当然注解属性也可以拥有默认值,这种情况下,在使用注解时可以不用显式赋值。

使用注解

使用注解也十分简单,我们在要使用的位置直接打注解即可:

@MyFirstAnnotation(id=1,msg="MyFirstAnnotation")
public class AnnotationFather {
}

当然此时注解内没有任何逻辑,没有作用效果,需要我们添加逻辑。这里先简单介绍下创建语法,然后聊聊注解相关的概念我们再去实际的去写一段逻辑。

注解的本质

注解的本质就是一个Annotation接口:

/**Annotation接口源码*/
public interface Annotation {

    boolean equals(Object obj);

    int hashCode();

    Class<? extends Annotation> annotationType();
}

通过以上源码,我们知道注解本身就是Annotation接口的子接口,也就是说注解中其实是可以有属性和方法,但是接口中的属性都是static final的,对于注解来说没什么意义,而我们定义接口的方法就相当于注解的属性,也就对应了前面说的为什么注解只有属性成员变量,其实他就是接口的方法,这就是为什么成员变量会有括号,不同于接口我们可以在注解的括号中给成员变量赋值

元注解

元注解顾名思义我们可以理解为注解的注解,它是作用在注解中,方便我们使用注解实现想要的功能。元注解分别有@Retention、 @Target、 @Document、 @Inherited和@Repeatable(JDK1.8加入)五种

Retention

Retention 的英文意为保留期的意思。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间,它的取值如下:

  • RetentionPolicy.SOURCE :注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
  • RetentionPolicy.CLASS :注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
  • RetentionPolicy.RUNTIME :注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。

Retention其实明确了该注解的作用域,在什么时候才能得到使用。这里我们让我们的注解一直可以被保留到运行时:

@Retention(RetentionPolicy.RUNTIME)
public @interface MyFirstAnnotation {
    int id() default 1;
    String msg() default "hello Annotation";
}

Documented

顾名思义,这个元注解肯定是和文档有关。它的作用是能够将注解中的元素包含到 Javadoc 中去,我们也将这个元注解增加到我们的注解中去:

@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyFirstAnnotation {
    int id() default 1;
    String msg() default "hello Annotation";
}

Target

Target 是目标的意思,@Target 指定了注解运用的地方。当一个注解被 @Target 注解时,这个注解就被限定了运用的场景。类比到标签,原本标签是你想张贴到哪个地方就到哪个地方,但是因为 @Target 的存在,它张贴的地方就非常具体了,比如只能张贴到方法上、类上、方法参数上等等。

在这里插入图片描述

一般比较常用的是ElementType.TYPE类型,这里我们给我们的注解也加上限定注释目标的元注解:

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
public @interface MyFirstAnnotation {
    int id() default 1;
    String msg() default "hello Annotation";
}

Inherited

Inherited的英文意思是继承,但是这个继承和我们平时理解的继承大同小异,一个被@Inherited注解了的注解修饰了一个父类,如果他的子类没有被其他注解修饰,则它的子类也继承了父类的注解。我们给自己的注解加上这个元注解:

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Inherited
public @interface MyFirstAnnotation {
    int id() default 1;
    String msg() default "hello Annotation";
}

可以理解为,父类Father注解了MyFirstAnnotation,那么Son继承Father也自动继承了它的注解。

@MyFirstAnnotation(id=1,msg="MyFirstAnnotation")
public class AnnotationFather {
}

AnnotationChildren 继承了AnnotationFather类

public class AnnotationChildren extends AnnotationFather{

    public static void main(String[] args){

        //获取Son的class对象
        Class<AnnotationChildren> sonClass = AnnotationChildren.class;
        // 获取Son类上的注解MyTestAnnotation可以执行成功
        MyFirstAnnotation annotation = sonClass.getAnnotation(MyFirstAnnotation.class);
        System.out.println(annotation.annotationType());
    }
}

我们看下打印结果:
在这里插入图片描述

Repeatable

Repeatable的英文意思是可重复的。顾名思义说明被这个元注解修饰的注解可以同时作用一个对象多次,但是每次作用注解又可以代表不同的含义。多次注解的方法依赖容器注解的使用,首先定义一个容器注解:

package com.example.MyFirstJavaWeb.annotation;


import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Inherited
public @interface Volume
{
    MyFirstAnnotation[]  value();   //注解的属性
}

按照规定,它里面必须要有一个 value 的属性,属性类型是一个被 @Repeatable 注解过的注解数组,也就是我们的自定义注解:

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Inherited
@Repeatable(Volume.class)
public @interface MyFirstAnnotation {
    int id() default 1;
    String msg() default "hello Annotation";
}

我们的自定义注解的Repeatable元注解正是使用了容器注解:@Repeatable(Volume.class),使用的时候可以这样:

@MyFirstAnnotation(id=1,msg="MyFirstAnnotation")
@MyFirstAnnotation(id=2,msg="MyFirstAnnotation2")
@MyFirstAnnotation(id=3,msg="MyFirstAnnotation3")
public class AnnotationFather {
}

我们可以这么理解,AnnotationFather 想使用不同属性值的MyFirstAnnotation注解,但是一次只能用一个,所以它求助于Repeatable,通过引入容器注解解决它的需求。

Java预置注解

通过以上内容我们大致定义了一个没有任何实现的注解,其实Java内部已经预定义好了各种注解可以直接使用:

  • @Deprecated@Deprecated 所标注内容,不再被建议使用。
  • @Override@Override 只能标注方法,表示该方法覆盖父类中的方法。
  • @SuppressWarnings@SuppressWarnings 所标注内容产生的警告,编译器会对这些警告保持静默。

这些预置注解都是可以直接使用的,当然引入第三方依赖以及框架依赖后预置可使用的注解数量会大大增多。

自定义注解的使用

当我们自定义一个注解的之后,不光是把注解的标签打到目标的类或方法或其它元素上,我们还期望在运行时获取到它的属性值来进行相应的操作,这才是一个完整的闭环。

定义注解

定义注解的部分其实从上边的流程我们已经处理的比较清晰了,这里我们定义一个简单的人员注解:

package com.example.MyFirstJavaWeb.annotation;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Inherited
public @interface PersonAnnotation {
    String name();
    int age() ;
    String sex();

}

再定义一个给属性使用的注解:

package com.example.MyFirstJavaWeb.annotation;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.FIELD)
@Inherited
public @interface NameAnnotation {
    int value() default 1;
}

以及一个给方法使用的注解:

package com.example.MyFirstJavaWeb.annotation;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.METHOD)
@Inherited
public @interface PlayGameAnnotation {
    int value() default 1;
}

标注注解

定义好后,我们给AnnotationFather进行注解标记:

package com.example.MyFirstJavaWeb.javabean;

import com.example.MyFirstJavaWeb.annotation.NameAnnotation;
import com.example.MyFirstJavaWeb.annotation.PersonAnnotation;
import com.example.MyFirstJavaWeb.annotation.PlayGameAnnotation;


@PersonAnnotation(name = "tml",age=27,sex = "男")
public class AnnotationFather {

    @NameAnnotation
    private String name="张三";
    private int age=20;
    private String sex="女";

    @PlayGameAnnotation
    public void playGame(){

    }

}

提取注解

使用注解,也就是提取注解的方法如下:

 /**是否存在对应 Annotation 对象*/
  public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
        return GenericDeclaration.super.isAnnotationPresent(annotationClass);
    }

 /**获取 Annotation 对象*/
    public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
        Objects.requireNonNull(annotationClass);
        return (A) annotationData().annotations.get(annotationClass);
    }
 /**获取所有 Annotation 对象数组*/   
 public Annotation[] getAnnotations() {
        return AnnotationParser.toArray(annotationData().annotations);
    }    

提取注解的方式如下:

package com.example.MyFirstJavaWeb.javabean;

import com.example.MyFirstJavaWeb.annotation.NameAnnotation;
import com.example.MyFirstJavaWeb.annotation.PersonAnnotation;
import com.example.MyFirstJavaWeb.annotation.PlayGameAnnotation;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) throws NoSuchMethodException {

        /**
         * 获取类注解属性
         */
        Class<AnnotationFather> fatherClass = AnnotationFather.class;
        boolean annotationPresent = fatherClass.isAnnotationPresent(PersonAnnotation.class);
        if(annotationPresent){
            PersonAnnotation annotation = fatherClass.getAnnotation(PersonAnnotation.class);
            System.out.println(annotation.name());
            System.out.println(annotation.age());
            System.out.println(annotation.sex());
        }

        /**
         * 获取属性和方法注解
         */
        try {
            Field name = fatherClass.getDeclaredField("name");
            boolean annotationPresent1 = name.isAnnotationPresent(NameAnnotation.class);
            if(annotationPresent1){
                NameAnnotation annotation = name.getAnnotation(NameAnnotation.class);
                System.out.println(annotation.value());
            }

            Method play = AnnotationFather.class.getDeclaredMethod("playGame");
            boolean annotationPresent2 = play.isAnnotationPresent(PlayGameAnnotation.class);
            if (annotationPresent2){
                PlayGameAnnotation annotation = play.getAnnotation(PlayGameAnnotation.class);
                System.out.println(annotation.value());
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}

打印的结果如下:
在这里插入图片描述

注解处理【APT】

和提取注解类似,提取注解后可以使用一些业务代码逻辑来进行处理。之后在应用到这些的时候详细说说这方面内容。说白了处理逻辑都是可以自定义的,所以注解具体是什么取决于你怎么使用它。

注解的应用

注解主要针对的是编译器和其它工具软件(SoftWare tool)。,注解一般有如下的用途:

  • 编译检查: 编译器可以利用注解来检测出错误或者警告信息,打印出日志。
  • 编译自动处理【插入式注解】: 软件工具可以用来利用注解信息来自动生成代码、文档或者做其它相应的自动处理,例如loombook,
  • 运行时处理【反射】: 某些注解可以在程序运行的时候接受代码的提取,自动做相应的操作,例如使用注解进行参数化的配置
  • 生成帮助文档:通过给 Annotation 注解加上 @Documented 标签,能使该 Annotation 标签出现在 javadoc 中

当开发者使用了Annotation 修饰了类、方法、Field 等成员之后,这些 Annotation 不会自己生效,必须由开发者提供相应的代码来提取并处理 Annotation 信息。这些处理提取和处理 Annotation 的代码统称为 APT(Annotation Processing Tool)

总结一下

注解是什么?注解就类似一个标签,打在类、方法或者属性上。描述、验证以及限制这些目标,在运行时可以通过反射获取注解的属性值,又能有动态配置的感觉。而其在编译阶段的作用又有编译检查和自动生成代码的作用,最后还有生成帮助文档的作用。总而言之注解就是方便我们编码的工具,有了注解很多固定逻辑可以被反复复用,例如一个属性值的最大、最小范围等都可以用注解标识,而免去了大量的逻辑代码。注解的大量使用在Spring,那个时候估计还要拿出来深入研究一番

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
本书综合介绍了使用J2SE(Java 2 Standard Edition)5.0或更高版本开发动态程序的过程,阐述了Java语言的各个方面,包括Java语言结构和工作原理的基本知识,用大量的实例讲述如何开发功能齐全的Java应用程序,以期帮助读者掌握Java语言的最新特性Java类库中主要的功能包,积累Java应用环境的使用经验,并在核心技术领域打下坚实的基础。本书系统全面、浅显易懂,非常适合没有任何编程经验的初学者阅读,也可作为软件开发人员和高校师生的必备参考书。通过阅读本书,你可以借助J2SE 5.0这个最新、最出色的Java平台来学习Java编程。J2SE 5.0拥有强大的新特性和新功能,使Java性能产生了巨大的飞跃。新的Java平台所带来的丰富内涵使本书相比于过去的版本似乎稍厚了些,但是并不说明新的内容更复杂。当然,要想成为熟练的Java程序员,所需的基础知识会略有增多,但都不困难。只要有进取心和一点点对编程的领悟力,你就可以得到足够多的知识,成为一个高效的Java程序员。本书提供了规范化的学习方法,可帮助你事半功倍地完成学习。你不仅会学到一项相当流行的技术,而且做完一件值得做的事情将使人心旷神怡。尝试读本书吧,你肯定会喜欢的! 作为理想的面向对象的程序设计语言,Java以自身的简单性和强大功能成为Internet编程和跨平台开发中最常用的开发语言。本书以规范化的方式讲解Java编程。在介绍Java语言各个方面的过程中,一边解释有关Java语言结构和工作原理的基本知识,一边以大量的代码实例讲述如何开发功能齐全的Java应用程序。本书系统全面、浅显易懂,涵盖了从入门到精通的所有知识,以期帮助读者掌握Java语言的最新特性Java类库中主要的功能包,积累Java应用环境的使用经验,并在核心技术领域打下坚实的基础。本书主要内容:有关Java语言程序工作原理的基本知识;运用语言要素的方法;在程序中保存数据的方法;定义及实例化类的方法;在交互式Web网页中建立applet的方法;实现图形化用户程序的技术;利用JDBC功能使用关系型数据库;成为一个熟练的Java程序员所需的一切知识。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

存在morning

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值