Java注解

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

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

注解释义特别说明
@Override重写@Override只能修饰方法,不能修饰其他程序元素
@Deprecated已过时可以修饰类、方法等
@SuppressWarnings(value = “unchecked”)抑制编译器警告一定要写unchecked为该注释成员变量设置值
@SafeVarags修饰”堆污染”警告Java7专门抑制”堆污染”警告提供的
@FunctionalInterfaceJava8的函数式接口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将具有继承性
@RepeatableJava8新加特性,可重复的注解

@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");    }}
  • 结果
test1test3test5共运行3个方法, 成功3个, 失败0个

原文:大专栏  Java注解


计算机