元注解及自定义注解
元注解 ? ? ??元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解: [email?protected], 这些类型和它们所支持的类在java.lang.annotation包中可以找到。下面我们看一下每个元注解的作用和相应分参数的使用说明。 @Target: @Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标 ? ? ? ? ? ? 1.CONSTRUCTOR:用于描述构造器 @Retention: ??@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。 作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效) 取值(RetentionPoicy)有: 1.SOURCE:在源文件中有效(即源文件保留) @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Column { public String name() default "fieldName"; public String setFuncName() default "setField"; public String getFuncName() default "getField"; public boolean defaultDBValue() default false; } ?Column注解的的RetentionPolicy的属性值是RUTIME,这样注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理。 @Documented: @Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。 ?@Inherited: ? @Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。 注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。 当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层. 实例代码: ? 五、常见标准的Annotation 从java5版本开始,自带了三种标准annontation类型, ? (1)、Override ? java.lang.Override?是一个marker annotation类型,它被用作标注方法。它说明了被标注的方法重载了父类的方法,起到了断言的作用。如果我们使用了这种annotation在一个没有覆盖父类方法的方法时,java编译器将以一个编译错误来警示。 ? 这个annotaton常常在我们试图覆盖父类方法而确又写错了方法名时加一个保障性的校验过程。 (2)、Deprecated ? Deprecated也是一种marker annotation。当一个类型或者类型成员使用@Deprecated修饰的话,编译器将不鼓励使用这个被标注的程序元素。所以使用这种修饰具有一定的?“延续性”:如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员,虽然继承或者覆盖后的类型或者成员并不是被声明为?@Deprecated,但编译器仍然要报警。 ? 注意:@Deprecated这个annotation类型和javadoc中的?@deprecated这个tag是有区别的:前者是java编译器识别的,而后者是被javadoc工具所识别用来生成文档(包含程序成员为什么已经过时、它应当如何被禁止或者替代的描述)。 (3)、SuppressWarnings ? 此注解能告诉Java编译器关闭对类、方法及成员变量的警告。 有时编译时会提出一些警告,对于这些警告有的隐藏着Bug,有的是无法避免的,对于某些不想看到的警告信息,可以通过这个注解来屏蔽。 ? SuppressWarning不是一个marker annotation。它有一个类型为String[]的成员,这个成员的值为被禁止的警告名。对于javac编译器来讲,被-Xlint选项有效的警告名也同样对@SuppressWarings有效,同时编译器忽略掉无法识别的警告名。 ? annotation语法允许在annotation名后跟括号,括号中是使用逗号分割的name=value对用于为annotation的成员赋值: 代码: ?
? ? ? ?注意:我们可以在下面的情况中缩写annotation:当annotation只有单一成员,并成员命名为"value="。这时可以省去"value="。比如将上面的SuppressWarnings annotation进行缩写: ? 代码: ?
? ? 如果SuppressWarnings所声明的被禁止警告个数为一个时,可以省去大括号: ?
? ?在这个例子中SuppressWarnings annotation类型只定义了一个单一的成员,所以只有一个简单的value={...}作为name=value对。又由于成员值是一个数组,故使用大括号来声明数组值。 ? 六、自定义注解 ? ? ?使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。 ? ? ? ?定义注解格式: 注解参数的可支持数据类型: 1.所有基本数据类型(int,float,boolean,byte,double,char,long,short) Annotation类型里面的参数该怎么设定:? 第二,参数成员只能用基本类型byte,short,int,boolean八种基本数据类型和 String,Enum,Class,annotations等数据类型,以及这一些类型的数组.例如,String value();这里的参数成员就为String; 简单的自定义注解和使用注解实例: package annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 水果名称注解 * @author wangsheng * */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitName { String value() default ""; } package annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 水果颜色注解 * @author peida * */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitColor { /** * 颜色枚举 * @author wangsheng * */ public enum Color{ BULE,RED,GREEN}; /** * 颜色属性 * @return */ Color fruitColor() default Color.GREEN; } package annotation; import annotation.FruitColor.Color; public class Apple { @FruitName("Apple") private String appleName; @FruitColor(fruitColor=Color.RED) private String appleColor; public void setAppleColor(String appleColor) { this.appleColor = appleColor; } public String getAppleColor() { return appleColor; } public void setAppleName(String appleName) { this.appleName = appleName; } public String getAppleName() { return appleName; } public void displayName(){ System.out.println("水果的名字是:苹果"); } } 注解元素的默认值: ? ? ??注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null。因此,使用空字符串或0作为默认值是一种常用的做法。这个约束使得处理器很难表现一个元素的存在或缺失的状态,因为每个注解的声明中,所有元素都存在,并且都具有相应的值,为了绕开这个约束,我们只能定义一些特殊的值,例如空字符串或者负数,一次表示某个元素不存在,在定义注解时,这已经成为一个习惯用法 package annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 水果供应者注解 * @author wangsheng * */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitProvider { /** * 供应商编号 * @return */ public int id() default -1; /** * 供应商名称 * @return */ public String name() default ""; /** * 供应商地址 * @return */ public String address() default ""; } 定义了注解,并在需要的时候给相关类,类属性加上注解信息,如果没有响应的注解信息处理流程,注解可以说是没有实用价值。如何让注解真真的发挥作用,主要就在于注解处理方法,下一步我们将学习注解信息的获取和处理 看完上面的例子后,来看一个更加具体的例子,该例子共涉及4个类: 清单1:Author.java /** * */ package com.wsheng.aggregator.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 定义作者信息,name和group * * @author Josh Wang(Sheng) * * @email [email?protected] * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface Author { String name(); // 因为没有定义public,所以默认的访问权限为包权限,在定义时没有指定默认值,则使用时必须指定默认值 String group(); } ? ?清单2:Description.java /** * */ package com.wsheng.aggregator.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 定义描述信息value * * @author Josh Wang(Sheng) * * @email [email?protected] * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented public @interface Description { String value();// 只有一个属性时,最好定义为value,因为可以省略哦:) } 清单3:Utility.java : 使用自定义Annotation注解的类 /** * */ package com.wsheng.aggregator.annotation; /** * @author Josh Wang(Sheng) * * @email [email?protected] * */ @Description(value="这是一个有用的工具类") // value可以省略 public class Utility { @Author(name="wangsheng",group="developer team") public String work() { return "work over!"; } } 清单4:AnalysisAnnotation.java /** * */ package com.wsheng.aggregator.annotation; import java.lang.reflect.Method; /** * *在运行时分析处理annotation类型的信息 * * @author Josh Wang(Sheng) * * @email [email?protected] * */ public class AnalysisAnnotation { public static void main(String[] args) { try { // 通过运行时反射API获得annotation信息 Class<?> rtClass = Class.forName("com.wsheng.aggregator.annotation.Utility"); Method[] methods = rtClass.getMethods(); boolean descriptionExist = rtClass.isAnnotationPresent(Description.class); if (descriptionExist) { Description description = (Description)rtClass.getAnnotation(Description.class); System.out.println("Utility‘s Description --- > " + description.value()); for (Method method : methods) { if (method.isAnnotationPresent(Author.class)) { Author author = (Author)method.getAnnotation(Author.class); System.out.println("Utility‘s Author ---> " + author.name() + " from " + author.group()); } } } } catch (ClassNotFoundException e) { e.printStackTrace(); } } } 运行结果: Utility‘s Description --- > 这是一个有用的工具类 ? Utility‘s Author ---> wangsheng from developer team (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |