Java注解

By AverageJoeWang
 标签:

Annotation是从JDK1.5开始提供为程序元素(包、类、构造器、方法、成员变量、参数、局部变量)设置元数据的一个接口,这些信息被存储在Annotation的name=value对中,本篇笔记用于记录什么是Annotation?什么是基本Annotation及其作用?什么是JDK元Anntation及其作用?

20161019193733624

一、基本Annotation

基本的Annotation是由JDK的java.lang所提供

注解 释义 特别说明
@Override 重写 @Override只能修饰方法,不能修饰其他程序元素
@Deprecated 已过时 可以修饰类、方法等
@SuppressWarnings(value = “unchecked”) 抑制编译器警告 一定要写unchecked为该注释成员变量设置值
@SafeVarags 修饰”堆污染”警告 Java7专门抑制”堆污染”警告提供的
@FunctionalInterface Java8的函数式接口 Java8专门提供,只能修饰接口,不能修饰其他元素
  • 编程中尽量多使用@Override注释,这样可以在编译的时候编译器就检查出问题
public class Person {
    public void name(){
        System.out.println("I am oliver");
    }
}

class Me extends Person{
    @Override
    public void name(){
        System.out.println("I am AverageJoeWang");
    }
}

二、JDK元Annotation

元Annotation用于修饰其他的Annotation定义

注解 释义
@Retention 注解保留策略
@Target 指定Annotation可以放置的位置(被修饰的目标)
@Documented 指定被修饰的该Annotation可以被javadoc工具提取成文档
@Inherited 指定被它修饰的Annotation将具有继承性
@Repeatable Java8新加特性,可重复的注解

@Retention注解

@Retention(RetentionPolicy.RUNTIME)
public @interface anno {}
  • RetentionPolicy的value值只有三个,分别是SOURCE,CLASS,RUNTIME
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,

    /**
     * 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
}

@Target

@Target指定Annotation可以放置的位置(被修饰的目标)

@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}
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
}

@Documented

@Documented指定被修饰的该Annotation可以被javadoc工具提取成文档.

@Inherited

@Inherited指定被它修饰的Annotation将具有继承性,如果某个类使用了如果某个类使用@Xxx注解(该Annotation使用了@Inherited修饰)修饰, 则其子类自动被@Xxx注解修饰.

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

三、自定义注解

  • 使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
public interface Annotation {
    /**
     * Returns true if the specified object represents an annotation
     * that is logically equivalent to this one.  In other words,
     * returns true if the specified object is an instance of the same
     * annotation type as this instance, all of whose members are equal
     * to the corresponding member of this annotation, as defined below:
     * <ul>
     *    <li>Two corresponding primitive typed members whose values are
     *    <tt>x</tt> and <tt>y</tt> are considered equal if <tt>x == y</tt>,
     *    unless their type is <tt>float</tt> or <tt>double</tt>.
     *
     *    <li>Two corresponding <tt>float</tt> members whose values
     *    are <tt>x</tt> and <tt>y</tt> are considered equal if
     *    <tt>Float.valueOf(x).equals(Float.valueOf(y))</tt>.
     *    (Unlike the <tt>==</tt> operator, NaN is considered equal
     *    to itself, and <tt>0.0f</tt> unequal to <tt>-0.0f</tt>.)
     *
     *    <li>Two corresponding <tt>double</tt> members whose values
     *    are <tt>x</tt> and <tt>y</tt> are considered equal if
     *    <tt>Double.valueOf(x).equals(Double.valueOf(y))</tt>.
     *    (Unlike the <tt>==</tt> operator, NaN is considered equal
     *    to itself, and <tt>0.0</tt> unequal to <tt>-0.0</tt>.)
     *
     *    <li>Two corresponding <tt>String</tt>, <tt>Class</tt>, enum, or
     *    annotation typed members whose values are <tt>x</tt> and <tt>y</tt>
     *    are considered equal if <tt>x.equals(y)</tt>.  (Note that this
     *    definition is recursive for annotation typed members.)
     *
     *    <li>Two corresponding array typed members <tt>x</tt> and <tt>y</tt>
     *    are considered equal if <tt>Arrays.equals(x, y)</tt>, for the
     *    appropriate overloading of {@link java.util.Arrays#equals}.
     * </ul>
     *
     * @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:
     *
     * <p>The hash code of an annotation is the sum of the hash codes
     * of its members (including those with default values), as defined
     * below:
     *
     * The hash code of an annotation member is (127 times the hash code
     * of the member-name as computed by {@link String#hashCode()}) XOR
     * the hash code of the member-value, as defined below:
     *
     * <p>The hash code of a member-value depends on its type:
     * <ul>
     * <li>The hash code of a primitive value <tt><i>v</i></tt> is equal to
     *     <tt><i>WrapperType</i>.valueOf(<i>v</i>).hashCode()</tt>, where
     *     <tt><i>WrapperType</i></tt> is the wrapper type corresponding
     *     to the primitive type of <tt><i>v</i></tt> ({@link Byte},
     *     {@link Character}, {@link Double}, {@link Float}, {@link Integer},
     *     {@link Long}, {@link Short}, or {@link Boolean}).
     *
     * <li>The hash code of a string, enum, class, or annotation member-value
     I     <tt><i>v</i></tt> is computed as by calling
     *     <tt><i>v</i>.hashCode()</tt>.  (In the case of annotation
     *     member values, this is a recursive definition.)
     *
     * <li>The hash code of an array member-value is computed by calling
     *     the appropriate overloading of
     *     {@link java.util.Arrays#hashCode(long[]) Arrays.hashCode}
     *     on the value.  (There is one overloading for each primitive
     *     type, and one for object reference types.)
     * </ul>
     *
     * @return the hash code of this annotation
     */
    int hashCode();

    /**
     * Returns a string representation of this annotation.  The details
     * of the representation are implementation-dependent, but the following
     * may be regarded as typical:
     * <pre>
     *   &#064;com.acme.util.Name(first=Alfred, middle=E., last=Neuman)
     * </pre>
     *
     * @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分为两类:
    • 标记Annotation: 没有成员变量的Annotation; 这种Annotation仅利用自身的存在与否来提供信息;
    • 元数据Annotation: 包含成员变量的Annotation; 它们可以接受(和提供)更多的元数据;
  • 定义新注解使用@interface关键字, 其定义过程与定义接口非常类似(见上面的@Testable), 需要注意的是:Annotation的成员变量在Annotation定义中是以无参的方法形式来声明的, 其方法名返回值类型定义了该成员变量的名字类型, 而且我们还可以使用default关键字为这个成员变量设定默认值.
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface MyAnnotation {
    String name();
    int age() default 18;
}
public class TestAnnotation {
    @MyAnnotation(name = "abc")
    public void execute(){
        System.out.println("method");
    }
}

四、提取Annotation信息

  • 使用注解修饰了类/方法/成员变量等之后,这些注解不会自己生效,必须由这些注解的开发者提供相应的工具来提取并处理注解信息(当然,只有当定义注解时使用了@Retention(RetentionPolicy.RUNTIME)修饰,JVM才会在装载class文件时提取保存在class文件中的注解,该注解才会在运行时可见,这样我们才能够解析).
  • Java使用Annotation接口来代表程序元素前面的注解,该接口是所有注解的父接口。

获取Class的实例三种方法

  • 利用对象调用getClass()方法获得Class实例
  • 利用Class类的静态的forName()方法,使用类名获得Class实例
  • 运用.class的方式获得Class实例,如:类名.class

实例

  • interface MyAnnotation
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Created by oliverwang on 2018/2/27.
 */

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface MyAnnotation {
    String name();
    int age() default 18;
}
  • TestAnnotation
import java.lang.annotation.Annotation;

/**
 * Created by oliverwang on 2018/2/27.
 */


public class TestAnnotation {
    @MyAnnotation(name = "abc")
    public void execute(){
        System.out.println("method");
    }

    @MyAnnotation(name = "oliver")
    public void info(){
        try {
            Annotation [] annotation = Class.forName("TestAnnotation").getMethod("info").getAnnotations();
            for (Annotation annotation1 : annotation){
                System.out.println(annotation1);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args){
        TestAnnotation testAnnotation = new TestAnnotation();
        testAnnotation.info();
    }
}
  • 输出结果:
@MyAnnotation(age=18, name=oliver)

五、模拟JUnit

  • 注解Testable接口
/**
 * Created by oliverwang on 2018/2/27.
 */

@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Testable {

}
  • TestCase
/**
 * Created by oliverwang on 2018/2/27.
 */


public class TestCase {
    @Testable
    public void test1(){
        System.out.println("test1");
    }

    public void test2(){
        System.out.println("test2");
    }

    @Testable
    public void test3(){
        System.out.println("test3");
    }

    public void test4(){
        System.out.println("test4");
    }

    @Testable
    public void test5(){
        System.out.println("test5");
    }
}
  • TestableProcessor
/**
 * Created by oliverwang on 2018/2/27.
 */


public class TestableProcessor {
    public static void process(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        int passed = 0;
        int failed = 0;
        Object obj = Class.forName(className).newInstance();
        for (Method method : Class.forName(className).getMethods()) {
            if (method.isAnnotationPresent(Testable.class)) {
                try {
                    method.invoke(obj);
                    ++passed;
                } catch (IllegalAccessException | InvocationTargetException e) {
                    System.out.println("method " + method.getName() + " execute error: < " + e.getCause() + " >");
                    e.printStackTrace(System.out);
                    ++failed;
                }
            }
        }

        System.out.println("共运行" + (failed + passed) + "个方法, 成功" + passed + "个, 失败" + failed + "个");
    }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        TestableProcessor.process("TestCase");
    }
}
  • 结果
test1
test3
test5
共运行3个方法, 成功3个, 失败0个