java 注解学习

来源:互联网 发布:域名注册网站哪个好 编辑:程序博客网 时间:2024/06/03 18:27

今天刚刚学习了 java 的注解和反射的一些内容,虽然说还不是很全面,但是还是要记录下来的。这样自己才会有积累。

我们平时做java开发,使用的最多的就是框架,而现在的框架不用注解的很少很少,Spring, Herbinate, JPA 等等,都是建立在注解和反射的基础上面实现的。

关于注解

注解我们使用起来很简单,小到我们继承一个类时写的 @Override,大到我们使用@Controller, @Resource来编写JavaEE 程序,但是,我们往往只是使用别人写好的注解,知其然,不知其所以然。

那我们就从一个最简单的例子开始看起:

@Override

这样的一个注解大家都见过,在编程的时候,如果重载一个类的函数而没有添加上这样的一个标记的话,我们可爱的eclipse随即就会报错,我们保存的时候,eclipse 总是不厌其烦的帮助我们添加上这样的一个标记。但是如果没有这个标记,我们手工编写代码并且编译的话,编译器同样也不会报错。这样一个看似没用的标记,有什么用呢?

  • 标记这个函数是一个重载的函数,

  • 如果有这样的一个标记的话,编辑器会根据这样的查找父类/接口是否有这样的一个函数,如果没有的话,就说明我们的函数写错了,比如 toString写成了 toStirng 等。

这样的一个接口的实现则是:

@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public @interface Override {}

这里我们看到了另外的一种定义 ‘对象’ 的方法, @interface, 这个方法在我看来,它是一个像 interface, class 一样的一个关键字,只是它出现的时间很晚,又和interface长得很像,功能很像,所以没有给它一个关键字的名分。 所有的关键字注解都要有这样的一个 @interface 定义,就像所有的 类都需要 class 一样。

@Retention(RetentionPolicy.SOURCE)

retention 的中文意思是保留,保持。这个是一个 ‘元注解’,至于什么是元注解,你可以简单理解为java虚拟机帮我们预先定义的一个注解,让我们用它来生成其他注解的一种注解。 这个注解的意思是告诉我们我们编写的注解可以保留到java编译运行的哪一个程度,在哪个位置丢弃这个注解。而这一个程度,就是由 RetentionPolicy 这个枚举类来定义。 这个枚举类有三项:SOURCE,CLASS,RUNTIME。他们分别代表着我们的三种情况:

  • 使用SOURCE 保留的注解,只在源文件中保留,在编译期间就会被抛弃。
  • 使用CLASS 保留策略的注解,在编译后会 被存储到 .class 文件中, 但是在运行时JVM不会去获取这些注解。
  • 使用RUNTIME保留策略的注解,在编译后会被存储到 .class文件中,并且可以在 jvm运行时获取这些注解。

由此我们可以看出,@Override 只是 SOURCE 层面的注解,在编译期间被抛弃,但是,编译器同样会在编译的时候对注解的内容进行检查,这个就是SOURCE 策略的用途。 同样的比如说 @SuppressWarnings这个注解,它同样在编译时被抛弃,它的作用仅仅是告诉编译器/编译器,这个地方有异常,我注意到了并且处理好了,从而减少编程的bug。

CLASS 层面的保留策略javadoc 是这么说的:

Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time. This is the default behavior.

这里它告诉我们,这个一个默认的注解,即,如果我们没有明确指定@Retention 的话, 这个就是默认的注解。关于这个注解,我在网上及书上没有找到他的相关用法,不过,据我猜测,JVM 在运行时不能够调用这个注解,那他可能就是在classloader 加载class 文件的时候,对文件的检查有一定的作用。

RUNTIME 层面的注解是最多的,因为我们的注解不一定都是为了代码的安全问题。同样我们还需要在运行时对代码做一定的修饰。比如说, Junit 中的 @Before, @After 就是控制一个类的执行流程,Spring中的@Resource,则是将一些数据依赖注入到我们的类中的函数或属性中去。

RUNTIME 保留策略的注解并不能直接让我们的程序就那样的执行起来,它的作用就像在代码中加了一个个的锚,我们会用另外的程序来读取这个class 文件,并获取这些锚,比如说我们通过@Resource 注解反射获取到一个属性的位置,然后通过反射的方法将这个位置上属性给赋值。这个就是RUNTIME的作用。


说完@Retention 注解,我们再说 @Target 注解,这个注解的参数内容相当多,它的值同样是一个枚举类 java.lang.annotation.ElementType 。我们直接在它的源代码上写上相应的注释。

public enum ElementType {    /** Class, interface (including annotation type), or enum declaration */    TYPE,    /** Field declaration (includes enum constants) */    FIELD,    /** Method declaration */    METHOD,    /** Formal parameter declaration */    PARAMETER,    /** Constructor declaration */    CONSTRUCTOR,    /** Local variable declaration */    LOCAL_VARIABLE,    /** Annotation type declaration */    ANNOTATION_TYPE,    /** Package declaration */    PACKAGE,    /**     * Type parameter declaration     *     * @since 1.8     */    TYPE_PARAMETER,    /**     * Use of a type     *     * @since 1.8     */    TYPE_USE}

这个注解定义了我们定义的注解可以使用在什么地方,比如说我们的@Override 注解只能够使用在方法上,因为它是 @Target(ElementType.METHOD) 类型的注解。
当然,我们也可以让一个注解使用在多个地方,但此时定义的时候就是用大括号将枚举常量包含进来,比如说@RequestMapping注解就是放在类和函数上的,所以它就是@Target({ElementType.METHOD,ElementType.TYPE})


除了这@Retention 和 @Target 这两个元注解之外,还有三个比较重要的元注解,不过,没有上面两个复杂,它们分别是:@Repeatable, @Document,@Inherited

@Repeatable 表示被注解的注解可以多次的注解一个元素,没有这个注解,则不能持续注解一个元素。举个例子:

@Document@Target(ElementType.TYPE)@interfacce Anno{    String value()}@Anno(value="1")@Anno(value="2")@Anno(value="3")class Cl{}

@Document 是标记接口,用于通知某个工具,注解将被文档化

@Inherited 表示被注解的类是可以继承这个注解的,比如说:

@Document@Target(ElementType.TYPE)@interfacce Anno{    String value()}@Anno(value="1")@Anno(value="2")@Anno(value="3")class A{}class B extends A{}

那么在这种情况下,B 同样拥有A类的三个注解。

还有一点比较重要的是,注解 对象的内容。我们举一个例子

@Target(ElementType.TYPE)@interface Anno{    String value() default "hello world";    int count();}@Anno(count = 1)class Hello{}

在上面的这个例子中,我们可以看见注解的参数是 像 声明一个函数一样声明在 注解对象中的, 比如我们想要一个 String 类型的 value 参数, 我们就声明 String value(); 而不是像声明属性一样:String value;!
我们在这里同样可以声明一个参数的默认值,default default_value; 如果没有声明,我们必须在使用注解的时候强制赋值。否则,编译就会报错。而拥有默认值的参数就可以不用强制赋值。

今天就写到这里,明天,将注解的 驱动, 反射给写掉。

0 0