Java 注解基础知识


备注: 本文大部分内容及代码均来自网上,具体引用看末尾 8.参考引用**


  1. 概念及作用
  2. JDK注解
  3. 元注解
  4. 自定义注解
  5. 注解处理器
  6. Java 8 中注解新特性
  7. 注解的优缺点及与XML的比较
  8. 参考引用

1. 概念及作用

  • 1.1 概念

    • 注解即元数据,就是源代码的元数据
    • 注解在代码中添加信息提供了一种形式化的方法,可以在后续中更方便的 使用这些数据
    • Annotation是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。它是一种由JSR-175标准选择用来描述元数据的一种工具。
  • 1.2 作用

    • a. 生成文档
    • b. 跟踪代码依赖性,实现替代配置文件功能,减少配置。如Spring中的一些注解
    • c. 在编译时进行格式检查,如@Override等
  • 1.3 意义

    • 注解之前,XML被广泛的应用于描述元数据,XML的维护越来越糟糕
    • 在需要紧耦合的地方,比XML该容易维护,阅读更方便
    • 在需要比较多参数设置时,使用xml更方便,而在将某个方法声明为服务时这种紧耦合的情况下,比较适合注解
    • XML是松耦合的,注解是紧耦合的
    • 对于“XML VS 注解” ,可以google了解一下
    • 对于XML和注解的使用,要具体问题具体分析
    • Java的annotation没有行为,只能有数据,实际上就是一组键值对而已。通过解析(parse)Class文件就能把一个annotation需要的键值对都找出来

2. JDK注解

  • 2.1 Override: 保证编译时 要重写方法的正确性
  • 2.2 Deprected: 提示该方法已经过时
  • 2.3 SuppressWarnings: 关闭特定警告信息,参数如下:
    • all: to suppress all warnings
    • boxing: to suppress warnings relative to boxing/unboxing operations
    • cast: to suppress warnings relative to cast operations
    • dep-ann: to suppress warnings relative to deprecated annotation
    • deprecation: to suppress warnings relative to deprecation
    • fallthrough: to suppress warnings relative to missing breaks in switch statements
    • finally: to suppress warnings relative to finally block that don’t return
    • hiding: to suppress warnings relative to locals that hide variable
    • incomplete-switch: to suppress warnings relative to missing entries in a switch statement (enum case)
    • nls: to suppress warnings relative to non-nls string literals
    • null: to suppress warnings relative to null analysis
    • rawtypes: to suppress warnings relative to un-specific types when using generics on class params
    • restriction: to suppress warnings relative to usage of discouraged or forbidden references
    • serial: to suppress warnings relative to missing serialVersionUID field for a serializable class
    • static-access: to suppress warnings relative to incorrect static access
    • synthetic-access: to suppress warnings relative to unoptimized access from inner classes
    • unchecked: to suppress warnings relative to unchecked operations
    • unqualified-field-access: to suppress warnings relative to field access unqualified
    • unused: to suppress warnings relative to unused code

3. 元注解

  • 2.1 定义
    • 负责注解其他注解
  • 2.2 四种元注解
    • @Documented
    • @Target
    • @Retention
    • @Inherited
  • 2.3 @Documented
    • 用于描述其他类型的annotation应该被作为被标注的程序成员的公共API
    • 可以用于javadoc 此类的工具文档化
    • Document是一个标记注解,没有成员
  • 2.4 @Target
    • 说明Annotation所修饰的对象范围,即描述注解的使用范围
    • Annotation可被用于packages,types(类,接口,枚举,Annotation类型),类型成员(方法,构造方法,成员变量,枚举值),方法参数和本地变量(如循环变量,catch参数)
    • 在Annotation类型的声明中使用了target可更加明晰其修饰目标
    • 取值有:
类型 用途
CONSTRUCTOR 用于描述构造器
FIELD 用于描述域
LOCAL_VARIABLE 用于描述局部变量
METHOD 用于描述方法
PACKAGE 用于描述包
PARAMETER 用于描述参数
TYPE 用于描述类、接口(包括注解类型) 或enum声明
  • 2.5 @Retention
    • 定义了Annotation被保留的时间长短
    • 表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即被描述的注解在什么范围内有效)
    • Retention meta-annotation类型有唯一的value作为成员,它的取值来自java.lang.annotation.RetentionPolicy的枚举类型值。具体实例:
类型 用途 说明
SOURCE 在源文件中有效(即源文件保留) 仅出现在源代码中,而被编译器丢弃
CLASS 在class文件中有效(即class保留) 被编译在class文件中
RUNTIME 在运行时有效(即运行时保留) 编译在class文件中
  • 2.6 @Inherited
    • 是一个标记注解
    • 阐述了某个被标注的类型是被继承的
    • 使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类
    • @Inherited annotation类型是被标注过的class的子类所继承。类并不从实现的接口继承annotation,方法不从它所重载的方法继承annotation
    • 当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。

4. 自定义注解

  • 格式
public @interface 注解名{
    定义体
}
  • 注解参数的可支持数据类型:
    • 所有基本数据类型(int,float,double,boolean,byte,char,long,short)
    • String 类型
    • Class类型
    • enum类型
    • Annotation类型
    • 以上所有类型的数组
  • 修饰符只能是public 或默认(default)
  • 参数成员只能用基本类型byte,short,int,long,float,double,boolean八种基本类型和String,Enum,Class,annotations及这些类型的数组
  • 如果只有一个参数成员,最好将名称设为"value"
  • 注解元素必须有确定的值,可以在注解中定义默认值,也可以使用注解时指定,非基本类型的值不可为null,常使用空字符串或0作默认值
  • 在表现一个元素存在或缺失的状态时,定义一下特殊值来表示,如空字符串或负值
  • 示例:
package annotation;

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

/**
 * test注解
 * @author zlw
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestAnnotation {
    /**
     * id
     * @return
     */
    public int id() default -1;
    
    /**
     * name
     * @return
     */
    public String name() default "";
    
}

5. 注解处理器

  • 使用注解的过程,重要的是创建注解处理器
  • Java SE5 扩展了反射机制的API,以帮助程序员快速的构造自定义注解处理器
  • 注解处理器类库: java.lang.reflect.AnnotatedElement
    • Java使用Annotation接口来代表程序元素面前的注解,该接口是所有Annotation类型的接口。
    • java.lang.annotation.Annotation 是所有Annotation类型的父接口
    • java.lang.reflect.AnnotatedElement 可以接受注解的程序元素,该接口主要有Class(类定义),Constructor(构造器定义),Filed(类的成员变量定义),Method(类的方法定义),Package(类的包定义),
    • java.lang.reflect包所有提供的反射API扩充了读取运行时Annotation信息的能力,当一个Annotation类型被定义为运行时Annotation后,该注解才能运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取
    • AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的如下四个个方法来访问Annotation信息:
        方法1:<T extends Annotation> T getAnnotation(Class<T> annotationClass): 返回改程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
        方法2:Annotation[] getAnnotations():返回该程序元素上存在的所有注解。
        方法3:boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
        方法4:Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响
  • 示例:
/***********注解声明***************/

/**
 * 水果名称注解
 * @author peida
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
    String value() default "";
}

/**
 * 水果颜色注解
 * @author peida
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
    /**
     * 颜色枚举
     * @author peida
     *
     */
    public enum Color{ BULE,RED,GREEN};
    
    /**
     * 颜色属性
     * @return
     */
    Color fruitColor() default Color.GREEN;

}

/**
 * 水果供应者注解
 * @author peida
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
    /**
     * 供应商编号
     * @return
     */
    public int id() default -1;
    
    /**
     * 供应商名称
     * @return
     */
    public String name() default "";
    
    /**
     * 供应商地址
     * @return
     */
    public String address() default "";
}

/***********注解使用***************/

public class Apple {
    
    @FruitName("Apple")
    private String appleName;
    
    @FruitColor(fruitColor=Color.RED)
    private String appleColor;
    
    @FruitProvider(id=1,name="陕西红富士集团",address="陕西省西安市延安路89号红富士大厦")
    private String appleProvider;
    
    public void setAppleColor(String appleColor) {
        this.appleColor = appleColor;
    }
    public String getAppleColor() {
        return appleColor;
    }
    
    public void setAppleName(String appleName) {
        this.appleName = appleName;
    }
    public String getAppleName() {
        return appleName;
    }
    
    public void setAppleProvider(String appleProvider) {
        this.appleProvider = appleProvider;
    }
    public String getAppleProvider() {
        return appleProvider;
    }
    
    public void displayName(){
        System.out.println("水果的名字是:苹果");
    }
}

/***********注解处理器***************/

public class FruitInfoUtil {
    public static void getFruitInfo(Class<?> clazz){
        
        String strFruitName=" 水果名称:";
        String strFruitColor=" 水果颜色:";
        String strFruitProvicer="供应商信息:";
        
        Field[] fields = clazz.getDeclaredFields();
        
        for(Field field :fields){
            if(field.isAnnotationPresent(FruitName.class)){
                FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);
                strFruitName=strFruitName+fruitName.value();
                System.out.println(strFruitName);
            }
            else if(field.isAnnotationPresent(FruitColor.class)){
                FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class);
                strFruitColor=strFruitColor+fruitColor.fruitColor().toString();
                System.out.println(strFruitColor);
            }
            else if(field.isAnnotationPresent(FruitProvider.class)){
                FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class);
                strFruitProvicer=" 供应商编号:"+fruitProvider.id()+" 供应商名称:"+fruitProvider.name()+" 供应商地址:"+fruitProvider.address();
                System.out.println(strFruitProvicer);
            }
        }
    }
}

/***********输出结果***************/
public class FruitRun {

    /**
     * @param args
     */
    public static void main(String[] args) {
        
        FruitInfoUtil.getFruitInfo(Apple.class);
        
    }

}

====================================
 水果名称:Apple
 水果颜色:RED
 供应商编号:1 供应商名称:陕西红富士集团 供应商地址:陕西省西安市延安路89号红富士大厦

6. Java 8 中注解新特性

  • @Repeatable 元注解,表示被修饰的注解可以用在同一个声明式或者类型加上多个相同的注解(包含不同的属性值)
  • @Native 元注解,本地方法
  • java8 中Annotation 可以被用在任何使用 Type 的地方
    //初始化对象时
   String myString = new @NotNull String();
   //对象类型转化时
   myString = (@NonNull String) str;
   //使用 implements 表达式时
   class MyList<T> implements @ReadOnly List<@ReadOnly T>{
   ...
   }
   //使用 throws 表达式时
   public void validateValues() throws @Critical ValidationFailedException{
   ...
   }

7. 注解的优缺点及与XML的比较

  • 优:
    • 方便,简洁,配置信息和 Java 代码放在一起,有助于增强程序的内聚性
    • 若要对配置项进行修改,不得不修改 Java 文件,重新编译打包应用
  • 缺:
    • 分散到各个class文件中,维护性较差
    • 配置项编码在 Java 文件中,可扩展性差
  • 与XML比较:
    • 简洁
    • 没有XML配置更强大
    • 不便于修改,不便于统一管理

8. 参考引用

  1. 深入理解Java:注解(Annotation)--注解处理器
  2. 深入理解Java:注解(Annotation)自定义注解入门
  3. 深入理解Java:注解(Annotation)基本概念
  4. Java annotation的实例是什么类的?
  5. java中的注释
  6. JAVA 注解的几大作用及使用方法详解
  7. Java中的注解是如何工作的?
  8. Java中使用注解和使用配置文件各有什么优缺点
  9. JAVA注解
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容

  • Java 注解 Dependency injection 前言 Linus Benedict Torvalds :...
    CodePlayer_Jz阅读 1,024评论 0 8
  • 1. 元注解 元注解:用在注解上的注解,java1.5后添加的4个元注解: @Target @Retention ...
    小菜_charry阅读 385评论 1 2
  • 本文章涉及代码已放到github上annotation-study 1.Annotation为何而来 What:A...
    zlcook阅读 29,120评论 15 116
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,601评论 18 139
  • 2017/5/29起笔 着手写这文字的时候,我已经在家待两个星期了 两周前,还在学校里蹦哒的我,一下子被砸到深渊谷...
    Daylily_y阅读 1,043评论 7 11