(2.1.19)注释与注解

一、注释

首先来说注释有三种:

//      
/* */       
 /** */     

前两种编译器直接跳过,从来不阅读,第三种编译器是可以看懂的,当你使用javadoc这样的命令时会用到,用来生成api时用的。

二、注解

Annotations是一种元数据,其作用在于提供程序本身以外的一些数据信息,也就是说Annotation他不会属于程序代码本身,不参与逻辑运算,故而不会对原程序代码的操作产生直接的影响。

2.1 Java与元数据

元数据是关于数据的数据。在编程语言上下文中,元数据是添加到程序元素如方法、字段、类和包上的额外信息。

Java 元数据(Annotation)是 J2SE 5.0 (研发历时近三年,于2004年9月30日正式发布,代号为“Tiger”)新增加的功能之一,它在JSR-175规范中有详细定义。该机制允许在 Java 代码中添加自定义注释,并允许通过反射(reflection),以编程方式访问元数据注释。通过提供为程序元素附加额外数据的标准方法,元数据功能具有简化和改进许多应用程序开发领域的潜在能力,其中包括配置管理、框架实现和代码生成。

元数据的作用:

  • 用于创建文档
  • 跟踪代码中的依赖性
  • 执行编译时检查
  • 代码分析
  • 元数据还可用于协助程序元素与框架或者EJB、EMF 和 TestNG这样的工具之间的通信。EJB 3.0就广泛地应用了Java元数据,通过元数据来实现声明性请求企业服务,依赖性以及资源注入,消除了严格的EJB组件模型约束,并且取代了复杂的XML配置文件

元数据甚至使我们可以不用修改核心语言,就能够在 Java 语言中添加新功能,使核心语言成为一种开放式语言。在纯面向对象的语言中实现AOP就是使用元数据进行语言扩展的一个很好的例子。AspectWerkz、JBoss AOP以及AspectJ5 使用元数据将类的语义转换为一个aspect、将数据字段转换为一个pointcut、将方法转换为一个advice,等等。

2.2 简单的运用场景

@Override
public String toString() {
    return"This is String Representation of current object.";
}

上面的代码中,我重写了toString()方法并使用了@Override注解。但是,即使我不使用@Override注解标记代码,程序也能够正常执行。

那么,该注解表示什么?这么写有什么好处吗?

事实上,@Override告诉编译器这个方法是一个重写方法(描述方法的元数据),如果父类中不存在该方法,编译器便会报错,提示该方法没有重写父类中的方法。如果我不小心拼写错误,例如将toString()写成了toStrring(){double r},而且我也没有使用@Override注解,那程序依然能编译运行。但运行结果会和我期望的大不相同。

现在我们了解了什么是注解,并且使用注解有助于阅读程序。 当然这只是注解的一种使用场景而已

对于@Override注解你可能有些疑问,它什么都没做,那它是如何检查在父类中有一个同名的函数呢?
我们需要尤其注意的是,注解只是元数据,也就是说它只是一种类似vo的存储对象实例,无论想要完成任何逻辑上的处理,都需要其他代码的处理
也就是说,必然某处存在对@Override的处理

2.3 定义

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public@interface Override {

}

J2SE5.0版本在 java.lang.annotation提供了四种元注解,专门注解其他的注解:

@Documented –注解是否将包含在JavaDoc中,表示是否将注解信息添加在java文档中
@Retention –什么时候使用该注解
@Target –注解用于什么地方
@Inherited – 是否允许子类继承该注解

2.3.1 Retention

@Retention定义了该Annotation被保留的时间长短:

  • 某些Annotation仅出现在源代码中,而被编译器丢弃;
  • 而另一些却被编译在class文件中;
    • 编译在class文件中的Annotation可能会被虚拟机忽略
    • 而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。

使用这个meta-Annotation可以对 Annotation的“生命周期”限制,一般来说Annotation有如下三种使用情形:

  • Information for the compiler — Annotations can be used by the compiler to detect errors or suppress * warnings.
  • Compile-time and deployment-time processing — Software tools can process annotation information to generate code, XML files, and so forth.
  • Runtime processing — Some annotations are available to be examined at runtime.
  1. SOURCE 标记一些信息,为编译器提供辅助信息。

    • 可以为编译器提供而外信息,以便于检测错误,抑制警告等,譬如@Override、@SuppressWarnings等这类注解就是用于标识,可以用作一些检验。
  2. CLASS 编译时动态处理。

    • 一般这类注解会在编译的时候,根据注解标识,动态生成一些类或者生成一些xml都可以,在运行时期,这类注解是没有的,也就是在类加载的时候丢弃。
    • 会依靠动态生成的类做一些操作,因为没有反射,效率和直接调用方法没什么区别。ParcelableGenerator、butterknife 、androidannotaion都使用了类似技术
  3. RUNTIME 运行时动态处理。

    • 这个大家见得应该最多,在运行时拿到类的Class对象,然后遍历其方法、变量,判断有无注解声明,然后做一些事情。
    • 譬如使用表单验证注解@Validate,不保留活动的@SaveInstance

2.3.2 Target

@Target说明了Annotation所修饰的对象范围:Annotation可被用于

  • packages
  • types(类、接口、枚举、Annotation类型)
  • 类型成员(方法、构造方法、成员变量、枚举值)
  • 方法参数和本地变量(如循环变量、catch参数)

表示该注解用于什么地方。如果不明确指出,该注解可以放在任何地方。以下是一些可用的参数。需要说明的是:属性的注解是兼容的,如果你想给7个属性都添加注解,仅仅排除一个属性,那么你需要在定义target包含所有的属性。

ElementTypeexp
ElementType.PACKAGE用于记录java文件的package信息
ElementType.TYPE用于描述类、接口或enum声明
ElementType.CONSTRUCTOR构造方法声明
ElementType.METHOD构造方法声明
ElementType.PARAMETER参数声明
ElementType.FIELD字段声明(包括枚举常量),用于描述成员变量
ElementType.LOCAL_VARIABLE局部变量声明
ElementType.ANNOTATION_TYPE另一个注释

2.3.3 Inherited

表示该注解类型被自动继承。

如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

如果用户在当前类中查询这个元注解类型并且当前类的声明中不包含这个元注解类型,那么也将自动查询当前类的父类是否存在Inherited元注解,这个动作将被重复执行知道这个标注类型被找到,或者是查询到顶层的父类

2.3.4 内部实现

  • 在定义注解时,不能继承其他的注解或接口。
  • @interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。
    • 方法的名称就是参数的名称
    • 返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)
    • 可以通过default来声明参数的默认值。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Todo {

    public enum Priority {LOW, MEDIUM, HIGH}

    public enum Status {STARTED, NOT_STARTED}

    String author() default"Yash";

    Priority priority() defaultPriority.LOW;

    Status status() defaultStatus.NOT_STARTED;
}

@Todo(priority = Todo.Priority.MEDIUM, author = "Yashwant", status = Todo.Status.STARTED)
public void incompleteMethod1() {
    //Some business logic is written
    //But it’s not complete yet
}

如果注解中只有一个属性,可以直接命名为“value”,使用时无需再标明属性名。

@interface Author{
    String value();
}

@Author("Yashwant")
public void someMethod() {
}
  • 注解参数的可支持数据类型:

    1. 所有基本数据类型(int,float,boolean,byte,double,char,long,short)
    2. String类型
    3. Class类型
    4. enum类型
    5. Annotation类型
    6. 以上所有类型的数组
  • Annotation类型里面的参数该怎么设定:

    • 第一,只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型;   
    • 第二,参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和 String,Enum,Class,annotations等数据类型,以及这一些类型的数组.例如,String value();这里的参数成员就为String;  
    • 第三,如果只有一个参数成员,最好把参数名称设为”value”,后加小括号.例:下面的例子FruitName注解就只有一个参数成员。

注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null。
因此, 使用空字符串或0作为默认值是一种常用的做法。
当然,这个约束使得处理器很难表现一个元素的存在或缺失的状态,因为每个注解的声明中,所有元素都存在,并且都具有相应的值,为了绕开这个约束,我们只能定义一些特殊的值(-1,”“),例如空字符串或者负数,一次表示某个元素不存在,在定义注解时,这已经成为一个习惯用法

2.4 RUNTIME运行时注解的获取实例

2.4.1 反射获取RUNTIME注解的简单示例

现在我们需要写一个用户程序调用我们的注解。这里我们需要使用反射机制。如果你熟悉反射代码,就会知道反射可以提供类名、方法和实例变量对象。所有这些对象都有getAnnotation()这个方法用来返回注解信息。我们需要把这个对象转换为我们自定义的注释(使用 instanceOf()检查之后),同时也可以调用自定义注释里面的方法。

Class businessLogicClass = BusinessLogic.class;
for(Method method : businessLogicClass.getMethods()) {
    Todo todoAnnotation = (Todo)method.getAnnotation(Todo.class);
    if(todoAnnotation != null) {
        System.out.println(" Method Name : " + method.getName());
        System.out.println(" Author : " + todoAnnotation.author());
        System.out.println(" Priority : " + todoAnnotation.priority());
        System.out.println(" Status : " + todoAnnotation.status());
    }
}

2.4.2 关于RUNTIME注解的完整示例

@Retention(RetentionPolicy.RUNTIME)    
@Target({ElementType.TYPE,ElementType.METHOD})    
public @interface MyType {    
    String authorName();    
    String lastModified();    
    String bugFixes() default "ok";    
}

@MyType(authorName="hadeslee",lastModified="20061207")    
public class Test1 {    

    /** Creates a new instance of Test1 */   
    public Test1() {    
    }    
    @Deprecated   
    @MyType(authorName="hadeslee",lastModified="20061207",bugFixes="what")    
    public void doSth(){    

    }    
    @MyType(authorName="hadeslee",lastModified="20061207",bugFixes="what")    
    public void doAnother(){    

    }    
}    


public class GetAnno {    

    /** Creates a new instance of GetAnno */   
    public GetAnno() {    
    }    
    public static void main(String[] args)throws Exception {    
        Test1 t=new Test1();    
        Class c=Test1.class ;    
        Annotation[] as= c.getDeclaredAnnotations();    
        for(Annotation an:as){    
            System.out.println("类Test1的注释"+an);    
        }    
        Method med=c.getDeclaredMethod("doSth");    
        Annotation[] ass=med.getDeclaredAnnotations();    
        for(Annotation an:ass){    
            Class<!--/sp-->extends Annotation> clazz=an.annotationType();    
            Annotation[] ased=clazz.getAnnotations();    
            for(Annotation ad:ased){    
                System.out.println("注释的注释:"+ad);    
            }    
            System.out.println("方法doSth的注释:"+an);    
        }    
    }    
}   

此程序输出如下
类Test1的注释@testAnno.MyType(bugFixes=ok, authorName=hadeslee, lastModified=20061207)

注释的注释:@java.lang.annotation.Documented()
注释的注释:@java.lang.annotation.Retention(value=RUNTIME)
方法doSth的注释:@java.lang.Deprecated()

注释的注释:@java.lang.annotation.Retention(value=RUNTIME)
注释的注释:@java.lang.annotation.Target(value=[TYPE, METHOD])
方法doSth的注释:@testAnno.MyType(bugFixes=what, authorName=hadeslee, lastModified=20061207)

2.5 系统内置标准注解

注解的语法比较简单,除了@符号的使用外,他基本与Java固有的语法一致,JavaSE中内置三个标准注解,定义在java.lang中:

  • @Override:用于修饰此方法覆盖了父类的方法;
  • @Deprecated:用于修饰已经过时的方法;
  • @SuppressWarnnings:用于通知java编译器禁止特定的编译警告

2.5.1 Override

@Override,限定重写父类方法:

@Override 是一个标记注解类型,它被用作标注方法。它说明了被标注的方法重载了父类的方法,起到了断言的作用。如果我们使用了这种Annotation在一个没有覆盖父类方法的方法时,java编译器将以一个编译错误来警示。这个annotaton常常在我们试图覆盖父类方法而确又写错了方法名时发挥威力。使用方法极其简单:在使用此annotation时只要在被修饰的方法前面加上@Override即可

2.5.2 Deprecated

同样Deprecated也是一个标记注解。

当一个类型或者类型成员使用@Deprecated修饰的话,编译器将不鼓励使用这个被标注的程序元素。而且这种修饰具有一定的 “延续性”:如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员,虽然继承或者覆盖后的类型或者成员并不是被声明为 @Deprecated,但编译器仍然要报警

 值得注意,@Deprecated这个annotation类型和javadoc中的 @deprecated这个tag是有区别的:前者是java编译器识别的,而后者是被javadoc工具所识别用来生成文档(包含程序成员为什么已经过 时、它应当如何被禁止或者替代的描述)。

  在java5.0,java编译器仍然象其从前版本那样寻找@deprecated这个javadoc tag,并使用它们产生警告信息。但是这种状况将在后续版本中改变,我们应在现在就开始使用@Deprecated来修饰过时的方法而不是 @deprecated javadoc tag。

2.5.3 SuppressWarnnings

 @SuppressWarnings 被用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告。在java5.0,sun提供的javac编译器为我们提供了-Xlint选项来使编译器对合法的程序代码提出警告,此种警告从某种程度上代表了程序错误。例如当我们使用一个generic collection类而又没有提供它的类型时,编译器将提示出”unchecked warning”的警告。通常当这种情况发生时,我们就需要查找引起警告的代码。如果它真的表示错误,我们就需要纠正它。例如如果警告信息表明我们代码中的switch语句没有覆盖所有可能的case,那么我们就应增加一个默认的case来避免这种警告。
 
  有时我们无法避免这种警告,例如,我们使用必须和非generic的旧代码交互的generic collection类时,我们不能避免这个unchecked warning。此时@SuppressWarning就要派上用场了,在调用的方法前增加@SuppressWarnings修饰,告诉编译器停止对此方法的警告。
  
  SuppressWarning不是一个标记注解。它有一个类型为String[]的成员,这个成员的值为被禁止的警告名。对于javac编译器来讲,被-Xlint选项有效的警告 名也同样对@SuppressWarings有效,同时编译器忽略掉无法识别的警告名。
  
  annotation语法允许在annotation名后跟括号,括号中是使用逗号分割的name=value对用于为annotation的成员赋值。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值