深入理解java中的Annotation(注解),@interface的用法及@Rentention、@Target

一.介绍annotation

JDK1.5开始增加了Annotation功能,用于类,构造方法,成员变量,方法,参数等的声明中。该功能不会影响程序的运行,但是会对编译器警告等辅助功能产生影响,它能够工作的方式被看作类似程序的工具或者类库,它会反过来对正在进行的程序语义有所影响。annotation可以从源文件、class文件或者在运行时反射的多种方式被读取。
一个正式的Annotation功能:(1)允许开发者定义、使用自己的annotation类型(2)此功能由一个定义annotation类型的语法和一个描述annotation声明的语法,读取annotation的API,一个使用annotation修饰的class文件,一个annotation处理工具(apt)组成。

二.Java注解(Annotation)
Override 注解表示子类要重写(override)父类的对应方法。

public class OverrideTest {
	@Override
	//表示接下来的方法需要是重写父类的方法,否则报错,例如tostring会报错
	public String toString(){
		return "Thie is OverrideTest";
	}
	public static void main(String[] args){
		OverrideTest test = new OverrideTest();
		System.out.println(test); 
	}
}

Deprecated注解表示方法是不建议被使用的。

//import java.util.Date;
public class DeprecatedTest {
	@Deprecated
	public void doSomething(){
		System.out.println("dosomething");
	}
	public static void main(String[] args){
		DeprecatedTest test = new DeprecatedTest();
		test.doSomething();//横线表示不建议被使用
		
		
		
//		Date date = new Date();
//		System.out.println(date.toLocaleString());//deprecated
	}
}

SuppressWarnings注解表示抑制警告。

import java.util.Date;
import java.util.Map;
import java.util.TreeMap;
public class SuppressWaringsTest {
	@SuppressWarnings("unchecked")//将调用集合的方法的警告压制住
	public static void main(String[] args){
		Map map = new TreeMap(); 
		map.put("hello", new Date());
		System.out.println(map.get("hello"));
	}
}

三.自定义重点内容注解

这里写图片描述

自定义注解:当注解中的属性名为value时,在对其赋值时可以不指定属性的名字而直接写上属性值即可;除了value以外的其他值都需要使用name = value这种赋值方式,即明确指定给谁赋值。

public @interface AnnotationTest {
	String value();
}

public class AnnotationUsage {
	@AnnotationTest ("hello") //直接赋值
	public void method(){
		System.out.println("usage of annotation");
	}
	public static void main(String[] args){
		AnnotationUsage usage = new AnnotationUsage();
		usage.method();
	}
}


public @interface AnnotationTest {
	String name();
}

public class AnnotationUsage {
	@AnnotationTest (name = "hello") //需要指定属性名字
	public void method(){
		System.out.println("usage of annotation");
	}
	public static void main(String[] args){
		AnnotationUsage usage = new AnnotationUsage();
		usage.method();
	}
}


也可以通过属性后加default的方式为成员设置默认值。

三.@Rentention及@Target

注解@Retention可以用来修饰注解,是注解的注解,称为元注解。
Retention注解有一个属性value,是RetentionPolicy类型的,Enum RetentionPolicy是一个枚举类型,
这个枚举决定了Retention注解应该如何去保持,也可理解为Rentention 搭配 RententionPolicy使用。RetentionPolicy有3个值:CLASS RUNTIME SOURCE
用@Retention(RetentionPolicy.CLASS)修饰的注解,表示注解的信息被保留在class文件(字节码文件)中当程序编译时,但不会被虚拟机读取在运行的时候;
用@Retention(RetentionPolicy.SOURCE )修饰的注解,表示注解的信息会被编译器抛弃,不会留在class文件中,注解的信息只会留在源文件中;
用@Retention(RetentionPolicy.RUNTIME )修饰的注解,表示注解的信息被保留在class文件(字节码文件)中当程序编译时,会被虚拟机保留在运行时,
所以他们可以用反射的方式读取。RetentionPolicy.RUNTIME 可以让你从JVM中读取Annotation注解的信息,以便在分析程序的时候使用.

package com.self;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
 
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTarget
{ }
定义个一注解@MyTarget,用RetentionPolicy.RUNTIME修饰;
package com.self;
import java.lang.reflect.Method;
public class MyTargetTest
{
 @MyTarget
 public void doSomething()
 {
  System.out.println("hello world");
 }
 
 public static void main(String[] args) throws Exception
 {
  Method method = MyTargetTest.class.getMethod("doSomething",null);
  if(method.isAnnotationPresent(MyTarget.class))//如果doSomething方法上存在注解@MyTarget,则为true
  {
   System.out.println(method.getAnnotation(MyTarget.class));
  }
  }
}
上面程序打印:@com.self.MyTarget(),如果RetentionPolicy值不为RUNTIME,则不打印。
 
 @Retention(RetentionPolicy.SOURCE )
public @interface Override
 
@Retention(RetentionPolicy.SOURCE )
public @interface SuppressWarnings
 
@Retention(RetentionPolicy.RUNTIME )
public @interface Deprecated
由上可以看出,只有注解@Deprecated在运行时可以被JVM读取到
 
注解中可以定义属性,看例子:
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation
{
 String hello() default "gege";
  String world();
  int[] array() default { 2, 4, 5, 6 };
  EnumTest.TrafficLamp lamp() ;
  TestAnnotation lannotation() default @TestAnnotation(value = "ddd");
  Class style() default String.class;
}
上面程序中,定义一个注解@MyAnnotation,定义了6个属性,他们的名字为:
hello,world,array,lamp,lannotation,style.
属性hello类型为String,默认值为gege
属性world类型为String,没有默认值
属性array类型为数组,默认值为2,4,5,6
属性lamp类型为一个枚举,没有默认值
属性lannotation类型为注解,默认值为@TestAnnotation,注解里的属性是注解
属性style类型为Class,默认值为String类型的Class类型
 
看下面例子:定义了一个MyTest类,用注解@MyAnnotation修饰,注解@MyAnnotation定义的属性都赋了值
@MyAnnotation(hello = "beijing", world="shanghai",array={},lamp=TrafficLamp.RED,style=int.class)
public class MyTest
{
 @MyAnnotation(lannotation=@TestAnnotation(value="baby"), world = "shanghai",array={1,2,3},lamp=TrafficLamp.YELLOW)
 @Deprecated
 @SuppressWarnings("")
 public void output()
 {
  System.out.println("output something!");
 }
}
 接着通过反射读取注解的信息:
public class MyReflection
{
 public static void main(String[] args) throws Exception
 {
  MyTest myTest = new MyTest();
    Class<MyTest> c = MyTest.class;
    Method method = c.getMethod("output", new Class[] {});
       //如果MyTest类名上有注解@MyAnnotation修饰,则为true
  if(MyTest.class.isAnnotationPresent(MyAnnotation.class))
  {
   System.out.println("have annotation");
  }
   if (method.isAnnotationPresent(MyAnnotation.class))
   {
   method.invoke(myTest, null); //调用output方法
   //获取方法上注解@MyAnnotation的信息
     MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
    String hello = myAnnotation.hello();
   String world = myAnnotation.world();
   System.out.println(hello + ", " + world);//打印属性hello和world的值
   System.out.println(myAnnotation.array().length);//打印属性array数组的长度
   System.out.println(myAnnotation.lannotation().value()); //打印属性lannotation的值
   System.out.println(myAnnotation.style());
   }
    //得到output方法上的所有注解,当然是被RetentionPolicy.RUNTIME修饰的
     Annotation[] annotations = method.getAnnotations();
      for (Annotation annotation : annotations)
  {
   System.out.println(annotation.annotationType().getName());
  }
   }
}
上面程序打印:
have annotation
output something!
gege, shanghai
3
baby
class java.lang.String
com.heima.annotation.MyAnnotation
java.lang.Deprecated
如果注解中有一个属性名字叫value,则在应用时可以省略属性名字不写。
可见,@Retention(RetentionPolicy.RUNTIME )注解中,RetentionPolicy.RUNTIME是注解属性值,属性名字是value,
属性的返回类型是RetentionPolicy,如下:
public @interface MyTarget
{
    String value();
}
可以这样用:
  @MyTarget("aaa")
 public void doSomething()
 {
  System.out.println("hello world");
 }
 
注解@Target也是用来修饰注解的元注解,它有一个属性ElementType也是枚举类型,
值为:ANNOTATION_TYPE CONSTRUCTOR  FIELD LOCAL_VARIABLE METHOD PACKAGE PARAMETER TYPE
如@Target(ElementType.METHOD) 修饰的注解表示该注解只能用来修饰在方法上。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTarget
{
 String value() default "hahaha";
}
如把@MyTarget修饰在类上,则程序报错,如:
@MyTarget
public class MyTargetTest
注解大都用在开发框架中吧,好了有关注解就学习那么多了,谢谢。

总结:

Rentention注解类

注解的生命周期:Java源文件—》class文件—》内存中的字节码。编译或者运行时,都有可能会取消注解。Rentention的3种取值意味让注解保留到哪个阶段,RententionPolicy.SOURCE、RententionPolicy.CLASS(默认值)、RententionPolicy.RUNTIME。

@Override、@SuppressWarnings是默认保留到SOURCE阶段;@Deprecated是保留到RUNTIME阶段。
Rentention相当于注解类的一个属性,因为Rentention的值不同,注解类保留到的阶段不同。注解类内部Rentention的值使用value表示,例如,@Deprecated中,value=Runtime。

Rentention的值是枚举RententionPolicy的值,只有3个:SOURCE、CLASS、RUNTIME。

Target注解类

性质和Rentention一样,都是注解类的属性,表示注解类应该在什么位置,对那一块的数据有效。例如,@Target(ElementType.METHOD)Target内部的值使用枚举ElementType表示,表示的主要位置有:注解、构造方法、属性、局部变量、函数、包、参数和类(默认值)。多个位置使用数组,例如,@Target({ElementType.METHOD,ElementType.TYPE})。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值