Annotation注解

Annotation注解Annotation 称为注释或注解 它是一个接口 注解提供了一种为程序元素 类 方法 成员变量等 设置元数据 描述其它数据的数据 的方法 编译器 开发工具或其它程序中可以通过反射来获取程序中的 Annotation 对象 通过该对象获得注解里的元数据 注解不影响程序代码 通过使用注解可以在不改变程序逻辑的情况下 在源文件中嵌入一些补充信息 1 基本注释 Override 表明该方

   Annotation称为注释或注解,它是一个接口。注解提供了一种为程序元素(类、方法、成员变量等)设置元数据(描述其它数据的数据)的方法。编译器、开发工具或其它程序中可以通过反射来获取程序中的Annotation对象,通过该对象获得注解里的元数据。注解不影响程序代码,通过使用注解可以在不改变程序逻辑的情况下,在源文件中嵌入一些补充信息。所有的注解都是java.lang.annotation.Annotation的子接口。

  如下@Override为标准注解,用来修饰方法,编译器可以通过反射来获取方法使用的注解,然后做出对应的处理。@SuppressWarnings为包含一个成员的标准注解,在修饰类或方法的时候应该为该成员赋指定的值以指示编译器不同的处理。我们也可以自定义一个注解来修饰类、方法、成员等,然后其它类中可以通过反射来获得该类或方法或成员的注解,然后可以进一步的获得该注解的成员值。

1、基本注释

  @Override:表明该方法是重写父类的方法,或者是实现接口的方法,eg:

class Test { @Override public void func() { ...... } }

  @Deprecated:表示该类或方法或模块已过时被弃用,程序使用这些类或方法时会发出编译警告。

  @SuppressWarnings:指示类或方法取消显示的编译警告,@SuppressWarnings中只有一个类型为String[]的元素成员value,其中常见的参数值为:

  使用示例:

 @SuppressWarnings(value="unchecked") public void func1() { List list = new ArrayList<Integer>(); list.add(10); //list没有指定泛型,此处本来会产生警告 List<String> ls = list; //将不带泛型的对象赋给带泛型的对象,导致“堆污染”,此处本来会产生警告 System.out.println(ls.get(0));//该语句运行时产生异常 } @SuppressWarnings({"unchecked", "deprecation"}) public void func2() { }

   @SafeVarargs:指示取消显示个数可变形参产生的警告,eg:

  我们不能定义一个泛型数组,比如new List[10]的话编译不能通过,但可以声明一个泛型数组,如List[] listAry,泛型数组一般用在个数可变的形参中,因为个数可变的形参相当于是数组:

 //个数可变的形参相当于是数组,所以形参相当于是一个String[] public static void func2(String...aryString) { } //个数可变的形参相当于是数组,因为没有泛型数组,所以形参相当于是一个List[], //泛型被抹去,产生“堆污染”,不使用@SafeVarargs的话会产生编译警告 @SafeVarargs public static void func3(List<String>...aryStringList) { }

  如果我们能保证带个数可变形参的函数的方法内不会产生类型错误的话就可以使用@SafeVarargs,如下所示代码就会产生问题,如果我们确定代码不会有问题的话才能使用@SafeVarargs:

import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args)throws Exception{ func(new ArrayList<String>(), new ArrayList<String>()); } public static void func(List<String>... varargs) { Object[] array = varargs; List<Integer> tmpList = Arrays.asList(42); array[0] = tmpList; //语义不对,不过编译不会有警告 String s = varargs[0].get(0); //执行时期发生ClassCastException } }

 @FunctionalInterface:指示该接口是一个函数式接口(只有一个抽象方法,可以包含多个默认方法或静态方法的接口)。如Runnable接口:

Annotation注解

2、自定义注释

 使用@interface来自定义一个注解,如下定义了名为Coder的注解,注解中包含两个元素personId(int类型)和company(String类型,默认值为”peking”):

public @interface Coder { int personId(); String city()default "peking"; }

  使用上面定义的注解:

public class Main { @Coder public void func(){ } ...... }

  如果注释中仅包含一个元素,这个元素的名字应该为value,如:

public @interface Coder { String value(); }

  如果使用注解时只需要为value成员变量指定值,那么可以省略value=,例如:

@Coder(“1001”) //@Coder(value="1001") public void func() { ...... }

  注释中可以包含枚举类型:

public @interface FruitColor { public enum Color{ BULE,RED,GREEN}; //枚举 Color CoderColor() default Color.GREEN; }

  注解中的元素也可以为数组:

@interface Test{ String[] args(); } public class Main { @Test(args = {"arg1", "arg2"}) public void func(){ } ...... }

 也可以为数组属性设置默认值:

@interface Test{ String[] args() default {"arg1", "arg2"}; }

 注解中的元素也可以为Class:

public @interface Test{ Class expected() default myClass.class; class myClass{} }

声明(使用)自定义的注释的时候需要为其成员变量指定值,如果该变量无默认值的话:

@Coder(personId = 1001) public void func() { ...... }

  注解可以分为标记注解(如@Override)和元注解(如@Retention),标记注解用来修饰类、方法、成员变量等,元注解用来修饰自定义的注解。

3、JDK的元注释

  下面的5个元Annotation,他们用来修饰自定义的注解。

  @Retention:指定被修饰的Annotation保留的时间,通过指定其value成员变量的值:

//编译器把注释保存在class文件中,当运行java程序时,JVM不可获取注释信息,这是默认值。 @Retention(value = RetentionPolicy.CLASS) @interface Testable{} //编译器把注释保存在class文件中,当运行java程序时,JVM可以获取注释信息 //程序可以通过反射获取该注释 @Retention(value = RetentionPolicy.RUNTIME) @interface Testable{} //注释保存在源码中,编译器直接丢弃该注释。 @Retention(value = RetentionPolicy.SOURCE) @interface Testable{}

  @Target:指定被修饰的Annotation能够修饰哪些程序单元,其成员value值常见的值有:

    java.lang.annotation.ElementType.TYPE_PARAMETER为只能修饰类型参数。

import java.lang.annotation.ElementType; import java.lang.annotation.Target; public @Target(ElementType.METHOD) //指示注解只能修饰方法 @interface Test2{ } public @Target({ElementType.METHOD, ElementType.TYPE}) //指示注解能修饰方法、类、接口、枚举 @interface Test2{ } @Target(ElementType.TYPE_PARAMETER) //下面的注解只能修饰类型参数,比如泛型 public @interface Test3{} public class Main<@Test3 T> { public static void main(String[] args){ } }

    java.lang.annotation.MODULE为指定该Annotation修饰模块, JDK9新增,比如@Deprecated注解和@SuppressWarnings就使用了该选项,在模块描述文件的module上可以使用这两个注解:

@Deprecated module cc.openhome.simple{ ...... }

 @Documented:指定被修饰的Annotation将被javadoc工具提取成文档。在制作JavaDoc文件时,默认不会将注解加入到文件中,如果想要将注解加入到文件中,可以为该注解添加@Documented修饰。

  @Inherited:指定被修饰的Annotation具有继承性,使用该Annotation的类的子类自动继承该Annotation。默认父类设定的注解不会被继承至子类,在定义注解的时候使用@Inherited修饰该注解,就可以让注解被子类继承。

  @Repeatable:在java 8之前,程序的元素(类、方法等)不能添加相同类型的注解,如果想要使用多个相同类型的注解的话必须使用Annotation容器,比如下面为使用两个@Result注解的实现方法:

 @AnnotationContainer({@Result(name = "liyang"), @Result(name = "wulei")}) public void func() { }

    java 8中允许使用多个相同类型的注解,通过为注解添加@Repeatable修饰,而且也需要先定义一个注解容器:

import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; //定义注解容器 @Retention(RetentionPolicy.RUNTIME) public @interface FkTagAry{ FkTag[] value(); }

import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) @Repeatable(FkTagAry.class) public @interface FkTag{ String name() default "jon"; int age(); }
public class Main { @FkTag(name = "leo", age = 9) @FkTag(age = 10) public static void main(String[] args)throws Exception { } }

4、使用自定义的注解

  自定义的注解默认是存储于.class文档,而且默认执行时期无法读取,如果想要在程序运行期间读取注解信息的话,可以在定义注解的时候使用@Retention来指定。所以可以为类或方法等实现标准注解或自定义的注解,然后其他程序通过反射获得该类或方法后获得其上的注解。通过反射获得的类(Class类型)或方法(Method类型)都继承自AnnotatedElement接口(Class、Method、Field、Constructor、Package等类都操作了AnnotatedElement)。 获得的注解类型为Annotation(Annotation是所有注解的父接口),可以将其转换为自定义的注解类型后获得注解的数据成员(注解的属性或称元数据)。

  AnnotatedElement包含以下方法:

        isAnnotationPresent()用来判断是否包含指定注解。

        getAnnotations()可以获得所有注解,包括父类的注解(父类的注解必须使用了@Inherited)                

        getDeclaredAnnotations() 获得的所有注解但不包含父类注解。

        getAnnotation()获得指定的注解,包括父类的注解(父类的注解必须使用了@Inherited)。

        getDeclaredAnnotation() 获取指定的注解,但不包含父类注解。

        getAnnotationsByType()获得指定的注解,包括父类的注解(父类的注解必须使用了@Inherited)。与getAnnotation()不同的是可以获得可重复注解。

        getDeclaredAnnotationsByType()获得指定的注解,但不包含父类注解。与getDeclaredAnnotation()不同的是可以获得可重复注解。

       

//Foo.java package xu; import java.lang.annotation.*; @Retention(value = RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @interface Testable { int id(); String city(); } public class Foo { @Testable(id = 1001, city = "peking") public static void func() { System.out.println("func called"); } }
//Test.java package xu; import java.lang.annotation.*; import java.lang.reflect.Method; public class Test { public static void main (String[] args)throws Exception { //Class<?> cls = Class.forName("xu.Foo"); //获取类的class对象 //getMethod()获得类中指定方法,getMethods()获得类中所有方法。 for(Method m : Class.forName("xu.Foo").getMethods()) { if(m.isAnnotationPresent(Testable.class)) //如果该方法使用了Testable注解 { m.invoke(null); //调用该方法(调用的是Foo类的func方法,该方法是静态方法,否则会产生异常) } } //getAnnotation获得指定注解,getAnnotations获得所有注解 Annotation[] ary1 = Class.forName("xu.Foo").getMethod("func").getAnnotations(); //获得Foo类中func方法的所有注解 for(Annotation tag : ary1)//func方法只有一个注解,所以以下输出为 @Testable(id=1001, city="peking") { System.out.println(tag); } //Foo tt = (Foo)cls.getDeclaredConstructor().newInstance(); //获取Foo类的实例 Foo tt = new Foo(); Annotation[] ary2 = tt.getClass().getMethod("func").getAnnotations(); for(Annotation tag : ary2) { if(tag instanceof Testable) //如果是自定义的注解 { //获得注解的元数据 int id = ((Testable)tag).id(); String city = ((Testable)tag).city(); System.out.println(city + id); //输出 peking,1001 } } } } /* */ 

  

5、类型注解

  java 8中对@Target的成员value的值增加了ElementType.TYPE_USE,它被称作类型注解,指定被修饰的注解除了修饰类、接口、方法、成员变量外,还可以在任何用到类型的地方使用,如使用new创建对象、类型转换、使用implements实现接口、使用throws声明抛出异常的时候:

@Target(ElementType.TYPE_USE) @interface notNull{} @notNull class Type implements @notNull Serializable { public void func1()//throws @notNull FileNotFoundException { Object obj = "fkjava.org"; String str = (@NotNull String)obj; Object win = new @NotNull JFrame("test"); } public void func2(List<@NotNull String> info){} }

6、APT

  APT(Annotation Processing Tool)是一种注解处理工具,它能够提取出源文件中包含的注解信息,然后针对注解信息进行额外的处理。Annotation处理器通常会采用继承AbstractProcessor的方式来实现处理过程。javac命令中有一个-processor选项,它用来指定编译的时候使用指定的Annotation处理器提取并处理源文件中的注解。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/231019.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • log4j 格式_标题的含义和作用ppt

    log4j 格式_标题的含义和作用ppt2019独角兽企业重金招聘Python工程师标准>>>…

    2022年8月22日
    5
  • c语言位运算符的优先级_c语言运算符优先级由高到低

    c语言位运算符的优先级_c语言运算符优先级由高到低一、位运算(|&^~)运算符 描述 实例 & 按位与操作,按二进制位进行”与”运算。运算规则: 0&0=0;0&1=0;1&0=0;1&1=1; (A&B)将得到12,即为00001100 | 按位或运算符,按二进制位进行”或”运算。运算规则: 0|0=0;0|1=1;1|0=1;1|1=1;.

    2022年9月26日
    3
  • 数据库 函数依赖及范式(最通俗易懂)

    数据库 函数依赖及范式(最通俗易懂)数据库函数依赖及范式(最通俗易懂) 一、基础概念  要理解范式,首先必须对知道什么是关系数据库,如果你不知道,我可以简单的不能再简单的说一下:关系数据库就是用二维表来保存数据。表和表之间可以……(省略10W字)。  然后你应该理解以下概念:  实体:现实世界中客观存在并可以被区别的事物。比如“一个学生”、“一本书”、“一门课”等等。值得强调的是这里所说的“事物”不仅仅是看得见摸得着的“…

    2022年6月16日
    27
  • asp动态数组

    asp动态数组

    2021年11月15日
    60
  • spring源码分析之事务transaction上篇

    spring源码分析之事务transaction上篇

    2021年8月4日
    56
  • python怎么读取xlsx文件_arcgis地理加权回归

    python怎么读取xlsx文件_arcgis地理加权回归空间计量经济学打破大多数经典统计和计量分析中相互独立的基本假设,主要解决如何在横截面数据和面板数据的回归模型中处理空间相互作用(空间自相关)和空间结构(空间不均匀性)分析的问题。空间计量经济理论认为一个地区空间单元上的某种经济地理现象或某一属性值与邻近地区空间单元上同一现象或属性值是相关的。也就是说,各区域之间的数据存在与时间序列相关相对应的空间相关。空间计量模型所研究的空间效应包括空间自相关和空…

    2022年8月31日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号