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