71、Java文档注释的全面指南

Java文档注释的全面指南

1. 文档注释的结构

文档注释以 /** 开头,以 */ 结尾。每个文档注释描述紧随其后声明的标识符。注释行开头的 * 字符和 * 前面的空白字符会被忽略。注释的第一句是该标识符的摘要,“句子”指的是到第一个后面带有空白字符的句号为止的所有文本。

例如:

/**
 * Do what the invoker intends.  "Intention" is defined by
 * an analysis of past behavior as described in ISO 4074-6.
 */
public void dwim() throws IntentUnknownException;

此方法 dwim 的摘要为 “Do what the invoker intends.”。文档注释的第一句应是一个好的摘要。

HTML 标签常被嵌入文档注释中,用作格式指令或指向其他文档的交叉引用链接。几乎可以使用任何标准 HTML 标签,但使用 <h1> <h2> 等标题标签时要小心,因为它们可能会破坏整体文档的结构,不过在类和包文档中使用通常是安全的。要插入字符 < > & ,应分别使用 &lt; &gt; &amp; 。如果一行开头必须有 @ ,则使用 &#064; ,否则 @ 会被视为文档注释标签的开始。

只有紧接在类、接口、方法或字段之前的文档注释才会被处理。如果文档注释与其描述的内容之间有除空白、注释或注解之外的任何内容,该文档注释将被忽略。例如,若在文件顶部放置一个文档注释,且在该注释与类之间有包或导入语句,那么该文档注释将不会被使用。文档注释适用于在单个语句中声明的所有字段,因此在使用文档注释时,通常应避免在单个语句中声明多个字段。

2. 标签

文档注释可以包含携带特定信息的标签,标签有两种形式:
- 块标签 :定义独立的文本元素,所有块标签都以 @ 开头,如 @see @deprecated 。这些段落在生成的文档中会被特殊处理,产生标记段落、指向其他文档的链接等特殊效果。
- 行内标签 :可以出现在文档注释文本的任何位置,用于应用特殊格式,如 {@code} 用于使用代码字体,或生成特殊文本,如使用 {@link} 创建超文本链接。行内标签的形式为 {@tag-name args} ,其中可选的 args 值可能提供要格式化的文本或链接的目标。

除了带标签的段落和行内标签,文档注释中的文本被视为 HTML 的输入文本。可以使用标准的 <p> 标签创建文档中的段落分隔,使用 <pre> 创建示例代码块等。并非所有标签都需要由 javadoc 工具默认处理,具体处理哪些标签的详细信息请参考工具文档。

2.1 @see

@see 标签用于创建指向其他 javadoc 文档的交叉引用链接。可以指定任何标识符,但必须进行充分限定。例如,通常可以使用类成员的简单名称,但如果该成员是重载方法,则必须通过列出参数类型来指定要引用的重载方法。可以使用未限定名称指定当前包中的接口或类,但必须使用完全限定名称指定其他包中的类型。通过在成员名称前加 # 来指定类型的成员。以下是一些可能有效的 @see 标签示例:

@see #getName
@see Attr
@see com.magic.attr.Attr
@see com.magic.attr.Deck#DECK_SIZE
@see com.magic.attr.Attr#getName
@see com.magic.attr.Attr#Attr(String)
@see com.magic.attr.Attr#Attr(String, Object)
@see com.magic.attr
@see <a href="spec.html#attr">Attribute Specification</a>
@see "The Java Developer's Almanac"
  • 第一种形式引用与文档注释所在的同一类或接口,或任何包含的类或接口中的 getName 方法,同样的语法也适用于构造函数和字段。
  • 第二种形式引用当前包或导入包中的类。
  • 第三种形式通过完全限定名称引用类。
  • 接下来的四种形式引用成员,前两种展示了字段( DECK_SIZE )或方法( getName )的形式。可以直接使用方法名,因为在 Attr 类中只定义了一个 getName 方法。后两种形式引用 Attr 类的构造函数,一个接受 String 参数,另一个接受 String Object 参数。当构造函数或方法重载时,必须指定要引用的方法的参数。
  • 下一种 @see 形式将读者指向特定的包: com.magic.attr
  • 最后两种形式允许引用其他文档,第一种使用 <a...> 定义链接,第二种使用引号括起文档名称。

命名语言实体的 @see 形式(除最后两种外的任何上述形式)可以在实体后面跟一个标签,该标签名称将在生成的文档中使用,而不是实体的名称。例如:

@see #getName Attribute Names

这将创建一个指向 getName 文档的链接,但显示的文本为 “Attribute Names” 而不是 “getName”。通常应使用实际的成员名称,但偶尔可能会发现此功能很有用。

2.2 {@link} 和 {@linkplain}

@see 标签适用于文档末尾的 “See also” 部分。当交叉引用应包含在注释文本中时,可以在文本中嵌入 {@link} 行内标签。 @link 的语法为:

{@link package.class#member [label]}

标识符的规范与 @see 相同,可选标签也相同。以下句子嵌入了一个指向 getValue 方法的链接:

Changes the value returned by calls to {@link #getValue}.

{@linkplain} 标签的作用与 {@link} 相同,只是生成的文本使用普通字体而不是代码字体,这对于用纯文本标签替换链接很有用。

2.3 @param

@param 标签用于记录方法或构造函数的单个参数,或类、接口或泛型方法中的类型参数。如果使用 @param 标签,应该为方法的每个参数都有一个标签。段落的第一个单词被视为参数名称,其余部分是其描述:

@param max    The maximum number of words to read.

在记录类型参数时,应在类型参数名称周围使用 < >

@param <E>  The element type of this List

通常,类型参数不需要显式文档,因为它们的含义很明显。

2.4 @return

@return 标签用于记录方法的返回值:

@return       The number of words actually read.
2.5 @throws 和 @exception

@throws 标签用于记录方法或构造函数抛出的异常。如果使用 @throws 标签,应该为方法抛出的每种类型的异常都有一个标签。这个列表通常包括比必须在 throws 子句中声明的受检查异常更多的异常,无论是否需要,在 throws 子句中声明所有异常是个好主意,使用 @throws 标签时也是如此。例如,假设方法检查其参数以确保没有参数为 null ,如果发现 null 参数则抛出 NullPointerException ,那么应该在 throws 子句和 @throws 标签中声明 NullPointerException

@throws UnknownName   The name is unknown.
@throws java.io.IOException
            Reading the input stream failed; this exception
            is passed through from the input stream.
@throws NullPointerException
          The name is <code>null</code>.

@exception 标签与 @throws 等效。

2.6 @deprecated

@deprecated 标签将标识符标记为已弃用,即不适合继续使用。使用已弃用的类型、构造函数、方法或字段的代码在编译时可能会生成警告。应该确保已弃用的实体继续工作,以免破坏尚未更新的现有代码。弃用有助于鼓励代码用户更新到最新版本,同时保留现有代码的完整性。用户可以在他们选择的时候转向新机制,而不是在发布类型的新版本时被迫立即转向。应该为已弃用的实体指明替代方案:

/**
 * Do what the invoker intends.  "Intention" is defined by
 * an analysis of past behavior as described in ISO 4074-6.
 *
 * @deprecated  You should use dwishm instead
 * @see         #dwishm
 */
@Deprecated
public void dwim() throws IntentUnknownException;

虽然编译器在看到 @deprecated 标签时可能会生成警告,但如果看到 @Deprecated 注解,则一定会生成警告。应该始终同时使用这两者:使用 @deprecated 标签记录弃用的原因,使用 @Deprecated 注解通知编译器。

2.7 @author

@author 标签用于指定类或接口的作者:

@author Aristophanes
@author Ursula K. LeGuin
@author Ibid

可以根据需要指定任意数量的 @author 段落。为了在所有情况下都能获得一致的输出,每个 @author 段落应该只指定一个作者。

2.8 @version

@version 标签用于指定类或接口的任意版本规范:

@version 1.1
2.9 @since

@since 标签用于指定一个任意的版本规范,表示带标签的实体何时被添加到系统中:

@since 2.1

标记“诞生版本”有助于跟踪哪些实体较新,因此可能需要加强文档或测试。按照惯例,类或接口上的 @since 标签适用于该类或接口中没有自己的 @since 标签的所有成员。例如,如果上述 @since 标签位于一个类之前,那么该类的所有构造函数、字段和方法都将被假定在版本 2.1 中就已经存在,除非有例如 @since 2.2 标签的成员。

2.10 {@literal} 和 {@code}

{@literal text} 行内标签会使文本按原样打印,而不会被解释为 HTML 源。这意味着可以使用 & < > ,而不是 &amp; &lt; &gt; {@code text} 行内标签的行为与 {@literal text} 完全相同,只是文本以代码字体打印,与将 {@literal text} 标签用 <code> </code> HTML 标签包裹的效果相同。这些行内标签在编写关于泛型类型的内容时非常方便。

2.11 {@value}

{@value static-field-name} 标签会被指定的常量静态字段的实际值替换,该标签使用与 @see 标签相同的成员语法。例如,如果文档注释包含以下文本:

The valid range is 0 to {@value java.lang.Short#MAX_VALUE}.

则生成的文本将是:

The valid range is 0 to 32767.

为了方便起见,正在记录的常量静态字段的值可以通过 {@value} 引用,而无需指定字段名称。例如:

/** The default capacity of a new queue ({@value}). */
static final int DEFAULT_CAPACITY = 10;

生成的文本将是:

The default capacity of a new queue (10).

在指定特定的字段成员时,要注意不要在成员名称和右花括号之间留下任何空白,因为它将被视为名称的一部分。所有常量字段都在 javadoc 工具生成的 “Constant Field Values” 页面上进行记录。

2.12 {@docRoot}

javadoc 生成的文件被放入一个树结构中,子目录包含文档的各个部分。文件在树中的具体位置取决于 javadoc 的实现,并且可能会根据环境、用户偏好等因素而变化。在构建树之后,可能需要向其中添加内容,然后在文档注释中引用这些添加的部分。行内 {@docRoot} 标签允许在文档注释中放入对文档树中其他文件的相对引用, {@docRoot} 标签将被替换为生成的文档树顶部的相对路径。例如,以下句子:

Check out <a href="{@docRoot}/license.html">our license</a>.

将生成一个带有指向文档树根目录下 license.html 文件链接的句子。从文档注释生成文档后,可以将 license.html 复制到生成的文档树的顶部,上述链接将始终从生成的输出指向 license.html 文件。对于某些应用程序,可能会发现 “The doc-files Directory” 中描述的机制更有用。

2.13 {@inheritDoc}

{@inheritDoc} 标签用于从超类型复制文档注释,这将在下一节讨论。

3. 继承方法文档注释

如果没有为继承的方法提供文档注释,该方法将“继承”超类型的文档注释。这通常就足够了,特别是当一个类实现一个接口时,实现中的方法通常只需执行接口指定的操作,或者至少没有什么需要包含在文档注释中的内容。如果必须将接口或超类的所有文档注释复制到定义重写方法的类中,那将非常不方便。

在继承文档注释时,应该添加一个明确的注释,以免阅读代码的人认为忘记了为方法添加文档,例如:

// inherit doc comment
public void dwim() throws IntentUnknownException {
    // ...
}

虽然重写接口方法可能不需要对文档进行任何更改,但重写超类方法很可能涉及对方法契约的更改,如弱化参数的前置条件、强化返回值的后置条件或减少可能抛出的异常类型。为了更轻松地仅更改需要更改的文档部分,块标签定义了可以独立继承的单独实体:
- 主体注释 :方法的主要文档注释称为主体注释。如果不提供主体注释,将继承超类型的完整主体注释。如果提供任何文本作为主体注释,则不会继承超类型主体注释的任何部分。
- @param 注释 :每个 @param 注释根据参数名称独立继承。如果不为某个参数提供 @param 注释,则将继承超类型的 @param 注释。
- @return 注释 :如果未指定 @return 注释,则继承超类型的 @return 注释。
- @throws 注释 :有特殊的继承规则,将在下面讨论。

如果一个方法从超类和超接口都继承了文档注释,则使用接口的注释。

通常,对文档注释的更改只涉及添加额外信息。例如,一个重写方法可能保证它永远不会返回 null ,而父方法可能返回 null 。为了避免手动复制超类型的文档注释,可以使用 {@inheritDoc} 标签从超类型复制文档注释。 {@inheritDoc} 标签可以出现在主体注释中或任何块标签内,它会复制该实体的整个超类型的文档注释。例如,一个不再返回 null 的方法可以有如下文档注释:

/**
 * @return  {@inheritDoc}
 * This implementation never returns null.
 */

{@inheritDoc} 标签允许在继承的文档之前或之后轻松添加新文档,但它的有效性取决于原始文档的编写方式。例如,如果前一个方法的原始 @return 注释是:

@return The value last set, or null if it has not been set.

那么添加的文档会导致描述不够清晰:

Returns: The value last set, or null if it has not been set.
          This implementation never returns null.

为了清晰起见,重写整个文档注释可能比尝试扩充它更好。

4. 继承 @throws 注释

继承 @throws 注释有一个特殊规则:重写方法仅继承其 throws 子句中列出的、且被重写方法记录的异常的 @throws 注释。例如,如果原始方法有一个关于 IOException @throws 注释,而重写方法声明它抛出 IOException ,则该注释将被继承。但是,如果重写方法声明它抛出 FileNotFoundException IOException 的子类),则该注释不会被继承,因为该异常类型未被重写方法记录。对 @throws 注释继承时机的这种限制是必要的,因为自动继承 @throws 注释通常是错误的做法。不幸的是,不继承它们也可能是错误的做法。

重写方法可以抛出比被重写方法更少的受检查异常,但不能更多。因此,自动继承实际上不会抛出的受检查异常的 @throws 注释是错误的做法。对于受检查异常,只继承 throws 子句中列出的异常的文档注释是有意义的,毕竟这些是唯一可能抛出的受检查异常。所以,如果仍然抛出该异常,则继承注释;如果不抛出,则不继承注释。

运行时异常通常不会在 throws 子句中声明,这给情况增加了一些复杂性。通常希望继承像 IllegalArgumentException 这样的异常的 @throws 注释,因为对于重写的方法来说是非法的,对于重写后的方法通常也是非法的,所以如果不自动继承注释,该异常将不会被记录,这是错误的。但是,考虑一个接口,其中有可选实现的方法声明它们抛出 UnsupportedOperationException ,这个运行时异常由一个 @throws 注释记录。如果自动继承该注释,那么即使某个类的实现支持该操作,因此永远不会抛出该异常,该异常仍会被记录为被抛出,这同样是错误的。

通过要求在继承关联的 @throws 注释之前将异常列在 throws 子句中,可以避免继承不适用的 @throws 注释的问题。然而,这促进了一种将运行时异常列在方法的 throws 子句中的编程风格,这与标准做法相悖。建议继续只在方法的 throws 子句中列出受检查异常。对于未检查异常,建议手动重写 @throws 注释,但使用 {@inheritDoc} 来避免手动复制异常描述。例如,可以使用以下方式记录方法在其父方法会抛出 IllegalArgumentException 时仍然会抛出该异常:

@throws IllegalArgumentException {@inheritDoc}

仍然需要记住,未检查异常必须以这种方式手动记录。

总结

本文详细介绍了 Java 文档注释的结构、各种标签的使用以及方法文档注释的继承规则。通过合理使用文档注释和标签,可以提高代码的可读性和可维护性,方便开发者之间的交流和协作。在实际开发中,应根据具体情况选择合适的标签和注释方式,确保文档的准确性和清晰性。

表格总结

标签类型 标签名称 作用
块标签 @see 创建交叉引用链接到其他 javadoc 文档
块标签 @deprecated 标记标识符为已弃用
块标签 @author 指定类或接口的作者
块标签 @version 指定类或接口的版本规范
块标签 @since 指定实体添加到系统的版本
行内标签 {@link} 在注释文本中嵌入交叉引用链接
行内标签 {@linkplain} 与 {@link} 类似,但使用普通字体
行内标签 {@literal} 使文本按原样打印,不解释为 HTML
行内标签 {@code} 以代码字体打印文本
行内标签 {@value} 替换为常量静态字段的实际值
行内标签 {@docRoot} 提供文档树中其他文件的相对引用
行内标签 {@inheritDoc} 从超类型复制文档注释

mermaid 流程图 - 方法文档注释继承流程

graph TD;
    A[开始] --> B{是否有文档注释};
    B -- 否 --> C[继承超类型文档注释];
    B -- 是 --> D{是否有主体注释};
    D -- 是 --> E[不继承主体注释];
    D -- 否 --> F[继承主体注释];
    G{是否有 @param 注释};
    G -- 是 --> H[使用自身 @param 注释];
    G -- 否 --> I[继承超类型 @param 注释];
    J{是否有 @return 注释};
    J -- 是 --> K[使用自身 @return 注释];
    J -- 否 --> L[继承超类型 @return 注释];
    M{是否有 @throws 注释且异常在 throws 子句中};
    M -- 是 --> N[继承 @throws 注释];
    M -- 否 --> O[不继承 @throws 注释];
    C --> G;
    E --> G;
    F --> G;
    H --> J;
    I --> J;
    K --> M;
    L --> M;
    N --> P[结束];
    O --> P;

这个流程图展示了方法文档注释继承的主要流程,从判断是否有文档注释开始,逐步确定主体注释、 @param 注释、 @return 注释和 @throws 注释的继承情况。

Java文档注释的全面指南

5. 文档注释使用示例

为了更好地理解如何使用文档注释和各种标签,下面给出一个完整的 Java 类示例,展示如何对类、方法、字段等进行详细的文档注释:

/**
 * This is a sample class demonstrating the use of Java doc comments.
 * 
 * @author John Doe
 * @version 1.0
 * @since 1.0
 */
public class SampleClass {

    /**
     * The default value for the sample field.
     */
    public static final int DEFAULT_VALUE = 10;

    /**
     * A sample field with a description.
     */
    private int sampleField;

    /**
     * Constructs a new SampleClass object with the given initial value.
     * 
     * @param initialValue The initial value for the sample field.
     */
    public SampleClass(int initialValue) {
        this.sampleField = initialValue;
    }

    /**
     * Gets the current value of the sample field.
     * 
     * @return The current value of the sample field.
     */
    public int getSampleField() {
        return sampleField;
    }

    /**
     * Sets the value of the sample field.
     * 
     * @param newValue The new value to set for the sample field.
     * @throws IllegalArgumentException If the new value is negative.
     */
    public void setSampleField(int newValue) throws IllegalArgumentException {
        if (newValue < 0) {
            throw new IllegalArgumentException("Value cannot be negative.");
        }
        this.sampleField = newValue;
    }

    /**
     * Performs a sample operation and returns the result.
     * 
     * @param input The input value for the operation.
     * @return The result of the operation.
     * @throws ArithmeticException If the input is zero.
     * @see #getSampleField()
     */
    public int performOperation(int input) throws ArithmeticException {
        if (input == 0) {
            throw new ArithmeticException("Input cannot be zero.");
        }
        return sampleField / input;
    }
}

在这个示例中:
- 类 SampleClass 有一个整体的文档注释,包含了作者、版本和添加到系统的版本信息。
- 静态常量 DEFAULT_VALUE 有一个简单的描述性注释。
- 构造函数 SampleClass(int initialValue) 使用 @param 标签对参数进行了说明。
- getSampleField() 方法使用 @return 标签说明了返回值。
- setSampleField(int newValue) 方法使用 @param 标签说明参数,使用 @throws 标签说明了可能抛出的异常。
- performOperation(int input) 方法综合使用了 @param @return @throws @see 标签,提供了详细的文档信息。

6. 文档注释的最佳实践

为了使文档注释更加清晰、有用,以下是一些最佳实践建议:
1. 保持简洁明了 :文档注释的第一句应该是一个简洁的摘要,能够快速传达标识符的主要功能。后续的描述应该详细但不冗长,避免使用过于复杂的句子和术语。
2. 使用完整的句子 :虽然文档注释可以使用简洁的语言,但尽量使用完整的句子,以提高可读性。
3. 一致性 :在整个项目中保持文档注释的风格和格式一致。例如,使用相同的标签顺序、注释格式等。
4. 及时更新 :当代码发生变化时,及时更新相应的文档注释,确保文档与代码保持一致。
5. 避免重复 :如果多个方法或类有相似的功能,可以使用继承和 {@inheritDoc} 标签来避免重复编写文档注释。
6. 详细记录异常 :使用 @throws 标签详细记录方法可能抛出的异常,包括异常的类型和触发条件。
7. 使用标签 :合理使用各种标签,如 @see @param @return 等,提供丰富的信息。

7. 文档注释的工具支持

javadoc 是 Java 提供的一个强大的工具,用于从源文件中的文档注释生成 HTML 格式的文档。以下是使用 javadoc 工具的基本步骤:
1. 编写文档注释 :在 Java 源文件中按照规范编写文档注释。
2. 打开命令行 :进入包含 Java 源文件的目录。
3. 运行 javadoc 命令
- 生成单个文件的文档:
plaintext javadoc YourClass.java
- 生成整个包的文档:
plaintext javadoc -d doc com.yourpackage
其中, -d 选项指定生成文档的输出目录, com.yourpackage 是要生成文档的包名。
4. 查看生成的文档 :打开生成的 HTML 文件,通常位于指定的输出目录中。

表格总结 - 最佳实践

最佳实践 说明
保持简洁明了 第一句为简洁摘要,后续描述详细不冗长
使用完整的句子 提高可读性
一致性 项目中保持风格和格式一致
及时更新 代码变化时同步更新文档注释
避免重复 使用继承和 {@inheritDoc} 标签
详细记录异常 使用 @throws 标签说明异常类型和触发条件
使用标签 合理运用各种标签提供丰富信息

mermaid 流程图 - 使用 javadoc 工具流程

graph TD;
    A[编写文档注释] --> B[打开命令行];
    B --> C{选择生成方式};
    C -- 单个文件 --> D[javadoc YourClass.java];
    C -- 整个包 --> E[javadoc -d doc com.yourpackage];
    D --> F[生成文档];
    E --> F;
    F --> G[查看生成的 HTML 文件];

这个流程图展示了使用 javadoc 工具生成文档的主要流程,从编写文档注释开始,经过命令行操作,最终生成并查看文档。

总结

Java 文档注释是提高代码可读性和可维护性的重要工具。通过合理使用各种标签和遵循最佳实践,可以为代码提供详细、准确的文档。同时,利用 javadoc 工具可以方便地将文档注释转换为 HTML 格式的文档,便于开发者之间的交流和协作。在实际开发中,应该养成编写良好文档注释的习惯,为项目的长期发展奠定基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值