疯狂Java学习笔记(52)-----------Annotation(注释)第一篇

从JDK1.5开始,Java中增加了对元数据(MetaData)的支持,也就是Annotation(注释),这种Annotation与Java程序中的单行注释和文本注释是有一定区别,也有一定联系的。其实,我们现在说的Annotation是代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过Annotation,程序开发人员可以在不改变原来逻辑的情况下,在源文件嵌入一些补充的信息。代码分析工具,开发工具和部署工具可通过这些补充信息进行验正或者部署。

  Annotation提供了一条为程序元素设置元素的方法,从某些方面来看,Annotation就象修饰符一样被使用,可用于修饰包,类,构造器,方法,成员变量,参数,局部变量的声明,这些信息被存在Annotation的"name=value"对中。

注意

  Annotation是的一个接口,程序可以通过反射来获取指定程序元素的Annotation对象,然后通过Annotation对象来取得注释里的元数据。我们需要注意使用Annotation的地方,有的Annotation指的是java.lang.Annotation接口,有的指的是注释本身.

  Annotation能被用来为程序元素(类,方法,成员变量)设置元数据。值得指出的是:Annotation不能影响程序代码的执行,无论增加,删除Annotation,代码都始终如一地执行。如果希望让程序中的Annotation能在运行时起一定的作用,只有通过某种配套的工具对Annotation中的信息进行访问和处理,访问和处理Annotation的工具统称为APT(Annotation Processing Tool).

一.基本Annotation

  Annotation必须使用工具来处理,工具负责读取Annotation里包含的元数据,工具还会根据这些元数据增加额外的功能。在系统学习新的Annotation语法之前,先看一下Java提供的三个基本Annotation的用法:使用Annotation时要在其前面增加@符号,并把该Annotation当成一个修饰符使用,用于修饰它支持的程序元素.

三个基础的Annotation如下

@Override

@Deprecated

@SuppressWarings

(这三个基本的Annotation都定义在java.lang包下)

下面我们来依次这三个Annotation的用法

(1)限定重写父类方法 @Override

  @Override就是用来指定方法覆盖的,它可以强制一个子类必须覆盖父类的方法。如下程序中使用@Override指定子类Apple的info方法必须重写父类方法

注意: @Override Annotation只能用于作用于方法,不能用于作用于其它程序元素

实例:

<span style="font-size:18px;">class Shape{

   void draw(){

      System.out.println("形状");

   }

}

class Circle extends Shape{

   //将@Override省略效果相同

   @Override

   void draw() {

      System.out.println("圆形");

   }

}
</span>
<span style="font-size:18px;">public class  Fruit
{
 public void info()
 {
  System.out.println("水果的info方法...");
 }
}

class Apple extends Fruit
{
 @Override
 public void info()
 {
  System.out.println("苹果重写水果的info方法...");
 }
}

</span>

作用:

编译上面程序,可能丝毫看不出程序中@Override有任何作用。因为@Override Annotation的作用是告诉编译器检查这个方法,并从父类查找是否包含一个被该方法重写的方法,否则就编译出错。这个Annotation主要是帮助我们避免一些低级错务,例如我们把上面的Apple类中的info方法不小心写成inf0()方法,这样的“低级错务”,可能导致后期排错时的巨大障碍。

(2)标记已过时 @Deprecated

@Deprecated 用于表示某个程序元素(类,方法等)已过时,当其它程序使用已过时的类,方法时,编译器将会出现警告。如下程序指定Apple类中的info方法已过时,其它程序中使用Apple类的info方法时编译器将会出给出警告。

实例:

<span style="font-size:18px;">public classTest{

   public static void main(String[] args) {

      //下面调用shape的draw方法时,编译器会给出警告

      new Shape().draw();

   }

}

class Shape{

   @Deprecated

   void draw(){

      System.out.println("画一个形状");

   }

}
</span>
<span style="font-size:18px;">class Apple
{
 //定义info方法已过时
 @Deprecated
 public void info()
 {
  System.out.println("Apple的info方法");
 }
}
public class DeprecatedTest
{
 public static void main(String[] args)
 {
  //下面使用info方法时将会被编译器警告
  new Apple().info();
 }
}
</span>
<span style="font-size:18px;"> </span>

 

(3)@SuppressWarnings 抑制编译器警告

  @SupressWarnings指示被Annotation标识的程序元素(以及在程序元素中的所有子元素)取消显示指定的编译器警告。 @SuppressWarnings会一直作用于该程序元素的所有子元素,例如使用@SuppressWarning标识一个类来取消某个编译器警告,同时又标识该类里某个方法取消另一个编译器警告,那么将在此方法中同时取消这两个编译器警告。

  通常情况下,如果程序中使用没有泛型限制的集合将会引起编译器警告,为了避免这种编译器警告,可以使用@SuppressWarnings Annotation,下面程序取消了没有使用泛型的编译器警告。

<span style="font-size:18px;color:#000000;">@SuppressWarnings(value="unchecked")
public class SuppressWarningsTest
{
 public static void main(String[] args)
 {
  List<String> myList = new ArrayList();
 }
}</span>
<span style="font-size:18px;">//关闭整个类里的编译器警告

@SuppressWarnings("unchecked")

public class Test{

   public static void main(String[]args) {

      List<String> tempList = new ArrayList();

   }

}
</span>

说明:

  a.程序中使用@SuppressWarnings来关闭SuppressWarningTest类里的所有编译器警告,编译上面程序时将不会看到任何编译器警告。

       b.当我们使用@SuppressWarnings Annotation来关闭编译器警告时,一定要在插号里使用name=value对来为该Annotation的成员变量设置值.

二.自定义Annotation

  前面已经介绍了如何使用java.lang包下的三个标准Annotation。下面介绍如何定义 Annotation,并利用Annotation完成一些实际的功能。

(1)定义Annotation

  定义新的Annotation类型时使用@interface关健字(在原有的interface关健字前增加@符号),它用于定义新的Annotation类型。定义一个新的Annotation类型与定义一个接口非常像。

如下代码定义一个简单的Annotation

<span style="font-size:18px;">//使用@Test修饰类定义
@Test
public class MyClass
{
...
}</span>

  定义该Annotation之后,就可以在程序任何地方使用该Annotation ,使用Annotation时的语法非常类似于public ,final这样的修饰符,通常可以用于修饰程序中的类,方法,变量,接口等定义。

  通常我们会把Annotation放在所有修饰符之前,而且由于使用Annotation时可能还需要为其它成员变量指定值,因而Annotation的长度可能较长,所以通常把Annotaion另放一行.

<span style="font-size:18px;">//使用@Test修饰类定义
@Test
public class MyClass
{
...
}</span>

默认情况下,Annotation可用于修饰任何元素,包括类,接口,方法等。如下程序使用@TestAnnotation来修饰方法

public class MyClass
{
   //使用@TestAnnotation修饰方法
   @Test
   public void info()
   {
 ...
   }
}

Annotation不仅可以是这种简单的Annotation,Annotation还可以带用员变量,Annotation的成员变量在Annotation定义中以无参数来声明。其方法和返回值定义了该成员的名字和类型。

如下代码可以定义一个有成员变量的Annotation

public @interface MyTag

{

//定义两个成员变量Annotation

//Annotation中的成员变量以方法的形式来定义

String name();

int age();

}

 

 仔细观察:上面定义Annotation的代码与定义接口的语法非常像,只是上面MyTag使用@interface关健字来定义,而接口使用interface来定义

 一旦在Annotation里定义了成员变量之后,使用该Annotation时应该为该Annotation的成员变量指定值,

如下代码所示

public class Test

{

//使用带成员变量的Annotation时,需要为成员变量付值

MyTag(name=‘heyitang‘,age=30)

public void info()

{

...

}

}

  我们还可以在定义Annotation的成员变量时为其指定初始值,指定成员变量的初始值可使用default关健字。如下代码定义了MyTag Annotaion,该Annotation里包含了两个成员:name和age,这两个成元变量使用default指定了默认值

public @interface MyTag

{

//定义了两个成员变量的Annotaion

//以default为两个成员变量指定初始值

String name() default "yeeku";

int age() default 32;

}

  如果为Annotation的成员变量指定了默认值,使用该Annotation则可以不为这些成员变量指定值,而是直接使用默认值,如下代码所示

public class Test

{

//使用带成员变量的Annotation

//因为它的成员变量有默认值,所以可以无须为成员变量指定值

@MyTag

public void info()

{

...

}

}

当然我们介绍的Annotation是否可以包含成员指定值,如果MyTag为的成员变量指定了值,则默认值不会起作用

    总结

根据我们介绍的Annotation是否可以包含成员变量,我们可以把Annotation分为如下两类

       1.标记Annotation:一个没有成员变量的Annotation类型被称为标记。这种Annotation仅合使用自身的存在与否来为我们提供信息。如前面介绍的@Override,@Test等Annotation

2.元数据Annotation:那些包含成员变量的Annotation,因为它们可接受更多元数据,所以也被称为元数据Annotation

(2)提取Annotation

  前面已经提到:JAVA使用Annotation接口来代表元素前面的注释,该接品是所有Annotation类型的父接口。除此之外,JAVA在java.lang.reflect包下新增了AnnotationElement接口,该接口代表程序中可以接受注释的程序元素,该接口主要有如下几个实现类.

Class:类定义

Constructor:构造器定义

Field:类的成员变量定义

Method:类的方法定义

Package:类的包定义

AnnotationElement接口是所有程序元素(如Class,Method,Constructor)的父接口,所以程序通过反射获取某个类的了AnnotationElement对象(如Class,Method,和Constructor)之后,程序就可以调用该对象的如下三个方法来访问Annotation信息

getAnnotation(Class<T> annotationClass):返回该程序元素上存在的,指定类型的注释

Annotation[] getAnnotations:返回该程序元素上存在的所有注释

boolean isAnnotationPreset(Class<? extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注释,存在则返回true,否则返回false

(3)使用Annotation的例子

  下面介绍两个使用Annotation的例子,第一个Annotation Testable没有任何成员变量,仅是一个标记Annotation,它的作用是标记哪些方法是可测试的.

Testable.java源文件。

Testable.java源文件

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
//定义Testable Annotation将被javadoc工具提取
@Documented
public @interface Testable
{
}

上面程序定义了一个标记Testable Annotation,定义该Annotation时使用了@Retention和@Target两个系统元注释,其中@Retention注释指明Testabel注释可以保留多久,而@Target注释指定Testable能修饰的目标(只能是方法)。

  如下MyTest测试用例里定义了8个方法,这8个方法没有太大的区别,其中四个方法使用@testable注释来标记这些方法是可测试的

public class MyTest
{
 //使用@Testable标记注释指定该方法是可测试的
 @Testable
 public static void m1()
 {
 }
 public static void m2()
 {
 }
 //使用@Testable标记注释指定该方法是可测试的
 @Testable
 public static void m3()
 {
  throw new RuntimeException("Boom");
 }
 public static void m4()
 {
 }
 //使用@Testable标记注释指定该方法是可测试的
 @Testable
 public static void m5()
 {
 }
    public static void m6()
 {
 }
 //使用@Testable标记注释指定该方法是可测试的
 @Testable
 public static void m7()
 {
  throw new RuntimeException("Crash");
 }
 public static void m8()
 {
 }
}

正如前面提到的,仅仅使用注释来标记程序元素对程序是不会有任何影响的,这也是注释的一条重要原则,为了让程序中这些注释起作用,我们必须为这些注释提供一个注释处理工具。

下面的注释处理工具分析目标类,如果目标类中方法使用了@Testable注释修饰,则通过反射来运行该测试方法

import java.lang.reflect.*;
public class TestProcessor
{
 public static void process(String clazz)
  throws ClassNotFoundException
 {
  int passed = 0;
  int failed = 0;
  //遍历obj对象的所有方法
  for (Method m : Class.forName(clazz).getMethods())
  {
   //如果包含@Testable标记注释
   if (m.isAnnotationPresent(Testable.class))
   {
    try
    {
     //调用m方法
     m.invoke(null);
     //passed加1
     passed++;
    }
    catch (Exception ex)
    {
     System.out.printf("方法" + m + "运行失败,异常:" + ex.getCause() + "\n");
     failed++;
    }
   }
  }
  //统计测试结果
  System.out.printf("共运行了:" + (passed + failed)+ "个方法,其中:\n" +
   "失败了:" + failed + "个,\n" +
   "成功了:" + passed + "个!\n");
 }
}

参考博文:http://blog.sina.com.cn/s/blog_4c925dca0100hseu.html

http://blog.csdn.net/zhai56565/article/details/40503743

时间: 2024-10-13 12:35:21

疯狂Java学习笔记(52)-----------Annotation(注释)第一篇的相关文章

疯狂Java学习笔记(87)-----------十篇必读的Java文章

1.Brian Goetz:"管理工作:发人深省的部分" 这其实不是一篇博文,而是Brian Goetz对于甲骨文公司Java的管理的一个非常有趣的讨论的记录.在 以前我们将Java语言与Scala或者Ceylon相比较的时候,对其1-2个特性一直稍微有些意见. 对于为什么Java尽快变得和其他语言一样"时髦"不是一个好主意,Brian提出了很好的观点.每一个Java开发者都应有所了解(大约一个小时). Youtube链接. 2.Aleksey Shipilёv:(

疯狂Java学习笔记(53)------------Annotation(注释)第二篇

JDK的元Annotation JDK除了java.lang下提供了3个基本Annotation之外,还在java.lang.annotation包下提供了四个Meta Annotation(元Annotation),这四个都是用于修饰其它Annotation定义 (1)使用@Retention @Retention只能用于修饰一个Annotation定义,用于该Annotation可以保留多长时间,@Retention包含一个RetentionPolicy类型的value成员变量,所以使用@R

【疯狂Java学习笔记】【理解面向对象】

[学习笔记]1.Java语言是纯粹的面向对象语言,这体现在Java完全支持面向对象的三大基本特征:封装.继承.多态.抽象也是面向对象的重要组成部分,不过它不是面向对象的特征之一,因为所有的编程语言都需要抽象. 2.面向对象开发方法比较结构化开发方法的优势在于可以提供更好的可重用性.可扩展性.可维护性. 3.基于对象和面向对象的区别:基于对象也使用了对象,但是无法通过现有的对象作为模板来生成新的对象类型,继而产生新的对象,也就是说,基于对象没有继承的特点.而面向对象有继承,而多态则是建立在继承的基

疯狂java学习笔记之面向对象(八) - static和final

一.static: 1.static是一个标识符: - 有static修饰的成员表明该成员是属于类的; - 没有static修饰的成员表明该成员是属于实例/对象的. 2.static修饰的成员(Field.方法.初始化块),与类共存亡:static修饰的成员建议总是通过类名来访问,虽然它也可以通过实例来访问(实质也是通过类来访问的),所以平时若在其他程序中见到通过实例/对象来访问static成员时,可以直接将实例/对象 替换成类名: 3.程序都是先有类再有对象的,有可能出现有类但没有实例/对象的

疯狂Java学习笔记(84)----------关于 Java 对象序列化您不知道的 5 件事

数年前,当和一个软件团队一起用 Java 语言编写一个应用程序时,我体会到比一般程序员多知道一点关于 Java 对象序列化的知识所带来的好处. 关于本系列 您觉得自己懂 Java 编程?事实上,大多数程序员对于 Java 平台都是浅尝则止,只学习了足以完成手头上任务的知识而已.在本 系列 中,Ted Neward 深入挖掘 Java 平台的核心功能,揭示一些鲜为人知的事实,帮助您解决最棘手的编程挑战. 大约一年前,一个负责管理应用程序所有用户设置的开发人员,决定将用户设置存储在一个 Hashta

疯狂Java学习笔记(84)----------大约 Java 对象序列化,你不知道 5 事

几年前,.当一个软件团队一起用 Java 书面申请.我认识比一般程序猿多知道一点关于 Java 对象序列化的知识所带来的优点. 关于本系列 您认为自己懂 Java 编程?其实,大多数程序猿对于 Java 平台都是浅尝则止,仅仅学习了足以完毕手头上任务的知识而已.在本 系列 中,Ted Neward 深入挖掘 Java 平台的核心功能,揭示一些鲜为人知的事实,帮助您解决最棘手的编程挑战. 大约一年前,一个负责管理应用程序全部用户设置的开发者,决定将用户设置存储在一个 Hashtable中,然后将这

疯狂Java学习笔记(75)-----------NIO.2第一篇

Java 7引入了NIO.2,NIO.2是继承自NIO框架,并增加了新的功能(例如:处理软链接和硬链接的功能).这篇帖子包括三个部分,我将使用NIO.2的一些示例,由此向大家演示NIO.2的基本使用方法. 下一篇地址http://blog.csdn.net/u011225629/article/details/46386599 文件拷贝 Q:怎样拷贝一个文件? A:你可以使用java.nio.file.Files类的public static Path copy(Path source, Pat

疯狂Java学习笔记(77)-----------注释注意事项

代码注释,可以说是比代码本身更重要.这里有一些方法可以确保你写在代码中的注释是友好的: 不要重复阅读者已经知道的内容 能明确说明代码是做什么的注释对我们是没有帮助的. // If the color is red, turn it green if (color.is_red()) { color.turn_green(); } 要注释说明推理和历史 如果代码中的业务逻辑以后可能需要更新或更改,那就应该留下注释:) /* The API currently returns an array of

疯狂Java学习笔记(37)----------List集合

在网上找了很长时间关于List集合的资料,发现都是代码,理论性的东西太少了,对于想要深入的学习我来说,很困难呀,光看代码,不能解决问题呀!所以,自己精心的搜刮来了一点资料在这整理了一下! List集合! ·List列表 ·list: list代表有序.可重复集合,每个元素都有对应的索引,所以List集合中的元素可以重复.List集合默认暗元素的添加顺序设计元素! ·list当然也用于collection中的所有方法,并且自己也有添加了额外的方法,所有List实现类都可以调用这些方法来操作元素.L