`

“Java注解Annotation”总结

    博客分类:
  • java
阅读更多
从Java 5.0版发布以来,5.0平台提供了一个正式的annotation功能:允许开发者定义、使用自己的annotation类型。此功能由一个定义annotation类型的语法和一个描述annotation声明的语法,读取annotation的API,一个使用annotation修饰的class文件,一个annotation处理工具(apt)组成。


在实际开发中,其实“注解”和“xm配置文件”是一个问题的两种解决手段,本质上是一回事,你掌握了配置文件来完成项目也行,掌握了注解来完成项目也行,具体使用哪种方式是根据项目统一要求的,公司的不同要求使用的方式也不同。


Annotation并不直接影响代码语义,但是它能够工作的方式被看作类似程序的工具或者类库,它会反过来对正在运行的程序语义有所影响。Annotation可以从源文件、class文件或者以在运行时反射的多种方式被读取。


注解可以用在类、方法、成员变量、方法的参数、方法的局部变量上面,任何地方都可以使用注解。


自定义注解的方式:
Myecplise里面新建一个Annotation,你会发现它的类型是@interface,如果自定义的注解类里面什么都没有的话,比如在某个类上面使用这个注解的时候也就相应的什么都不用做,直接在类的上面写上@开头的注解名称即可;而如果在注解类里面为其增加一个属性比如String value();,然后在某个类上面使用此注解的时候也必须相应的为此属性赋值。赋值方式下面会讲到。



1. Java注解(Annotation):
a) Override注解表示子类要重写(override)父类的对应方法。
b) Deprecated注解表示方法是不建议被使用的。
c) SuppressWarnings注解表示抑制警告。
2. 自定义注解:当注解中的属性名为value 时,在对其赋值时可以不指定属性的名称而直接写上属性值即可;除了value以外的其他值都需要使用name=value这种赋值方式,即明确指定给谁赋值。
3. 当我们使用@interface关键字定义一个注解时,该注解隐含地继承了
java.lang.annotation.Annotation 接口;如果我们定义了一个接口,并且让该接口继承自Annotation,那么我们所定义的接口依然还是接口而不是注解(我们所定义的接口依然是接口类型而不是注解类型。);Annotation本身是接口而不是注解(Annotation本身是接口类型而不是注解类型。)。可以与Enum类比。



对每一个注解来说,我们都可以为其设定一个默认值,例如:
Public @interface AnnotationTest {
String value() default “hello”;
}
里面的@interface是一个整体,是一个类型,和class是一个级别。
Value成员设定默认值,用“default”关键词


使用@interface自行定义Annotation型态时,实际上是自动继承了java.lang.annotation.Annotation接口,由编译程序自动为您完成其它产生的细节。

在定义Annotation型态时,不能继承其它的Annotation型态或是接口。

Java.lang.Annotation接口说明:它是一个公用的接口,被所有的annotation类型所继承,我们手动的去继承这个接口并不意味着我们会定义一个annotation类型(一个注解类),要想定义注解只能通过“@interface”这个关键字来定义。这个接口本身并没有定义annotation类型,换句话说这个接口本身还是一个接口,它并不是说注解继承了它,那它(Annotation)也成了注解了,它本身不是注解,而是接口,当定义一个注解的时候自动的就继承了它,但是当我们手工的去定义一个接口并且让这个接口去继承Annotation,那我们自己定义那个接口它还是接口,不会变成注解。


定义Annotation型态时也可以使用包来管理类别。
方式类同于类的导入功能。



下面进入注解的核心内容讲解:
Retention注解类型,里面有一个value属性。
Java.lang.annotatino.Retention型态可以在您定义Annotation(枚举)型态时,指示编译程序该如何对待您的自定义Annotation型态度。
缺省(默认)情况编译程序会将Annotation信息留在.class档案中,但不被虚拟机读取,而仅用于编译程序或工具程序运行时提供信息。



在使用Retention型态时,需要提供java.lang.annotation.RetentionPolicy的枚举型态
Package java.lang.annotation;
Public enum RetentionPolicy {
SOURCE,//编译程序处理完Annotation信息后就完成任务,这种情况下注解会被编译器所忽略掉,编译器就不会把它编译到.class文件当中,这时注解只会存在于源文件当中。
CLASS,//编译程序将Annotation(注解)储存于class文件中,不能在运行期被虚拟机所获取到,缺省(默认)
RUNTIME//编译程序将Annotation储存于class当中,并且在运行期可由JVM(java虚拟机)获取到,并可供java反射读入
}

注:RUNTIME和CLASS唯一的区别就在于是不是能由JVM(java虚拟机)在运行期通过反射的方式获取到注解。

Retention注解类里面有一个属性value,value类型就是RetentionPolicy的,是一个枚举类型的,这个枚举决定了Retention这个注解应该如何的去保持,能保持多长时间,访问权限是什么样的等等。默认情况下这个Retention注解信息会被保存到class文件中(value ==RetentionPolicy.CLASS )。



注解是可以修饰注解的,例如:
@Retention(value=SOURCE)
Public @interface SuppressWarnings
这个代码说明SuppressWarnings这个注解它应该就存在于源文件当中,不会被编译到class文件当中,更不会被JVM所读取到。


一个方法可以被多个注解所修饰。一个注解同样的也可以被多个注解所修饰。


RetentionPolicy为SOURCE的例子是@SuppressWarnings
仅在编译时期告知编译程序来抑制警告,所以不必将这个信息储存于.class档案。
RetentionPolicy为RUNTIME的时机,可以像是您使用Java设计一个程序代码分析工具,您必须让JVM能读出Annotation(注释)信息,以便在分析程序时使用。
搭配反射(Reflection)机制,就可以达到这个目的。


下面是利用注解提供的一些反射API来在运行期间(@Retention(value= RUNTIME)
)根据获得的注解信息来做一些其他的操作:

告知编译程序如何处理@ Retention:
Java.lang.reflect.AnnotatedElement接口;
Public Annotation getAnnotation(Class annotationType);
Public Annotation[] getAnnotations();
Public Boolean isAnnotationPresent(Class annotationType);
Class、Constructor、Field、Method、Package等类别,都实现了AnnotatedElement接口。


要想在反射中能读取Annotation(注解),定义Annotation时必须设定RetentionPolicy为RUNTIME,而就是可以在JVM中读取Annotation信息。


下面是一个反射操作注解的例子(重点掌握此例,注解的最核心内容):
package com.shengshiyuan.annotation;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
	String hello() default "shengshiyuan";

	String world();
}


package com.shengshiyuan.annotation;

@MyAnnotation(hello = "beijing", world = "shanghai")
public class MyTest {
	@MyAnnotation(hello = "tianjing", world = "shangdi")
	@Deprecated
	@SuppressWarnings("unchecked")
	public void output() {
		System.out.println("output something!");
	}
}


package com.shengshiyuan.annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * 通过反射方式读取MyTest类上面所修饰的注解信息,把这些信息都获取过来
 * 类: MyReflection <br>
 * 描述: TODO <br>
 * 作者: 
 * 时间: Oct 29, 2013 10:48:32 AM
 */
public class MyReflection {
	public static void main(String[] args) throws Exception {
		MyTest myTest = new MyTest();
		Class<MyTest> c = MyTest.class;

		// 获得output方法对应的Method对象
		Method method = c.getMethod("output", new Class[] {});

		// 判断此方法上面是不是被MyAnnotation注解所修饰
		if (method.isAnnotationPresent(MyAnnotation.class)) {
			// 调用output方法
			method.invoke(myTest, new Object[] {});

			// 获得output方法(method就表示output方法)上面修饰它的MyAnnotation注解
			MyAnnotation myAnnotation = method
					.getAnnotation(MyAnnotation.class);

			// 获得myAnnotation注解里面的hellow和world属性值
			String hello = myAnnotation.hello();
			String world = myAnnotation.world();

			System.out.println(hello + "," + world);
		}

		// 获得所有修饰output方法的注解组成的数组
		Annotation[] annotations = method.getAnnotations();

		// 遍历该数组(这里能访问并打印出来的肯定都是@Retention(value=SOURCE)类型的注解,因为其他类型的注解通过反射是访问不到的,因此这里只会打印出MyAnnotation和Deprecated两个注解,而SuppressWarnings却不会被打印出来)
		for (Annotation annotation : annotations) {
			// annotation.annotationType()是获得注解类型
			System.out.println(annotation.annotationType().getName());
		}
	}
}




限定annotation使用对象@Target
使用java.lang.annotation.Target可以定义其使用之时机。来规定由它来修饰的那个自定义的注解的使用时机,就是什么时候能用那个自定义的注解。
在定义时要指定java.lang.annotation.ElementType的枚举之一。

Target和Retention有异曲同工之处,Retention要搭配一个枚举RetentionPolicy,而Target也要搭配一个枚举ElementType。

@Target注解主要用于:我们自己定义了一个注解,这个注解修饰方法也可以,修饰类也可以,修饰成员变量也可以,修饰方法的参数也可以,修饰局部变量也可以,修饰什么都行,没有什么限制,但是实际开发里面某些注解只能让它修饰类,某些注解只能让它修饰方法,这样的话我们就应该给它限定一个目标,这个注解到底是干嘛用的,它的使用范围是什么,那么,这个@Target注解就是起到这个作用的。

Package java.lang.annotation;
Public enum ElementType {
TYPE,//适用class,interface,enum
FIELD,//适用field
METHOD,// 适用method
PARAMETER,// 适用method之上parameter
CONSTRUCTOR,// 适用constructor
LOCAL_VARIABLE,//适用局部变量
ANNOTATION_TYPE,//适用annotation型态
PACKAGE//适用package
}

下面这个例子里面的自定义注解MyTarget就只能用在方法上,比如用在类上面就会报错:
package com.shengshiyuan.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
public @interface MyTarget {
	String value();
}


package com.shengshiyuan.annotation;

public class MyTargetTest {

	@MyTarget("hello")
	public void doSomething() {
		System.out.println("hello world");
	}
}


子类是否继承父类@Inherited
默认情况下父类别中的Annotation并不会被继承至子类别中:
如果想要让其被继承,可以在定义Annotation型态时加上java.lang.annotation.Inherited型态的Annotation。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics