小小程序员的深入浅出系列(1):Java Annotation,搞懂java注解,这一篇就够了

1 概述

开题三板斧,What(是什么) ? Why(为什么用) ? How(怎么用) ?

1.1 java annotation 是什么?

Java Annotations 可以让我们为我们的代码增加元数据(metadata),并且这些元数据可以不属于程式本身.注解是在JDK 5中加入的,且注解对于代码的执行不会有影响.

1.2 java annotation 的使用

  • 先来三个平时最经常使用的内置(Build-in)注解压惊 @Deprecated, @Override & @SuppressWarnings, 这三个注解告诉编译器需要做的工作.对,我们先来个脸熟的压压惊.
 package java.lang;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Annotation type used to mark methods that override a method declaration in a
 * superclass. Compilers produce an error if a method annotated with @Override
 * does not actually override a method in a superclass.
 *
 * @since 1.5
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

这里是@Override的源码,现下我们仅仅看注解上的内容: 这个注解标记方法为重写了父类中的方法,编译器需要产生一个错误如果该方法没有覆盖父类中的方法.另外两个读者可以自行查询源码.

  • 通俗一点来说注解可以指导编译器的部分工作,也可以在运行时去生成代码或者xml等等,以及可以在运行时为java 反射工具提供需要的帮助,我们将在后面一步步深入,用我的潜水老师蹩脚的中文来说就是:慢慢来.

  • 此处看来java注解除开这三个常见的用法之外还只是无关痛痒的特性,但是假如您有阅读各个java 类库(比如EventBus)的源码您会发现少了如果少了注解的理解将寸步难行.

2 慢慢深入java annotation

java注解总以@符号开始,后面跟着注解的名称,如@Override ,符号@告诉编译器这是个注解.

2.1 我们能在什么地方用注解呢

java注解可以在类/接口/方法/属性上使用,例如:

@Override
void handle() { 
    //Do something 
}

这个注解告诉编译器,这个方法handle()需要覆盖父类中同样为handle()的方法.

2.2 java语言中现有的注解以及如何自定义注解

该篇的内容将会是整篇的重点,我们将从java的内置(build-in)注解以及工具包(java.lang.annotation.*)两处入手来彻底的理解注解.

2.2.1 java 内置注解

java有三个内置注解:

  • @Override
  • @Deprecated
  • @SuppressWarnings

@Override: 当需要在子类中重写父类的方法时,我们用该注解加在方法上, 从可读性上来说指出该方法是要被重写的方法,并且当不小心修改父类的方法后(修改名称或者增减参数),编译器会抛出异常.这样可以避免后续难以想象的debug灾难. 虽然还想再解释点什么但是我相信重写是每个java程序员知识体系基础中的基础就不多言了.
@Deprecated: Deprecated注解说明当前被标记的类/方法/属性是废弃的以及不应该再被使用的,编译器会为被标记的类/方法/属性生成warning .
@SuppressWarnings: 该注解告诉编译器忽略明确的警告,比如调用了上文提到的被@Deprecated标记的方法或者类.

@SuppressWarnings("deprecation")
public  void  callDeprecatedMethod() {
        a.depreactedMethod(); //调用被废弃的方法
}

看完了三个内置的注解,也许心细的读者就会有问题,为什么@Override 只能被用在方法上,@Deprecated却可以用在类/方法/属性上呢.在开始下节内容之前我们先比较一下两个注解的不同.
接下来是Override的源码:

package java.lang;
import java.lang.annotation.*;
/**
 * Indicates that a method declaration is intended to override a
 * method declaration in a supertype. If a method is annotated with
 * this annotation type compilers are required to generate an error
 * message unless at least one of the following conditions hold:
 *
 * <ul><li>
 * The method does override or implement a method declared in a
 * supertype.
 * </li><li>
 * The method has a signature that is override-equivalent to that of
 * any public method declared in {@linkplain Object}.
 * </li></ul>
 *
 * @author  Peter von der Ahé
 * @author  Joshua Bloch
 * @jls 9.6.1.4 @Override
 * @since 1.5
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

再对比下Deprecated

package java.lang;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
/**
 * A program element annotated @Deprecated is one that programmers
 * are discouraged from using, typically because it is dangerous,
 * or because a better alternative exists.  Compilers warn when a
 * deprecated program element is used or overridden in non-deprecated code.
 *
 * @author  Neal Gafter
 * @since 1.5
 * @jls 9.6.3.6 @Deprecated
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

注意@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})@Target(ElementType.METHOD)的使用区别,接下来将具体讲解.

2.2.2 创建自定义注解

难点从这里开始.我们可以使用@interface 来创建注解,例如

public @interface Override {
}

注解也能有自己的属性,看起来像是方法,例如:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;  

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyMethodAnnotation {
    String uri();
    String[] methods() default {"GET"};
}

在上面的示例代码中,我们有2个属性.还有若干的注解标签(@Target,@Retention 等等...),所有的注解都继承java.lang.annotation.Annotation,所以注解不能再继承其他的任何类.那在开始学习这些注解之前,我们先看下Annotation源码.

package java.lang.annotation;
/**
 * The common interface extended by all annotation types.  Note that an
 * interface that manually extends this one does <i>not</i> define
 * an annotation type.  Also note that this interface does not itself
 * define an annotation type.
 *
 * More information about annotation types can be found in section 9.6 of
 * <cite>The Java™ Language Specification</cite>.
 *
 * The {@link java.lang.reflect.AnnotatedElement} interface discusses
 * compatibility concerns when evolving an annotation type from being
 * non-repeatable to being repeatable.
 *
 * @author  Josh Bloch
 * @since   1.5
 */
public interface Annotation {
  
     *
     * @return true if the specified object represents an annotation
     *     that is logically equivalent to this one, otherwise false
     */
    boolean equals(Object obj);

    /**
     * Returns the hash code of this annotation, as defined below:
     * @return the hash code of this annotation
     */
    int hashCode();

    /**
     * Returns a string representation of this annotation.  The details
     * @return a string representation of this annotation
     */
    String toString();

    /**
     * Returns the annotation type of this annotation.
     * @return the annotation type of this annotation
     */
    Class<? extends Annotation> annotationType();
}

Annotation注释里第一句话就说明了Annotation是注解的父类,至于其他提供的方法equals, hashCode, toString为Object基础方法(如果有疑问可以看Effective Java中的介绍,这边不多加篇幅).接下来利用刚才定义的注解我们来做一个简单的测试:

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

class MyEasyAnnotationTest {

    public MyEasyAnnotationTest(){}
    //此数我们没有定义 methods 但是会使用default {"GET"}
    @MyAnnotation.MyMethodAnnotation(
            uri="/"
    )
    public void annotationMethods(){
        System.out.println("annotationMethods");
    }
}

public class Main {

    public static void main(String[] args) {
        Class<MyEasyAnnotationTest> clazz = MyEasyAnnotationTest.class;
        try {
            Method method  = clazz.getMethod("annotationMethods");
            MyAnnotation.MyMethodAnnotation myMethodAnnotation =  method.getAnnotation(MyAnnotation.MyMethodAnnotation.class);
            System.out.println("MyMethodAnnotation is child of Annotation ? :"+ (myMethodAnnotation instanceof Annotation));  //MyMethodAnnotation is child of Annotation ? :true
            System.out.println("uri:"+myMethodAnnotation.uri() );         //uri:/
            for (String allowedMethod : myMethodAnnotation.methods()) {
                System.out.println("allowedMethod:"+allowedMethod);       //allowedMethod:GET
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

输出结果符合我们之前的预期.
接下来我们挑两个常用的注解深入挖掘,此处可当笔记百科:
@Target:指定程序元定义的注释所使用的地方,它使用了另一个类:ElementType,是一个枚举类定义了注释类型可以应用到不同的程序元素以免使用者误用。看看java.lang.annotation 下的源代码:

@Documented  
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.ANNOTATION_TYPE)  
public @interface Target {  
    ElementType[] value();  
} 

ElementType是一个枚举类型,指明注释可以使用的地方,看看ElementType类:

public enum ElementType {  
     TYPE, // 指定适用点为 class, interface, enum  
     FIELD, // 指定适用点为 field  
     METHOD, // 指定适用点为 method  
     PARAMETER, // 指定适用点为 method 的 parameter  
     CONSTRUCTOR, // 指定适用点为 constructor  
     LOCAL_VARIABLE, // 指定使用点为 局部变量  
     ANNOTATION_TYPE, //指定适用点为 annotation 类型  
     PACKAGE // 指定适用点为 package  
} 

@Retention:这个元注释和java编译器处理注释的注释类型方式相关,告诉编译器在处理自定义注释类型的几种不同的选择,需要使用RetentionPolicy枚举类。此枚举类只有一个成员变量,可以不用指明成名名称而赋值,看Retention的源代码:

@Documented  
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.ANNOTATION_TYPE)  
public @interface Retention {  
    RetentionPolicy value();  
} 

有个RetentionPolicy类,也是一个枚举类,具体看代码:

public enum RetentionPolicy {  
     SOURCE, // 编译器处理完Annotation后不存储在class中  
     CLASS, // 编译器把Annotation存储在class中,这是默认值  
     RUNTIME // 编译器把Annotation存储在class中,可以由虚拟机读取,反射需要  
} 

此处算是非常简单的写了一个介绍,自定义注解活跃在各个广受欢迎的开源库中,如Retrofit,EventBus,Struct2等等, 这篇写起来自己都觉得不够尽兴,本来想模拟Retrofit利用注解写一个简单的请求路由处理,但会导致篇幅过长,争取在下一篇内容中EventBus3.0的源码分析中再次结合java注解进行分析.感谢各位的时间!!!

参考资料:除了java.lang.annotation.*源码与Retrofit 以及EventBus源码之外还有:http://beginnersbook.com/2014/09/java-annotations/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容

  • 什么是注解(Annotation):Annotation(注解)就是Java提供了一种元程序中的元素关联任何信息和...
    九尾喵的薛定谔阅读 3,123评论 0 2
  • 一、什么是注解? 注解对于开发人员来讲既熟悉又陌生,熟悉是因为只要你是做开发,都会用到注解(常见的@Overrid...
    _Justin阅读 1,335评论 0 10
  • 本文章涉及代码已放到github上annotation-study 1.Annotation为何而来 What:A...
    zlcook阅读 29,081评论 15 116
  • 内容概要 Annotation的概念 Annotation的作用 Annotation的分类 系统内置注解 元注解...
    DevinZhang阅读 4,134评论 0 28
  • 李敖说我不看电视 电视的毛病并非它的内容全部要不得。也不是全部庸俗讨厌。电视的毛病出在它陪你养成一个坏习惯——一个...
    伍帆阅读 534评论 0 0