Java中的注解

 

(一)什么是注解

    用一个词就能形容注解,那就是元数据。即一种描述数据的数据。注解是一种应用于类,方法,参数,变量,构造器以及包声明中的特殊字符。

      注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。

注解有许多用处,主要如下:

  • 提供信息给编译器: 编译器可以利用注解来探测错误和警告信息。

(如:重写父类方法的@Override,方法过期的@Deprecated,忽略错误的@SuppressWarings

  • 编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理(如:LomBok@Setter@Getter
  • 运行时的处理: 某些注解可以在程序运行的时候接受代码的提取。(如Junit中的@Test

      值得注意的是,注解不是代码本身的一部分。

(二)为什么要引入注解

       使用Annotation之前(甚至在使用之后),XML被广泛的应用于描述元数据。不知何时开始一些应用开发人员和架构师发现XML的维护越来越糟糕了。他们希望使用一些和代码紧耦合的东西,而不是像XML那样和代码是松耦合的(在某些情况下甚至是完全分离的)代码描述。假如你想为应用设置很多的常量或参数,这种情况下,XML是一个很好的选择,因为它不会同特定的代码相连。如果你想把某个方法声明为服务,那么使用Annotation会更好一些,因为这种情况下需要注解和方法紧密耦合起来,开发人员也必须认识到这点。

         另一个很重要的因素是Annotation定义了一种标准的描述元数据的方式。在这之前,开发人员通常使用他们自己的方式定义元数据。例如,使用标记interfaces,注释,transient关键字等等。每个程序员按照自己的方式定义元数据,而不像Annotation这种标准的方式。

(三)注解的属性

       注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无参的方法”来定义,其方法名定义了成员变量的名字,返回值定义了成员变量的类型

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TestAnnotation{
    int id();
    //default 为默认值
    String name() default “”;
}
@TestAnnotation(id=1,name="aa")
class Test{
}

        需要注意的是,在注解中定义属性时它的类型必须是 8 种基本数据类型(不能是包装类)外加 类、接口、注解及它们的数组

(四)元注解

      元注解是指注解的注解,包括@Retention @Target @Document @Inherit四种

(一)@Retention(保留期):定义注解的保留策略

      @Retention(RetentionPolicy.SOURCE):注解仅保留在源代码中,在class文件中不保留。

     @Retention(RetentionPolicy.CLASS):默认的注解保留策略,注解会在保留到编译期间,在Class文件中保留,但在运行时无法获取,即不会加载到JVM中。

     @Retention(RetentionPolicy.RUNTIME):注解会在class文件保留,会被加载到JVM中,在运行时可以通过反射获取

(二)@Target(目标):定义注解的作用目标

     @Target(ElementType.TYPE)   //接口、类、枚举、注解

     @Target(ElementType.FIELD) //字段、枚举的常量

     @Target(ElementType.METHOD) //方法

     @Target(ElementType.PARAMETER) //方法参数

     @Target(ElementType.CONSTRUCTOR)  //构造函数

     @Target(ElementType.LOCAL_VARIABLE)//局部变量

     @Target(ElementType.ANNOTATION_TYPE)//注解

     @Target(ElementType.PACKAGE) //    

(三)@Document(文档):说明该注解将被包含在javadoc(javadoc是Sun公司提供的一个技术,它从程序源代码中抽取类、方法、成员等注释形成一个和源代码配套的API帮助文档。)中。

(四)@Inherited(继承):该元注解并不是说注解本身可以被继承,而是当一个类被@Inherited所标注的话,那么其子类会自动继承其父类的注解

//使用了@Inherited元注解标注的注解
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target(ElementType.TYPE)
@interface Father{
    String value() default "father";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Mother{
    String value() default "mother";
}

//其子类会继承其@Father注解
@Father
class Parent{
    
}

@Mother
class Child extends Parent{
    
}

/*
 * 下面的内容可以先不看,等看完后面,再理解。
 * 只需要知道结果就行了、
 */
public class ExtendsAnnotation {
    public static void main(String[] args) {
	try {
	    Class parent = Class.forName("cn.QEcode.注解.Parent");
	    if(parent.isAnnotationPresent(Father.class)){
		Father father = (Father)parent.getAnnotation(Father.class);
		System.out.println("parent  "+father.value());
	    }
	    Class child = Class.forName("cn.QEcode.注解.Child");
	    //是否继承了父类的Father注解
	    if(child.isAnnotationPresent(Father.class)){
		Father father = (Father)child.getAnnotation(Father.class);
		System.out.println("child  "+father.value());
	    }
	    //是否拥有自己的Mother注解
	    if(child.isAnnotationPresent(Mother.class)){
		Mother mother = (Mother)child.getAnnotation(Mother.class);
		System.out.println("child  "+mother.value());
	    }
	} catch (ClassNotFoundException e) {
	    // TODO 自动生成的 catch 块
	    e.printStackTrace();
	}
    }
}


//输出结果为
parent  father
child  father
child  mother


(五)@Repeatable(可重复):表示注解的值可以同时取多个@Repeatable 是 Java 1.8 才加进来的。

/*
 * 容器注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Animal{
    Walk[] value();
}

@Repeatable(Animal.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Walk{
    String walk();
}
/*
 * 可以有多个@Walk注解
 */
@Walk(walk="跑")
@Walk(walk="爬")
class Cat{
    
}
/*
 * 下面的内容可以先不看,等看完后面,再理解。
 * 只需要知道结果就行了、
 */
public class RepeatableAnnotation {
    public static void main(String[] args) {
	try {
	    Class cat =  Class.forName("cn.QEcode.注解.Cat");
	    Annotation[] ann = cat.getAnnotations();
	    for (Annotation an : ann) {
		if (an instanceof Animal) {
		    System.out.println("Animal:");
		    an = (Animal) an;
		    Walk[] walks = ((Animal) an).value();
		    for (Walk walk : walks) {
			System.out.println(walk.walk());
		    }
		} else if (an instanceof Walk) {
		    System.out.println("Walk:");
		    Walk walk = (Walk) an;
		    System.out.println(walk.walk());
		}
	    }
	
	} catch (ClassNotFoundException e) {
	    // TODO 自动生成的 catch 块
	    e.printStackTrace();
	}
	
    }
}

//输出结果为:
Animal:
跑
爬  

      @Repeatable注解标注了Walk注解,并且表明了Animal是Walk的容器注解。

      容器注解是指存放其他注解注解。它里面必须要有一个 value 的属性属性类型是一个被 @Repeatable 注解过的注解数组

      同时从上面的代码可以看出,在使用反射获取Cat@Walk标注的注解)的注解时,需要获取的不是@Walk注解,而是容器注解@Animal,并且注解的值在@Animal中的value数组。

(五)注解的提取

    在使用注解来标注一个类后,我们需要在运行时获取到注解的内容。而想要获取到注解的内容,就需要用到反射。

    注解通过反射获取。首先可以通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解。

public boolean isAnnotationPresent(Class annotationClass) {}

   但是注意isAnnotation()是判断Class对象是否为一个注解 

   

public boolean isAnnotation(){}

然后通过 getAnnotation() 方法来获取 Annotation 对象。

public  A getAnnotation(Class annotationClass) {}

      或者是 getAnnotations() 方法。

public Annotation[] getAnnotations() {}

      前一种方法返回指定类型的注解,后一种方法返回注解到这个元素上的所有注解。

//标注一个类
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface ClassDesc{
    String name() default "";
    int age() default 18;
    boolean sex() default true;
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface MethodDesc{
    String value() default "";
}

@ClassDesc(name="QEcode",age=13)
class UserAnnotation{
    @MethodDesc("行为")
    public void test(){
	System.out.println("Test");
    }
    
    public void test2(){
	System.out.println("Test2");
    }
    @MethodDesc("动作")
    public void test3(){
	System.out.println("Test3");
    }
    
}

public class MyAnnotation {
    public static void main(String[] args){
	//使用类加载器加载类
	try {
	    Class clazz = Class.forName("cn.QEcode.注解.UserAnnotation");
	    //判断这个类是否应用了ClassDesc注解
	    if(clazz.isAnnotationPresent(ClassDesc.class)){
		//获取注解
		ClassDesc classDesc = (ClassDesc) clazz.getAnnotation(ClassDesc.class);
		System.out.println(classDesc.name()+" "+classDesc.age());
		//提取方法
		Method[] ms = clazz.getMethods(); 
		for(Method method:ms){
		    if(method.isAnnotationPresent(MethodDesc.class)){
			MethodDesc methDesc = (MethodDesc) method.getAnnotation(MethodDesc.class);
			System.out.println(methDesc.value());
			method.invoke(new UserAnnotation(), null);
		    }
		}
	    }
	} catch (ClassNotFoundException e) {
	    // TODO 自动生成的 catch 块
	    e.printStackTrace();
	} catch (IllegalAccessException e) {
	    // TODO 自动生成的 catch 块
	    e.printStackTrace();
	} catch (IllegalArgumentException e) {
	    // TODO 自动生成的 catch 块
	    e.printStackTrace();
	} catch (InvocationTargetException e) {
	    // TODO 自动生成的 catch 块
	    e.printStackTrace();
	}
	
    }
}

 

你可能感兴趣的:(java基础)