Java 注释

Sang Shin, sang.shin@sun.com, Sun Microsystems, www.javapassion.cn


如今,Java™ 技术已无处不在:大型企业系统、台式机、手持设备,甚至智能卡都有它的身影。因此,Java 技术是全世界开发人员的首选平台。所有这些都发生 1995 年 Java 技术产生后的较短的时间内。尽管该技术在第一版发布后有过不少更新和改进,但通过并入一系列 Java Specification Request(JSR),核心 Java 平台 5.0 版带来的语言层面的更新和改进是前所未有的。在本次动手课程中,您将学习到所有这些更新以及如何使用 Java 5.0 平台构建桌面应用程序。本动手实验室将指导您使用 Java 注释。


预计时间:150 分钟


软件需求

开始之前,需要在您的计算机中安装以下软件。


变更记录


实验室练习


练习 1:覆盖注释


程序员们有时会在想要覆盖一个方法时重载这个方法;在 Object 类中定义的 equals()方法就是这样一个典型的例子。AnnotatedClass.java 包含一个简单的例子:
public boolean equals(String otherName){

    int comparisson = name.compareTo(otherName);
    return (comparisson == 0);

}

程序员们认为他们正在覆盖 Object 类中的 equals()方法,但由于这样做是把 Object 作为一个参数,其结果(这是绝对正确的 Java 程序)反而是一个重载的方法。这样做会潜在地导致一些非常细微但也因此而难于调试的运行时错误。添加一套对此的标准注释类型可以在编译时辨识这些问题。

(1.1)覆盖注释


0. 如未启动 NetBeans IDE 则启动它。

1. 创建一个新 NetBeans 项目


图 1.10:创建一个新项目
2. 根据代码 1.11 修改由 IDE 生成的 OverrideAnnotation.java。仔细阅读该代码,需要特别注意粗体部分。
public class OverrideAnnotation {
   
    private String name;
    private int id;
   
    /**
     *  Constructor
     **/
    public OverrideAnnotation(){
        name = "Java Passion!";
    }
   
    public String getName(){
        return name;
    }
   
    public void setName(String name){
        this.name = name;
    }

    public int getId(){
        return id;
    }

    public void setId(int id){
        this.id = id;
    }
   
    /**
     *  Test of equals method overriding vs. overloading
     *  For exercise 1 change this method
     **/
    public boolean equals(String otherName){
        int comparisson = name.compareTo(otherName);
       
        return (comparisson == 0);
    }
   
    public static void main(String[] args){
        // TODO code application logic here
    }
   
}
代码 1.11:不带 @Override 注释的 equals()方法。3. 生成和运行项目
Created dir: C:\handson2\development\javase5generics\samples(2)\OverrideAnnotation\build\classes
Compiling 1 source file to C:\handson2\development\javase5generics\samples(2)\OverrideAnnotation\build\classes
compile:
run:
BUILD SUCCESSFUL (total time: 1 second)
图 1.13:运行 ExtendThreadClassTest0 应用程序的结果

5. 根据代码 1.15 修改 ExtendThreadClassTest0.java。需要添加的代码段突出显示为 蓝色粗体字
public class OverrideAnnotation {
   
    private String name;
    private int id;
   
    /**
     *  Constructor
     **/
    public OverrideAnnotation(){
        name = "Java Passion!";
    }
   
    public String getName(){
        return name;
    }
   
    public void setName(String name){
        this.name = name;
    }

    public int getId(){
        return id;
    }

    public void setId(int id){
        this.id = id;
    }
   
    /**
     *  Test of equals method overriding vs. overloading
     *  For exercise 1 change this method
     **/
    @Override
    public boolean equals(String otherName){
        int comparisson = name.compareTo(otherName);
       
        return (comparisson == 0);
    }
   
    public static void main(String[] args){
        // TODO code application logic here
    }
   
}
代码 1.15:含 @Override 注释的 equals()方法

6. 注意到编译程序现在检查出一个问题。这正是我们想实现的行为。


图 1.16:结果

7. 根据代码 1.17 修改 ExtendThreadClassTest0.java。需要添加的代码段突出显示为 蓝色粗体字
public class OverrideAnnotation {
   
    private String name;
    private int id;
   
    /**
     *  Constructor
     **/
    public OverrideAnnotation(){
        name = "Java Passion!";
    }
   
    public String getName(){
        return name;
    }
   
    public void setName(String name){
        this.name = name;
    }
   
    public int getId(){
        return id;
    }
   
    public void setId(int id){
        this.id = id;
    }
   
    /**
     *  Test of equals method overriding vs. overloading
     *  For exercise 1 change this method
     **/
    @Override
    public boolean equals(Object otherName){
        String newName = (String)otherName;
        int comparison = name.compareTo(newName);
       
        return (comparison == 0);
    }
   
    public static void main(String[] args){
        // TODO code application logic here
    }
   
}
代码 1.17:使其成为覆盖的方法

8. 编译该项目。注意到再也没有编译错误了。

解决方案
:针对此练习的解决方案作为可运行的 NetBeans 项目包含在动手实验室的压缩文件中。项目文件位于 <LAB_UNZIPPED_DIRECTORY>/javase5annotation/samples/OverrideAnnotation您可以打开并运行它。

返回练习顶部

结束语


在本练习中,您了解了如何使用简单的 @Override 注释在运行时检查无意覆盖。


                                                                                                                  返回顶部


练习 2:单成员注释

许多注释只需要附加单个值。仔细阅读包含单成员注释定义的 Mutator.java 文件。本练习的目的是编辑 AnnotatedClass.java  文件并为 setName 方法添加一个 Mutator 注释。有两种实现方法。第一种方法是按以下要求修改代码:

@Mutator(variable = "name")
public void setName(String name)

第二种方法如下。由于这是一个单成员注释,我们在分配值时不需要指明注释名称,这是可能的。但如果编译此代码,将出现编译器错误。为什么会这样呢? 答案是:对于单成员注释而言,用于该成员的标识符必须为调用值。修改 Mutator.java 来修正这个错误。
@Mutator("name")
 public void setName(String name)


(2.1)通过实现 Runnable 接口创建和启动线程 —— start()方法不在构造方法中


1. 创建一个新的 NetBeans 项目
2. 根据代码 2.11 修改由 IDE 生成的 MutatorAnnotation.java。此修改是将一个 Mutator 注释 添加到 setName 方法中。我们使用以上提及的第一种方法。仔细阅读该代码,需要特别注意粗体部分。
public class MutatorAnnotation {
   
    private String name;
    private int id;
   
    /**
     *  Constructor
     **/
    public MutatorAnnotation(){
        name = "Java Passion!";
    }
   
    public String getName(){
        return name;
    }
   
    @Mutator(variable = "name")
    public void setName(String name){
        this.name = name;
    }
   
    public int getId(){
        return id;
    }
   
    public void setId(int id){
        this.id = id;
    }
   
    /**
     *  Test of equals method overriding vs. overloading
     *  For exercise 1 change this method
     **/
    @Override
    public boolean equals(Object otherName){
        String newName = (String)otherName;
        int comparison = name.compareTo(newName);
       
        return (comparison == 0);
    }
   
    public static void main(String[] args){
        // TODO code application logic here
    }
   
}
代码 2.11:MutatorAnnoation

3. 编写 Mutator.java
public @interface Mutator {
  String variable();
}
代码 2.12:Mutator.java

4. 生成(编译)该项目
5. 根据代码 2.13 修改由 IDE 生成的 MutatorAnnotation.java
public class MutatorAnnotation {
   
    private String name;
    private int id;
   
    /**
     *  Constructor
     **/
    public MutatorAnnotation(){
        name = "Java Passion!";
    }
   
    public String getName(){
        return name;
    }
   
    @Mutator("name")
    public void setName(String name){
        this.name = name;
    }
   
    public int getId(){
        return id;
    }
   
    public void setId(int id){
        this.id = id;
    }
   
    /**
     *  Test of equals method overriding vs. overloading
     *  For exercise 1 change this method
     **/
    @Override
    public boolean equals(Object otherName){
        String newName = (String)otherName;
        int comparison = name.compareTo(newName);
       
        return (comparison == 0);
    }
   
    public static void main(String[] args){
        // TODO code application logic here
    }
   
}
代码 2.13:经过修改的 MutatorAnnotation.java

6. 注意到编译错误。(如图 2.13 和图 2.14 所示)如前所述,对单成员注释而言,用于该注释的标识符必须为调用值。
Compiling 2 source files to C:\handson2\development\javase5generics\samples(2)\MutatorAnnotation\build\classes
C:\handson2\development\javase5generics\samples(2)\MutatorAnnotation\src\MutatorAnnotation.java:18: cannot find symbol
symbol  : method value()
location: @interface Mutator
    @Mutator("name")
1 error
BUILD FAILED (total time:0 seconds)
图 2.14:编译错误


图 2.14:编译错误

6. 根据代码 2.15 修改 Mutator.java。
public @interface Mutator {
    String value();
}
代码 2.15:经过修改的 Mutator.java

7. 编译(生成)该项目
解决方案:针对此练习的解决方案作为可运行的 NetBeans 项目包含在动手实验室的压缩文件中。项目文件位于 <LAB_UNZIPPED_DIRECTORY>/javase5annotation/samples/MutatorAnnotation您可以打开并运行它。

                                                                                                              返回练习顶部




结束语

在本练习中,您了解了如何使用两种不同的方法添加单成员注释。

返回顶部



练习 3:多成员注释


注释可以被定义,从而具有多值并在需要的地方提供缺省值。在本练习中,您将了解到如何为访问器方法定义注释。这一注释含有定义为注释成员的变量名和变量类型。您也将了解如何设定缺省值 —— 为设定一个缺省值,请在成员名称后分号前的位置处添加缺省"值"。您还将了解如何修改 Accessor 注释,从而使 variableType 具有"String"这个缺省值。


(3.1)多成员注释


1. 创建一个新的 NetBeans 项目
2. 根据代码 3.11 修改 IDE 生成的 MultipleMemberAnnotation.java。仔细阅读该代码,需要特别注意粗体部分。
 public class MultipleMemberAnnotation {
  private String name;
  private int id;

  public MultipleMemberAnnotation(){
    name = "Java Passion!";
  }

  @Accessor(variableName = "name")
  public String getName(){
    return name;
  }

  @Accessor(variableName = "name", variableType = "int")
  public int getId(){
    return id;
  }

  public void setId(int id){
    this.id = id;
  }

  /**
   *  Test of equals method overriding v. overloading
   *  For exercise 1 change this method
   **/
  @Override
  public boolean equals(Object otherName){
    String newName = (String)otherName;
    int comparison = name.compareTo(newName);

    return (comparison == 0);
  }

    public static void main(String[] args){
        // TODO code application logic here
    }
}
代码 3.11:MultipleMemberAnnotation.java

3. 根据代码 3.12 编写 Accessor.java。本文件为访问器方法定义了一个注释。(访问器方法是访问字段值并采取 getXXX()形式的方法。) 此注释含有定义为注释成员的变量名和变量类型。该例也展示了如何为其中一个成员设置默认值。仔细阅读该代码,需要特别注意粗体部分。
/**
 *  Annotation definition for an accessor method.  This shows the use of
 *  multiple members.
 **/
public @interface Accessor {
  String variableName();
  String variableType()default "String";
}
代码 3.12:SimpleThread.java

4. 生成和运行项目
compile:
Building jar: C:\handson2\development\javase5generics\samples(2)\MultipleMemberAnnotation\dist\MultipleMemberAnnotation.jar
To run this application from the command line without Ant, try:
java -jar "C:\handson2\development\javase5generics\samples(2)\MultipleMemberAnnotation\dist\MultipleMemberAnnotation.jar"
jar:
BUILD SUCCESSFUL (total time:0 seconds)
图 3.13:运行 MultipleMemberAnnotation 应用程序的结果

解决方案:针对此练习的解决方案作为可运行的 NetBeans 项目包含在动手实验室的压缩文件中。项目文件位于 <LAB_UNZIPPED_DIRECTORY>/javase5annotation/samples/MultipleMemberAnnotation您可以打开并运行它。
                                                                                                              返回练习顶部


结束语


在本练习中,您了解了如何定义包含多成员的注释。                                                                                                                    返回顶部



练习 4:复杂注释类型


既然注释和接口的定义方式一样,在注释类型定义中就可以包含类型,而类型本身也是注释。在本练习中,您将了解如何定义一个叫做 Name 的复杂注释类型,该类型包含一个特定人的姓和名,以及定义将 Name 用作值类型的另外一个注释。

(4.1)复杂注释类型


1. 创建一个新的 NetBeans 项目
2. 根据代码 4.11 修改由 IDE 生成的 ComplexAnnotation.java
@Reviewer(@Name(first = "James", last = "Gosling"))
public class ComplexAnnotation {
   
    public static void main(String[] args){
        // TODO code application logic here
    }
   
}
代码 4.11:ComplexAnnotation.java

3. 根据代码 4.12 编写 Name.java
import java.lang.annotation.*;

public @interface Name {
  String first();
  String last();
}
代码 4.12:PrintStringsThread.java

4. 根据代码 4.13 编写 Reviewer.java。仔细阅读该代码,需要特别注意粗体部分。
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
public @interface Reviewer {
    Name value();
}
代码 4.13:TwoStrings.java

5. 编译和运行项目
compile:
run:
BUILD SUCCESSFUL (total time:0 seconds)
图 4.14:运行 ComplexAnnotation 应用程序的结果

解决方案:针对此练习的解决方案作为可运行的 NetBeans 项目包含在动手实验室的压缩文件中。项目文件位于 <LAB_UNZIPPED_DIRECTORY>/javase5annotation/samples/ComplexAnnotation您可以打开并运行它。
                                                                                                              返回练习顶部

结束语


在本练习中,您将了解如何定义一个使用其他注释的注释。
                                                                                                                    返回顶部

练习 5:元注释

为注释类型声明进行注释的注释类型称为元注释类型。java.lang.annotation 这个包提供了几个这样的注释类型。元注释类型可以用于限制其进行注释的注释类型。 Target 元注释被用于指定注释应用的位置。可能的选择包含以下内容: Retention 元注释被用于指定注释要保持多长时间。可能的选择包含以下内容: 在本练习中,您了解如何使用两个元注释类型。


(5.1)无线程间通信的生产者-消费者


1. 创建一个新的 NetBeans 项目
2. 根据代码 5.11 修改由 IDE 生成的 MetaAnnotation.java
public class MetaAnnotation {
   
    private String name;
   
    @Exposed("name")
    public void setName(String name){
        this.name = name;
    }
    public static void main(String[] args){
        // TODO code application logic here
    }
   
}
代码 5.11:MetaAnnotation.java

3. 生成和运行项目
Compiling 2 source files to C:\handson2\development\javase5annotation\samples\MetaAnnotation\build\classes
C:\handson2\development\javase5annotation\samples\MetaAnnotation\src\MetaAnnotation.java:6: annotation type not applicable to this kind of declaration
    @Exposed("name")
1 error
BUILD FAILED (total time:0 seconds)
图 5.12:编译错误

4. 根据代码 5.14 修改 MetaAnnotation.java 所做的修改是将 @Expose 注释用于指定一个字段而不是一个方法。
public class MetaAnnotation {
   
    @Exposed("name")
    private String name;
   
    // @Exposed("name")
    public void setName(String name){
        this.name = name;
    }
    public static void main(String[] args){
        // TODO code application logic here
    }
   
}
代码 5.14:MetaAnnotation.java

4. 生成和运行项目

解决方案:针对此练习的解决方案作为可运行的 NetBeans 项目包含在动手实验室的压缩文件中。项目文件位于 <LAB_UNZIPPED_DIRECTORY>/javase5annotation/samples/MetaAnnotation您可以打开并运行它。5. 针对您自己的练习,请通过使用以下代码 5.15 中的数组机制将注释应用到多处。
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
代码 5.15:TARGET 的多重应用

                                                                                                              返回练习顶部

结束语


在本练习中,您了解到如何使用 Target 和 Retention 元注释类型。


                                                                                                                    返回顶部

练习 6:在运行时读取注释


正如之前所解释的那样,可以这样说:注释包含在类文件中并通过 JVM 的运行时环境而可用。为访问运行时信息,需要使用反射 API,该 API 已经在 J2SE 5.0 中进行了修改从而引入了对 Metadata 的支持。.

Class 类现在有了两个额外的方法: Method、Constructor 和 Field 类同样也有两个新方法:

(6.1)调度一次性任务


1. 创建一个新的 NetBeans 项目
2. 根据代码 6.11 修改 IDE 生成的 RuntimeAnnotation.java。仔细阅读该代码,需要特别注意粗体部分。
import java.lang.annotation.*;

public class RuntimeAnnotation {
   
    AnnotatedClass ac;
   
    public RuntimeAnnotation(){
        ac = new AnnotatedClass();
    }
   
    public void printAnnotations(){
        Class c = ac.getClass();
        Annotation[] annotations = c.getAnnotations();
        int numberOfAnnotations = annotations.length;
        System.out.println("Class " + c.getName()+ " has " +
                numberOfAnnotations + " annotations");
       
        for (int i = 0 ; i < numberOfAnnotations; i++){
            System.out.println("Annotation " + i + ": " + annotations[i] +
                    ", type" + annotations[i].annotationType().getName());
        }
    }
   
    public static void main(String[] args){
        RuntimeAnnotation ar = new RuntimeAnnotation();
        ar.printAnnotations();
    }
}
代码 6.11:RuntimeAnnotation.java

3.  编写 AnnotatedClass.java
@Reviewer(@Name(first = "James", last = "Gosling"))
public class AnnotatedClass {
   
    private String name;
    private int id;
   
    public AnnotatedClass(){
        name = "Java Passion!";
    }
   
    @Accessor(variableName = "name")
    public String getName(){
        return name;
    }
   
    @Mutator(variable ="name")
    public void setName(String name){
        this.name = name;
    }
   
    @Accessor(variableName = "name", variableType = "int")
    public int getId(){
        return id;
    }
   
    public void setId(int id){
        this.id = id;
    }
   
    public boolean equals(String otherName){
        int comparisson = name.compareTo(otherName);
       
        return (comparisson == 0);
    }
}
代码 6.12:AnnotatedClass.java.

4. 编写 Reviewer.java。这段代码和上面编写的一样。
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
public @interface Reviewer {
    Name value();
}
代码 6.13:Reviewer.java

5. 编写 Name.java。这段代码和上面编写的一样。
import java.lang.annotation.*;

public @interface Name {
  String first();
  String last();
}
代码 6.14:Name.java

6. 编写 Mutator.java。这段代码和上面编写的一样。
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
public @interface Mutator {
    String variable();
}
代码 6.15:Name.java

7. 编写 Accessor.java。这段代码和上面编写的一样。
import java.lang.annotation.*;

/**
 *  Annotation definition for an accessor method.  This shows the use of
 *  multiple members.
 **/
@Retention(RetentionPolicy.RUNTIME)
public @interface Accessor {
    String variableName();
    String variableType()default "String";
}
代码 6.16:Name.java

8. 生成和运行项目
Class AnnotatedClass has 1 annotations
Annotation 0: @Reviewer(value=@Name(first=James, last=Gosling)), typeReviewer
图 6.17:运行 UnRuntimeAnnotation 应用程序的结果

解决方案:针对此练习的解决方案作为可运行的 NetBeans 项目包含在动手实验室的压缩文件中。项目文件位于 <LAB_UNZIPPED_DIRECTORY>/javase5annotation/samples/RuntimeAnnotation您可以打开并运行它。
                                                                                                              返回练习顶部


结束语


在本练习中,您了解了如何在运行时获取注释信息。                                                                                                                    返回顶部

课外练习(针对 Sang Shin“Java EE 编程在线课程”的学习者)


1. 课外练习是根据以下要求创建 MyOwnAnnotationExample 项目。
public @interface RequestForEnhancement {
    int    id();
    String synopsis();
    String engineer()default "[unassigned]";
    String date();    default "[unimplemented]";
}
2. 将以下文件以 JavaIntro-javase5annotation 作为 主题 发送到 javaintro1homework@sun.com




原文链接: http://www.javapassion.com/handsonlabs/javase5annotation/index.html