什么是注解
注解就是给某个注解标注的类或者方法一种特定行为的描述。
注解的语法
通过@interface关键字进行定义
public @interface MyAnnoation {}
它的形式跟接口类似,不过前面多了一个@符号,上面的代码就是创建了一个MyAnnoation注解。
也可以理解为创建了一个名字为MyAnnoation的标签。
注解的用法
如何使用上面的注解?
创建一个类 MyAnnoationTest,进行使用MyAnnoation注解。
@MyAnnoation
public class MyAnnoationTest {}
如上图所示,就是在类定义的地方加上该注解。
其实如果按照上面这样,这个注解就让他工作,那是不行的,还需要使用到元注解。
元注解
什么是元注解?
元注解就是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其他注解上面。
元注解有五种,分别是@Retention、@Documented、@Target、@Inherited、@Repeatable
@Retention
Retention的英文意为保留期的意思,当@Retention应用到一个注解上的时候,它解释说明了这个注解的存活时间
它的取值如下:
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE, // 注解只在源码阶段保留,在编译器进行编译时它将被丢弃
/**
* 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.
*/
CLASS, //注解只被保留到编译进行的时候,它并不会被加载到JVM中
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME // 注解可以保留到程序运行的时候,它会被加载到JVM中,所以程序运行时可以获取到它们
}
我们可以这样的方式来理解,@Retention去给一张标签解释的时候,它指定了这张标签的张贴的时间,@Retention相当于给一张标签上面盖了一张时间戳,时间戳指明了标签张贴的时间周期。
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnoation {}
@Documented
这个元注解是和文档有关的,它的作用是能够将注解中的元素包含到Javadoc中
@Target
指定了注解运用的地方。可以理解为当一个注解被@Target注解时,这个注解就被限定了运用的场景。
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
}
@Inherited
Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被**@Inherited**注解过的注解进行注解的话,那么如果他的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。
@Repeatable
Repeatable 自然是可重复的意思,假如一个人有三种身份,就可以这么定义。
首先定义@Persons注解
public @interface Persons {
Person[] value(); // 这里的Person是一个数组
}
再定义@Person注解,在Person注解上定义@Repeatable(Persons.class),表明该类可以重复
@Repeatable(Persons.class)
public @interface Person{
String role() default "";
}
在SuperMan类上面使用
@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan {
}
注解的属性
注解的属性也叫做成员变量,注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其返回值定义该成员变量的类型。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnoation {
int id();
String msg();
}
上面代码定义了MyAnnoation这个注解中拥有两个属性,在使用的时候,我们应该给他们进行赋值。
赋值的方法是在注解的括号内以value=""形式,多个属性用逗号进行分割。
@MyAnnoation(id = 1,msg = "anan")
public class MyAnnoationTest {}
需要注意的是,在注解中定义属性屎它的类型必须是8中数据类型外加类,接口,注解及它们的数组。
注解中属性可以有默认值,默认值需要用default关键字指定。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnoation {
int id() default 5;
String msg() default "hi annotation";
}
有默认值之后,就可以这样使用注解
@MyAnnoation() // 不用在括号中进行赋值了
public class MyAnnoationTest {
}
另外,如果一个注解内仅仅只有一个名字为value的属性时,应用这个注解就可以直接填写属性值到括号里面
@Check("hi")
public class MyAnnoationTest {
}
还有一种,如果是只有一个注解,里面没有任何属性,使用注解的时候,就可以不用写括号了,直接写注解就行
@Perform
public class MyAnnoationTest {
}
注解与反射
注解通过反射获取。首先通过Class对象的isAnnotationPresent()方法判断它是否应用了某个注解。
然后通过getAnnotation()方法来获取Annotation对象,或者getAnnotations()方法。
前一种方法发挥指定类型的注解,后一种方法返回注解到这个元素上的所有注解。
如果获取到的Annotation不为空,就可以调用属性方法了。
@MyAnnoation(id = 1,msg = "anan")
public class MyAnnoationTest {
public static void main(String args[]) {
boolean hasAnnotation = MyAnnoationTest.class.isAnnotationPresent(MyAnnoation.class);
if (hasAnnotation) {
MyAnnoation myAnnoation = MyAnnoationTest.class.getAnnotation( MyAnnoation.class);
System.out.println(myAnnoation.id());
System.out.println(myAnnoation.msg());
}
}
}
注解的用途
注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。
注解有许多用处,主要如下:
提供信息给编译器: 编译器可以利用注解来探测错误和警告信息
编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
运行时的处理: 某些注解可以在程序运行的时候接受代码的提取
值得注意的是,注解不是代码本身的一部分。
注解主要针对的是编译器和其它工具软件(SoftWare tool)。
当开发者使用了注解修饰了类、方法、Filed等成员后,这些注解不会自己生效,必须由开发者提供相应代码来提取并处理注解信息,这些处理提取和处理注解的代码统称为APT(Annotation Processing Tool)
现在,我们可以给自己答案了,注解有什么用?给谁用?给 编译器或者 APT 用的。
亲手定义注解,完成目的
定义注解
@Retention(RetentionPolicy.RUNTIME)
public @interface Jiacha {
}
使用注解
public class NoBug {
@Jiacha
public void suanShu() {
System.out.println("1234567890");
}
@Jiacha
public void jiaFa() {
System.out.println("1+1="+(1+1));
}
@Jiacha
public void jianFa() {
System.out.println("1-1="+(1-1));
}
@Jiacha
public void chengFa() {
System.out.println("3x5="+3*5);
}
@Jiacha
public void chuFa() {
System.out.println("6/0="+6/0);
}
public void ziwojieshao() {
System.out.println("我写的程序没有bug!");
}
}
解析注解
public class TestTool {
public static void main(String[] args) {
NoBug noBug = new NoBug();
Class clazz = noBug.getClass();
Method[] methods = clazz.getDeclaredMethods();
StringBuilder log = new StringBuilder();
int errornum = 0;
for (Method m:methods) {
if (m.isAnnotationPresent( Jiacha.class)) {
try {
m.setAccessible(true);
m.invoke(noBug,null);
} catch (Exception e) {
errornum++;
log.append( m.getName());
log.append("");
log.append("has error:");
log.append("\n\r caused by ");
log.append(e.getCause().getClass().getSimpleName());
log.append("\n\r");
log.append(e.getCause().getMessage());
log.append("\n\r");
}
}
}
log.append(clazz.getSimpleName());
log.append(" has ");
log.append(errornum);
log.append(" error.");
// 生成测试报告
System.out.println(log.toString());
}
}