注解

概念及作用

  1. 概念
  • 注解即元数据,就是源代码的元数据
  • 注解在代码中添加信息提供了一种形式化的方法,可以在后续中更方便的 使用这些数据
  • Annotation是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。它是一种由JSR-175标准选择用来描述元数据的一种工具。
  1. 作用
  • 生成文档
  • 跟踪代码依赖性,实现替代配置文件功能,减少配置。如Spring中的一些注解
  • 在编译时进行格式检查,如@Override等
  • 每当你创建描述符性质的类或者接口时,一旦其中包含重复性的工作,就可以考虑使用注解来简化与自动化该过程。

什么是java注解?

在java语法中,使用@符号作为开头,并在@后面紧跟注解名。被运用于类,接口,方法和字段之上,例如:


@Override
void myMethod() {
......
}

这其中@Override就是注解。这个注解的作用也就是告诉编译器,myMethod()方法覆写了父类中的myMethod()方法。

java中内置的注解

java中有三个内置的注解:

  • @Override:表示当前的方法定义将覆盖超类中的方法,如果出现错误,编译器就会报错。

  • @Deprecated:如果使用此注解,编译器会出现警告信息。

  • @SuppressWarnings:忽略编译器的警告信息。

本文不在阐述三种内置注解的使用情节和方法,感兴趣的请看这里

元注解

自定义注解的时候用到的,也就是自定义注解的注解;(这句话我自己说的,不知道对不对)

元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。

Java5.0定义的4个元注解:

  1. @Target
  2. @Retention
  3. @Documented
  4. @Inherited

java8加了两个新注解,后续我会讲到。

这些类型和它们所支持的类在java.lang.annotation包中可以找到。

@Target

@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。

作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

取值(ElementType)有:

类型 用途
CONSTRUCTOR 用于描述构造器
FIELD 用于描述域
LOCAL_VARIABLE 用于描述局部变量
METHOD 用于描述方法
PACKAGE 用于描述包
PARAMETER 用于描述参数
TYPE 用于描述类、接口(包括注解类型) 或enum声明

比如说这个注解表示只能在方法中使用:


@Target({ElementType.METHOD})
public @interface MyCustomAnnotation { } //使用
public class MyClass {
@MyCustomAnnotation
public void myMethod()
{
......
}
}

@Retention

@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。

作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

取值(RetentionPoicy)有:

类型 用途 说明
SOURCE 在源文件中有效(即源文件保留) 仅出现在源代码中,而被编译器丢弃
CLASS 在class文件中有效(即class保留) 被编译在class文件中
RUNTIME 在运行时有效(即运行时保留) 编译在class文件中

使用示例:


/***
* 字段注解接口
*/
@Target(value = {ElementType.FIELD})//注解可以被添加在属性上
@Retention(value = RetentionPolicy.RUNTIME)//注解保存在JVM运行时刻,能够在运行时刻通过反射API来获取到注解的信息
public @interface Column {
String name();//注解的name属性
}

@Documented

@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。

作用:将注解包含在javadoc中

示例:


java.lang.annotation.Documented
@Documented
public @interface MyCustomAnnotation { //Annotation body}

@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类型被发现,或者到达类继承结构的顶层。

作用:允许子类继承父类中的注解

示例,这里的MyParentClass 使用的注解标注了@Inherited,所以子类可以继承这个注解信息:


java.lang.annotation.Inherited
@Inherited
public @interface MyCustomAnnotation {
} @MyCustomAnnotation
public class MyParentClass {
...
} public class MyChildClass extends MyParentClass {
...
}

自定义注解

格式


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作默认值
  • 在表现一个元素存在或缺失的状态时,定义一下特殊值来表示,如空字符串或负值

示例:


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

注解处理器类库

java.lang.reflect.AnnotatedElement

Java使用Annotation接口来代表程序元素前面的注解,该接口是所有Annotation类型的父接口。除此之外,Java在java.lang.reflect 包下新增了AnnotatedElement接口,该接口代表程序中可以接受注解的程序元素,该接口主要有如下几个实现类:

  •  Class:类定义
  •  Constructor:构造器定义
  •  Field:累的成员变量定义
  •  Method:类的方法定义
  •  Package:类的包定义

java.lang.reflect 包下主要包含一些实现反射功能的工具类,实际上,java.lang.reflect 包所有提供的反射API扩充了读取运行时Annotation信息的能力。当一个Annotation类型被定义为运行时的Annotation后,该注解才能是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。

AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的如下四个个方法来访问Annotation信息:

  • 方法1: T getAnnotation(Class 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号红富士大厦

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{
...
}

思维导图

扩展阅读

http://www.cnblogs.com/ITtangtang/p/3974531.html

www.jianshu.com/p/948549b92e0a

https://www.jianshu.com/p/89c07ce0c99c

Java复习总结——注解的更多相关文章

  1. 【转】JAVA反射与注解

    转载自:https://www.daidingkang.cc/2017/07/18/java-reflection-annotations/ 前言 现在在我们构建自己或公司的项目中,或多或少都会依赖几 ...

  2. java复习面向对象(二)

    java复习面向对象(二) 1.static关键字 举例图片 静态变量 使用static修饰的成员变量是静态变量 如果一个成员变量使用了关键字static,那么这个变量不属于对象自己,而属于所在的类多 ...

  3. java中的注解(Annotation)

    转载:https://segmentfault.com/a/1190000007623013 简介 注解,java中提供了一种原程序中的元素关联任何信息.任何元素的途径的途径和方法. 注解是那些插入到 ...

  4. java @param参数注解

    注解,@param是参数的解释.如/***@param s 这里表示对s的文字说明,描述 */ public void aa(String s){}一般java中@表示注解,解释一个方法,类,属性的作 ...

  5. java 复习003 之排序篇

    由java 复习003跳转过来的C语言实现版见some-sort-algorithms 快速排序(不稳定 O(n log n)) package vell.bibi.sort_algorithms; ...

  6. java 复习001

    java 复习001 比较随意的记录下我的java复习笔记 ArrayList 内存扩展方法 分配一片更大的内存空间,复制原有的数据到新的内存中,让引用指向新的内存地址 ArrayList在内存不够时 ...

  7. JAVA高级特性 - 注解

    注解是插入到代码中用于某种工具处理的标签.这些标签可以在源码层次上进行操作,或者可以处理编译器将其纳入到注解类文件中. 注解不会改变对程序的编译方式.Java编译器会对包含注解和不包含注解的代码生成相 ...

  8. java复习(1)---java与C++区别

    [系列说明]java复习系列适宜有过java学习或C++基础或了解java初步知识的人阅读,目的是为了帮助学习过java但是好久没用已经遗忘了的童鞋快速捡起来.或者教给想快速学习java的童鞋如何应用 ...

  9. java中的注解总结

    1. 什么是注解 注解是java5引入的特性,在代码中插入一种注释化的信息,用于对代码进行说明,可以对包.类.接口.字段.方法参数.局部变量等进行注解.注解也叫元数据(meta data).这些注解信 ...

随机推荐

  1. 移动端和PC端页面常用的弹出层

    我们在页面的时候,很多时候用到了弹出层,消息提醒,确认框等等,统一样式的弹出框可以使页面更加优美.在此,我整理一下我们项目的移动端和PC端页面常用的弹出层. 一.移动端 我们需在页面引入弹出框的样式和 ...

  2. eclipse打开package explorer视图

    第一步:window-show view-other 第二步:

  3. C# Note28: Dispatcher类

    在项目中也是经常用到: 刚见到它时,你会想:为什么不直接使用System.Windows命名空间下的MessageBox类,何必要这么麻烦?(认真分析看它做了什么,具体原因下面解释) 主要介绍的方法: ...

  4. iframe全屏显示

    <iframe webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=" ...

  5. maven中jar下载失败

    关键词:maven jar 错误描述:反编译时遇到 invalid LOC header (bad signature)的问题 aether-89969cb8-5741-44e3-be2c-74f90 ...

  6. 简单JQuery+AJAX+Servlet的计算器实现

    index.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pag ...

  7. MySQL磁盘写入策略以及数据安全性的相关参数

     转载自:http://blog.itpub.net/22664653/viewspace-1063134/   innodb_flush_log_at_trx_commit和sync_binlog ...

  8. LoadRunner Vuser测试脚本添加前置条件举例

    调用接口前需要先获取登陆token,放入消息头中. /* * LoadRunner Java script. (Build: 3020) * * Script Description: 接口性能测试脚 ...

  9. Python——进程队列

    队列 先进先出 from multiprocessing import Queue q = Queue(5) #队列的大小 q.put(1) #放入内容 q.put(2) #放入内容 q.put(3) ...

  10. ES 6 系列 - 赋值的新方式:解构赋值

    变量的解构赋值 es 6 允许按照一定的模式,从数组和对象中提取值,然后对变量进行赋值,这被称之为解构: 一.数组的解构赋值 最基本写法: let [a, b, c] = [1, 2, 3]; a / ...