• java注解详解


    1.  了解注解

    我们有必要对JDK 5.0新增的注解(Annotation)技术进行简单的学习,因为Spring 支持@AspectJ,而@AspectJ本身就是基于JDK 5.0的注解技术。所以学习JDK 5.0的注解知识有助于我们更好地理解和掌握Spring的AOP技术。

    对于Java开发人员来说,在编写代码时,除了源程序以外,我们还会使用 Javadoc标签对类、方法或成员变量进行注释,以便使用Javadoc工具生成和源代码配套的Javadoc文档。这些@param、@return 等Javadoc标签就是注解标签,它们为第三方工具提供了描述程序代码的注释信息。使用过Xdoclet的朋友,对此将更有感触,像Struts、 Hibernate都提供了Xdoclet标签,使用它们可以快速地生成对应程序代码的配置文件。

    JDK5.0注解可以看成是Javadoc标签和Xdoclet标签的延伸和发展。在JDK5.0中,我们可以自定义这些标签,并通过Java语言的反射机制中获取类中标注的注解,完成特定的功能。

    注解是代码的附属信息,它遵循一个基本原则:注解不能直接干扰程序代码的运行,无论增加或删除注解,代码都能够正常运行。Java语言解释器会忽略这些注解,而由第三方工具负责对注解进行处理。第三方工具可以利用代码中的注解间接控制程序代码的运行,它们通过Java反射机制读取注解的信息,并根据这些信息更改目标程序的逻辑,而这正是Spring AOP对@AspectJ提供支持所采取的方法。

    注解的语法比较简单,除了@符号的使用以外,它基本上与java的固有语法一致,java内置了三种注解,定义在java.lang包中。

    @Override:只能用在方法之上的,用来告诉别人这一个方法是改写父类的。

    @Deprecated:建议别人不要使用旧的API的时候用的,编译的时候会用产生警告信息,可以设定在程序里的所有的元素上。

    @SuppressWarnings:表示关闭一些不当的编译器警告信息。

    2.  定义注解

    下面是一个定义注解的实例。

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface Description {
     
     String value();
    }

    其中的@interface是一个关键字,在设计annotations的时候必须把一个类型定义为@interface,而不能用class或interface关键字。所有的注解类都隐式继承于 java.lang.annotation.Annotation,注解不允许显式继承于其他的接口。

    一个注解可以拥有多个成员,成员声明和接口方法声明类似,这里,我们仅定义了一个成员,成员的声明有以下几点限制:

    a)   成员以无入参无抛出异常的方式声明,如boolean value(String str)、boolean value() throws Exception等方式是非法的;

    b)   可以通过default为成员指定一个默认值,如String level() default "LOW_LEVEL"、int high() default 2是合法的,当然也可以不指定默认值;

    c)   成员类型是受限的,合法的类型包括原始类型及其封装类、String、Class、enums、注解类型,以及上述类型的数组类型。如ForumService value()、List foo()是非法的。

    d)   如果注解只有一个成员,则成员名必须取名为value(),在使用时可以忽略成员名和赋值号(=),如@Description("使用注解的实例")。注解类拥有多个成员时,如果仅对value成员进行赋值则也可不使用赋值号,如果同时对多个成员进行赋值,则必须使用赋值号,如@DeclareParents (value = "NaiveWaiter", defaultImpl = SmartSeller.class)。

    e)   注解类可以没有成员,没有成员的注解称为标识注解,解释程序以标识注解存在与否进行相应的处理;

    注解定义包含四个元注解,分别为@Target,@Retention,@Documented,@Inherited。各元注解的作用如下:

    1)  @Target

    表示该注解用于什么地方,可能的 ElemenetType 参数包括:

    Ø  ElemenetType.CONSTRUCTOR 构造器声明。

    Ø  ElemenetType.FIELD 域声明(包括 enum 实例)。

    Ø  ElemenetType.LOCAL_VARIABLE 局部变量声明。

    Ø  ElemenetType.METHOD 方法声明。

    Ø  ElemenetType.PACKAGE 包声明。

    Ø  ElemenetType.PARAMETER 参数声明。

    Ø  ElemenetType.TYPE 类,接口(包括注解类型)或enum声明。

    2)  @Retention

    表示在什么级别保存该注解信息。可选的 RetentionPolicy 参数包括:

    Ø  RetentionPolicy.SOURCE 注解将被编译器丢弃。

    Ø  RetentionPolicy.CLASS 注解在class文件中可用,但会被VM丢弃。

    Ø  RetentionPolicy.RUNTIME VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。

    举一个例子,如@Override里面的Retention设为SOURCE,编译成功了就不要这一些检查的信息,相反,@Deprecated里面的Retention设为RUNTIME,表示除了在编译时会警告我们使用了哪个被 Deprecated的方法,在执行的时候也可以查出该方法是否被Deprecated。

    3)  @Documented

    将此注解包含在 javadoc 中

    4)  @Inherited

    允许子类继承父类中的注解

    3.  使用注解

    下面是一个使用注解注解的实例。

    @Description(value="使用注解的实例")
    public class TestAnnotation {

    }

    使用注解的语法:@<注解名>(<成员名1>=<成员值1>,<成员名1>=<成员值1>,...)

    如果成员是数组类型,可以通过{}进行赋值,如boolean数组的成员可以设置为{true,false,true}。下面是几个注解标注的例子:

    1)   多成员的注解

    @AnnoExample(id= 2868724, synopsis = "Enable time-travel", engineer = "Mr. Peabody")

    2)   一个成员的注解,成员名为value

    可以省略成员名和赋值符号:@Copyright("Right Reserved")

    @Copyright("2011 bookegou.com All Right Reserved")

    3)   无成员的注解

    @Override

    @Override

    4)   成员为字符串数组的注解

    @SuppressWarnings(value={"unchecked","fallthrough"})

    @SuppressWarnings(value={"unchecked","fallthrough"})

    5)   成员为注解数组类型的注解

    @Reviews({@Review(grade=Review.Grade.EXCELLENT,reviewer="df"),

    @Review(grade=Review.Grade.UNSATISFACTORY,reviewer="eg", 

    comment="This method needs an @Override annotation")})

    @Reviews({@Review(grade=Review.Grade.EXCELLENT,reviewer="df"),     

               @Review(grade=Review.Grade.UNSATISFACTORY,reviewer="eg",             

                        comment="This method needs an @Override annotation")})

    @Reviews注解拥有一个@Review注解数组类型的成员,@Review注解类型有三个成员,其中reviewer、comment都是String类型,但comment有默认值,grade是枚举类型的成员。

    4.  解析注解

    下面是一个解析注解的实例。

    public class ParseAnnotation {
     
     public static void main(String[] args) throws Exception{
      final Class cls = Class.forName("com.annotation.TestAnnotation");
      final Method[] method = cls.getMethods();
      
      // 判断是否有指定注解类型的注解
      if(cls.isAnnotationPresent(Description.class)) {
       // 根据注解类型返回指定类型的注解
       Description des = (Description)cls.getAnnotation(Description.class);
       System.out.println("注解描述:" + des.value());
      }
     }
    }

    输出结果如下:

     注解描述:使用注解的实例

    前面提到过,注解不会直接影响程序的运行,但是第三方程序或工具可以利用代码中的注解完成特殊的任务,间接控制程序的运行。对于RetentionPolicy.RUNTIME保留期限的注解,我们可以通过反射机制访问类中的注解。

    在JDK5.0里,Package、Class、Constructor、 Method以及Field等反射对象都新增了访问注解信息的方法:<T extends Annotation>T getAnnotation(Class<T> annotationClass),该方法支持通过泛型直接返回注解对象。 

    下面为一个例子:

    1、定义一个注解

    package annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
    * 定义一个注解
    * 
    * 
    * @author jiqinlin
    *
    */
    //Java中提供了四种元注解,专门负责注解其他的注解,分别如下
    
    //@Retention元注解,表示需要在什么级别保存该注释信息(生命周期)。可选的RetentionPoicy参数包括:
    //RetentionPolicy.SOURCE: 停留在java源文件,编译器被丢掉
    //RetentionPolicy.CLASS:停留在class文件中,但会被VM丢弃(默认)
    //RetentionPolicy.RUNTIME:内存中的字节码,VM将在运行时也保留注解,因此可以通过反射机制读取注解的信息
    
    //@Target元注解,默认值为任何元素,表示该注解用于什么地方。可用的ElementType参数包括
    //ElementType.CONSTRUCTOR: 构造器声明
    //ElementType.FIELD: 成员变量、对象、属性(包括enum实例)
    //ElementType.LOCAL_VARIABLE: 局部变量声明
    //ElementType.METHOD: 方法声明
    //ElementType.PACKAGE: 包声明
    //ElementType.PARAMETER: 参数声明
    //ElementType.TYPE: 类、接口(包括注解类型)或enum声明
    
    //@Documented将注解包含在JavaDoc中
    
    //@Inheried允许子类继承父类中的注解
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD, ElementType.TYPE})
    public @interface MyAnnotation {
    	// 为注解添加属性
    	String color();
    
    	String value() default "我是aaa"; // 为属性提供默认值
    
    	int[] array() default { 1, 2, 3 };
    
    	Gender gender() default Gender.MAN; // 添加一个枚举
    
    	MetaAnnotation metaAnnotation() default @MetaAnnotation(birthday = "我的出身日期为1988-2-18");
    }
    

      2、定义依赖项

    package annotation;
    /**
    * 枚举,模拟注解中添加枚举属性
    * 
    * 
    *
    */
    public enum Gender {
    	MAN {
    		public String getName() {
    			return "男";
    		}
    	},
    	WOMEN {
    		public String getName() {
    			return "女";
    		}
    	}; // 记得有“;”
    	public abstract String getName();
    }
    

      

    package annotation;
    
    /**
    * 定义一个注解,模拟注解中添加注解属性
    * 
    * 
    *
    */
    public @interface MetaAnnotation {
    	String birthday();
    }
    

      3、定义测试程序

    package annotation;
    
    //调用注解并赋值
    @MyAnnotation(metaAnnotation=@MetaAnnotation(birthday = "我的出身日期为1988-2-18"),color="red", array={23, 26})
    public class AnnotationTest {
    
    	public static void main(String[] args) {
    		// 检查类AnnotationTest是否含有@MyAnnotation注解
    		if (AnnotationTest.class.isAnnotationPresent(MyAnnotation.class)) {
    			// 若存在就获取注解
    			MyAnnotation annotation = (MyAnnotation) AnnotationTest.class
    					.getAnnotation(MyAnnotation.class);
    			System.out.println(annotation);
    			// 获取注解属性
    			System.out.println(annotation.color());
    			System.out.println(annotation.value());
    			// 数组
    			int[] arrs = annotation.array();
    			for (int arr : arrs) {
    				System.out.println(arr);
    			}
    			// 枚举
    			Gender gender = annotation.gender();
    			System.out.println("性别为:" + gender);
    			// 获取注解属性
    			MetaAnnotation meta = annotation.metaAnnotation();
    			System.out.println(meta.birthday());
    		}
    	}
    
    }
    

      附一张别人总结的思维导图:

  • 相关阅读:
    【LuoguP4770】[NOI2018] 你的名字
    【LuoguP5171】Earthquake
    【LuoguP3747】[六省联考2017] 相逢是问候
    【LuoguP4916】魔力环
    YOLO2:实时目标检测视频教程,视频演示, Android Demo ,开源教学项目,论文。
    谷歌发布 TensorFlow Lite [官方网站,文档]
    Chinese-Text-Classification,用卷积神经网络基于 Tensorflow 实现的中文文本分类。
    Chinese-Text-Classification:Tensorflow CNN 模型实现的中文文本分类器[不分词版]
    Hinton's paper Dynamic Routing Between Capsules 的 Tensorflow , Keras ,Pytorch实现
    谷歌开发者:看可口可乐公司是怎么玩转TensorFlow的?
  • 原文地址:https://www.cnblogs.com/snowbook/p/5359263.html
Copyright © 2020-2023  润新知