在多人合作的项目中,详细而有价值的代码注释显得尤为重要。这不仅仅减少了组内开发人员之间的无效沟通,也可以利用Javadoc工具,生成可对外发布的API文档。也潜在的强制开发人员规划好代码结构,比如类和方法的单一性问题等。
Tips:本文只讲述javadoc及Java注释规范,关于kotlin注释规范及 Dokka的使用,请参考官方文档:编写Kotlin代码文档
本文主要内容包含一下几个方面:
-
术语介绍
-
注释格式
-
块标签
-
内联标签
-
注释分类
-
源代码与生成API文档示例
-
IDE注释模板配置
-
参考资料
一、术语介绍
1、文档注释
java源代码中的特殊注释以/** ... */定义符。这些注释用javadoc处理以生成API文档。
2、javadoc
把文档注释生成API文档的JDK工具
3、源文件
Javadoc工具可以生成以下四种不同类型的“源”文件的输出
1.Java类(.java)的源代码文件——这些文件包含类、接口、字段、构造函数和方法注释
2.包注释文件——这些文件包含包注释
3.概述注释文件——这些文件包含关于软件包集的注释
4.杂项未处理的文件——包括图像、示例源代码、类文件、applet、HTML文件,以及任何您可能希望从前面的文件中引用的文件。
4、块标签
@tag 格式的标签(不被{ }包围的标签)为块标签,只能在主要描述(类注释中对该类的详细说明为主要描述)后面的标签部分(如果块标签放在主要描述的前面,则生成 API 帮助文档时会检测不到主要描述)。
5、内联标签
{@tag} 格式的标签(由{ }包围的标签)为内联标签,可以放在主要描述中的任何位置或块标签的注释中。
二、文档注释格式
doc注释是用HTML编写的,并且必须在类,字段,构造函数或方法声明之前。它由两部分组成-描述和标签。
类上的文档标注顺序:
1.概要描述,一句简要描述该类的作用,以句号作为结束。
2.详细描述,用一段或者多段话来详细描述该类的作用,一般每段话都以句号作为结束。详细描述中可以使用html标签,如等标签, 通常详细描述都以段落p标签开始。详细描述和概要描述中间通常有一个空行来分割。
、
、、、
3.文档标注,用于标注作者、创建时间、参阅类等信息
方法上文档标注顺序:
1.概要描述,一句简要描述该方法的作用,以句号作为结束
2.详细描述,一段或者多段话来详细描述该方法的作用,一般每段话都以句号作为结束。通常都以p
标签开始,而且p
标签通常都是单标签,不使用结束标签,其中使用最多的就是。
、
、、
pre
元素可定义预格式化的文本。被包围在pre
元素中的文本通常会保留空格和换行符。而文本也会呈现为等宽字体,pre
标签的一个常见应用就是用来表示计算机的源代码。
一般p经常结合pre
使用,或者pre
结合@code
共同使用(推荐@code
方式)
一般经常使用pre
来举例如何使用方法
3.文档标注,用于标注参数、返回值、异常、参阅等
示例:
/**
* 返回一个Image对象,然后可以将其绘制在屏幕上。
* url参数必须指定一个绝对 {@link URL} .
* name参数是一个相对于url参数的说明符。
*
* 无论图片是否存在此方法都会立即返回
*
* @param url 图片路径
* @param name 图片名字
* @return 返回一个 {@link Image}
* @see Image
*/
public Image getImage(URL url, String name) {
try {
return getImage(new URL(url, name));
} catch (MalformedURLException e) {
return null;
}
}
/**
* 通过Url获取Image
*
* @param url 图片地址
* @return 返回一个 {@link Image}
*/
private Image getImage(URL url) {
return null;
}
文档注释的第一句都为摘要句,介绍类或者方法的作用。紧挨着写的就是以@开头的标签
三、块标签
常用标签以及同时出现在同一注释块中的顺序如下:
-
@author
(仅类和接口是必需的) -
@version
(仅类和接口是必需的) -
@param
(仅方法和构造函数) -
@return
(仅方法) @throws
@see
@since
@deprecated
@author
适用范围:类、接口级别。
意义作用:指定对包或者类做出巨大贡献的技术人员,方便后续组内沟通。
使用规则:可以标注0个或者多个,@author标记并不重要,因为在生成API规范时不包含@author标记,因此只有查看源代码的人才能看到它。如果作者不明,请使用“ unscribed”作为@author的参数。
/**
* @author smile1
* @author smile2
*/
@version
适用范围:类、接口级别。
意义作用:指定版本文本,用于标记保存当前代码所属的版本号。
使用规则:可以标注0个或者多个
/**
* @version 1.15, 13 Dec 2006
*/
@param
适用范围:主要用于标记方法或者构造函数中参数。
意义作用:解释参数代表意义
使用规则:可以标注0个或者多个,@param标记后跟参数的名称(不是数据类型),后跟参数的描述,描述中的第一个名词是参数的数据类型。
/**
* 通过Url获取Image
*
* @param url 图片地址
* @return 返回一个 {@link Image}
*/
private Image getImage(URL url) {
return null;
}
@return
适用范围:仅可以指定方法的返回值,
意义作用:主要描述文本应描述返回类型和允许的值范围,对于返回void的方法和构造函数,请忽略@return;
/**
* @return true code>图像已完全加载并成功绘制
* false code> 否则
*/
public abstract boolean drawImage();
@throws(@exception是原始标记)
适用范围:仅可以指定方法
意义作用:对于任何已检查异常以及调用者想要捕获的未检查异常都应该包含该标记。不可预测的错误异常不必标记,例如NPE。
使用规则:可以标注0个或者多个,@throws标记后跟异常类型,后跟异常描述。
检查异常:除了类RuntimeException、Error及其子类,所有其他异常子类都是检查异常。
未检查异常:类RuntimeException、Error及其子类。
/**
* 用于案列展示
* @throws IOException 如果出现输入或输出异常
*/
public void foo() throws IOException {
}
@see
适用范围:任何注释文档
意义作用:添加带有链接或文本条目。
使用规则:可以标注0个或者多个,@see "字符串" 则显示字符串 ,@see 标签 给显示字符串添加链接。
- 多个@see显示顺序总体由小见大,如下所示:*
@see #field
@see #Constructor(Type, Type...)
@see #Constructor(Type id, Type id...)
@see #method(Type, Type,...)
@see #method(Type id, Type, id...)
@see Class
@see Class#field
@see Class#Constructor(Type, Type...)
@see Class#Constructor(Type id, Type id)
@see Class#method(Type, Type,...)
@see Class#method(Type id, Type id,...)
@see package.Class
@see package.Class#field
@see package.Class#Constructor(Type, Type...)
@see package.Class#Constructor(Type id, Type id)
@see package.Class#method(Type, Type,...)
@see package.Class#method(Type id, Type, id)
@see package
/*
* @see #finalize()
* @see Component#getGraphics()
* @see Component#paint(Graphics)
* @see Component#update(Graphics)
*/
public abstract void dispose();
@since
适用范围:任何注释文档
意义作用:表示此更改或功能自since-text指定的软件版本以来就已经存在
使用规则:可以标注0个或者多个,@since 1.5 意味着“自从从1.5版本以后就有该方法”。
/*
* @since 1.0
*/
public abstract void dispose();
@deprecated
适用范围:任何注释文档
意义作用: 表示不推荐使用
使用规则:使用@Deprecated注解弃用程序元素,然后编写@deprecated,进行解释说明。
/ **
* @deprecated 从JDK 1.1开始,由{@link #setBounds(int,int,int,int)}取代
* /
@Deprecated
public void setBounds(int a,int b){
}
@serial
作用:说明一个序列化属性
使用规则:@serial 字段描述 | include | exclude
示例:@serial 描述
@serialField
作用:记录可序列化类serialPersistentFields成员的ObjectStreamField组件
使用规则:@serialField field-name field-type field-description
@serialData
作用:数据描述以序列化的形式记录数据的类型和顺序
使用规则:@serialData data-description
使用范围:可以在writeObject、readObject、writeExternal、readExternal、writeReplace和readResolve方法的文档注释中使用。
四、内联标签(使用范围:注释内容内部嵌套)
{@code text}
作用:以代码字体显示文本,而不将文本解释为HTML标记或嵌套的javadoc标记。
如下:
{@code AC}
网页显示为:
AC(代码字体)
{@literal text}
作用:显示文本,而不将文本解释为HTML标记或嵌套的javadoc标记。
如下:
{@literal AC}
网页显示为:
AC
不会显示为粗体,也不是代码字体
{@docRoot}
作用:指明当前文档根目录的路径
{@inheritDoc}
作用:从直接父类继承的注释
使用规则:
- 在方法的主描述块中。在这种情况下,主描述是从层次结构上的类或接口复制的。
- 在方法的@return、@param和@throws标记的文本参数中。在本例中,标记文本从层次结构中相应的标记复制。
{@link}
作用:插入一个到另一个主题的链接
使用规则:{@link package.class#成员 标签},
/**
* 使用{@link #getComponentAt(int,int)getComponentAt}方法。
*/
public void getComponent(){
}
在网页上显示:
使用getComponentAt方法。
{@linkplain}
作用:插入一个到另一个主题的链接,但是该链接显示纯文本字体
使用规则:{@linkplain package.class#成员 标签},与相同{@link},不同之处在于链接的标签以纯文本而非代码字体显示。
/**
*请参阅{@linkplain add() 加法函数}
*/
在网页上显示为:
请参阅加法函数。
{@value}
作用:显示常量的值,该常量必须是 static 属性。
使用规则:
1.当{@value}在静态字段的doc注释中使用(不带任何参数)时,它将显示该常量的值。
2.与任何文档注释中的package.class#field参数一起使用时,它将显示指定常量的值。
/**
* 这个常数的值是{@value}.
*/
public static final String SCRIPT_START = "script"
/ **
*以{@value #SCRIPT_START}开头的脚本进行评估。
* /
public String evalScript(String script){
}
五、注释分类
1.类源代码文件
每个类或接口及其成员可以在.java文件中包含自己的文档注释。有关这些文档注释的更多详细信息。主要分为:类注释,方法注释,字段(域)注释。
如下:
类注释
/**
* 表示屏幕上窗口的类。
* 例如:
*
* Window win = new Window(parent);
* win.show();
*
*
* @author smile
* @version 1.15,2006年12月13日
* @see java.awt.BaseWindow
* @see java.awt.Button
*/
class Window extends BaseWindow {
...
}
方法注释
/**
* 返回指定索引处的字符。
* 索引的范围是0
到 length() - 1
.
*
* @param 所需字符的索引。
* @return 所需的字符。
* @exception StringIndexOutOfRangeException
* 如果索引不在范围内 0
* 到 length()-1
.
* @see java.lang.Character#charValue()
*/
public char charAt(int index) {
...
}
字段注释
/**
* 分量的x坐标。
*
* @see #getLocation()
*/
int x = 1263732;
2.包注释文件
每个包都可以有自己的文档注释,包含在它自己的“源”文件中,Javadoc工具将合并到它生成的包摘要页面中。要创建程序包注释文件,可以选择两个文件之一来放置注释。package-infojava或者package.html。
package-info.java例子如下:
在com.example.utils包下创建package-info.java
/**
* 提供项目所需的各种工具类
*
*包下类简介
*
* @author aurhor1;
* @author aurhor2
* @since 1.0
*/
package com.example.utils;
3.概述注释文件
每个应用程序或包集都可以有自己的概述文档注释,保存在它自己的“源”文件中,Javadoc工具将把它合并到它生成的概述页面中。
4.杂项未处理的文件
在源文件中包含希望Javadoc工具复制到目标目录的任何杂项文件。这些文件通常包括图形文件、示例Java源文件(. Java)和类文件(.class),以及独立的HTML文件,这些文件的内容会压倒普通Java源文件的文档注释。
六、示例
该示例代码以及API文档:github地址
package com.example.shareelem.exception;
import com.example.shareelem.NamingException;
/**
* 此异常用于描述解析链接时遇到的问题。
* 将附加信息添加到基本NamingException中以进行精确定位
* 链接问题。
*
* 类似于NamingException如何捕获名称解析信息,
* LinkException捕获精确定位的“链接”名称解析信息
* 解决链接时遇到的问题。所有这些领域可能
* 为空。
*
* - 链接解析名称。链接名称已解决的部分。
*
- 链接解析的对象。链接名称解析的对象。
*
- 链接剩余名称。链接名称尚未解析的部分。
*
- 链接说明。详细说明链接解析失败的原因。
*
*
*
* LinkException实例未针对并发进行同步
* 多线程访问。多个线程试图访问和修改一个LinkException实例应锁定该对象。
*
* @author author1
* @author author2
* @see Context#lookupLink
* @see LinkRef
* @since 1.3
*/
/*
* LinkException对象的序列化形式包括
*其NamingException超类的序列化字段,链接已解析
*名称(一个名称对象),链接解析对象,链接剩余名称
*(一个Name对象),以及链接说明String。
*/
public class LinkException extends NamingException {
/**
* 包含链接中已成功解析的部分。
* 这是一个复合名称,可以为null。
* 此字段由构造函数初始化。
* 您应该访问和操作此字段
* 通过其get和set方法。
*
* @serial
* @see #getLinkResolvedName
* @see #setLinkResolvedName
*/
protected Name linkResolvedName;
/**
* 包含链接部分解析成功的对象。
* 可以为null。该字段由构造函数初始化。
* 您应该访问和操作此字段
* 通过其get和set方法。
*
* @serial
* @see #getLinkResolvedObj
* @see #setLinkResolvedObj
*/
protected Object linkResolvedObj;
/**
* 包含尚未解析的其余链接名称。
* 这是一个复合名称,可以为null。
* 此字段由构造函数初始化。
* 您应该访问和操作此字段
* 通过其get和set方法。
*
* @serial
* @see #getLinkRemainingName
* @see #setLinkRemainingName
*/
protected Name linkRemainingName;
/**
* 包含为什么链接解析失败的例外。
* 可以为null。该字段由构造函数初始化。
* 您应该访问和操作此字段
* 通过其get和set方法。
*
* @serial
* @see #getLinkExplanation
* @see #setLinkExplanation
*/
protected String linkExplanation;
/**
* 构造一个带有解释的LinkException的新实例
* 所有其他字段均初始化为null。
*
* @param explanation 说明可能为空的字符串,其中包含其他
* 有关此异常的详细信息。
* @see java.lang.Throwable#getMessage
*/
public LinkException(String explanation) {
super(explanation);
linkResolvedName = null;
linkResolvedObj = null;
linkRemainingName = null;
linkExplanation = null;
}
/**
* 构造一个LinkException的新实例。
* 所有与链接无关和与链接有关的字段均初始化为null。
*/
public LinkException() {
super();
linkResolvedName = null;
linkResolvedObj = null;
linkRemainingName = null;
linkExplanation = null;
}
/**
* 检索已解析的链接名称的开头部分
* 成功地。
*
* @return 链接名称中已成功解析的部分。
* 这是一个综合名称。它可以为null,这意味着
* 尚未设置链接解析名称字段。
* @see #getLinkResolvedObj
* @see #setLinkResolvedName
*/
public Name getLinkResolvedName() {
return this.linkResolvedName;
}
/**
* 检索链接名称的剩余未解决部分。
*
* @return 链接名称中尚未解析的部分。
* 这是一个综合名称。它可以为null,这意味着
* 链接剩余名称字段尚未设置。
* @see #setLinkRemainingName
*/
public Name getLinkRemainingName() {
return this.linkRemainingName;
}
/**
* 检索成功解析的对象。
* 这是解析的链接名称所绑定的对象。
*
* @return 到目前为止已解决的可能为空的对象。
* 如果为null,则表示尚未设置链接解析对象字段。
* @see #getLinkResolvedName
* @see #setLinkResolvedObj
*/
public Object getLinkResolvedObj() {
return this.linkResolvedObj;
}
/**
* 检索与遇到的问题相关的解释
* 解析链接时。
*
* @return 可能为空的详细信息字符串,解释有关该问题的更多信息
* 解决链接。
* 如果为null,则表示没有
* 此异常的链接详细信息。
* @see #setLinkExplanation
*/
public String getLinkExplanation() {
return this.linkExplanation;
}
/**
* 设置与遇到的问题相关的解释
* 解析链接时。
*
* @param msg msg可能为null的详细信息字符串,解释有关该问题的更多信息
* 解决链接。如果为null,则表示将不记录任何细节。
* @see #getLinkExplanation
*/
public void setLinkExplanation(String msg) {
this.linkExplanation = msg;
}
/**
* 设置此异常的已解析链接名称字段。
*
* < tt > name tt>是一个复合名称。如果要设定
* 此字段使用复合名称或字符串,您必须
* “ stringify”复合名称,并创建一个复合
* 使用字符串用单个组件命名。那你可以
* 使用生成的复合名称调用此方法。
*
* 复制并存储< code > name code>。
* 随后对< code > name code>的更改不会
* 在此NamingException中影响副本,反之亦然。
*
* @param name name设置解析链接名称的名称。可以为空。
* 如果为null,则将链接解析的名称字段设置为null。
* @see #getLinkResolvedName
*/
public void setLinkResolvedName(Name name) {
if (name != null) {
this.linkResolvedName = (Name) (name.clone());
} else {
this.linkResolvedName = null;
}
}
/**
* 设置此异常的其余链接名称字段。
*
* < tt > name tt>是一个复合名称。如果要设定
* 此字段使用复合名称或字符串,您必须
* “ stringify”复合名称,并创建一个复合
* 使用字符串用单个组件命名。那你可以
* 使用生成的复合名称调用此方法。
*
* 复制并存储< code > name code>。
* 随后对< code > name code>的更改不会
* 在此NamingException中影响副本,反之亦然。
*
* @param name 设置剩余链接名称的名称。可以为空。
* 如果为null,则将剩余的name字段设置为null。
* @see #getLinkRemainingName
*/
public void setLinkRemainingName(Name name) {
if (name != null)
this.linkRemainingName = (Name) (name.clone());
else
this.linkRemainingName = null;
}
/**
* 设置此异常的链接解析对象字段。
* 这表示链接名称的最后一个成功解析的对象。
*
* @param obj 设置链接解析对象的对象。可以为空。
* 如果为null,则将链接解析对象字段设置为null。
* @see #getLinkResolvedObj
*/
public void setLinkResolvedObj(Object obj) {
this.linkResolvedObj = obj;
}
/**
* 生成此异常的字符串表示形式。
* 此字符串包含NamingException信息以及
* 链接的剩余名称。
* 此字符串用于调试,不用于解释
* 以编程方式。
*
* @return 此链接异常的非空字符串表示形式。
*/
public String toString() {
return super.toString() + "; Link Remaining Name: '" +
this.linkRemainingName + "'";
}
/**
* 生成此异常的字符串表示形式。
* 此字符串包含NamingException信息以及
* 解决链接的其他信息。
* 如果'detail'为true,则该字符串还包含有关以下内容的信息
* 链接解析的对象。如果为false,则此方法相同
* 作为toString()的形式,不接受任何参数。
* 此字符串用于调试,不用于解释
* 以编程方式。
*
* @param detail 如果为true,请添加有关已解析链接的信息
* 目的。
* @return此链接异常的非空字符串表示形式。
*/
public String toString(boolean detail) {
if (!detail || this.linkResolvedObj == null)
return this.toString();
return this.toString() + "; Link Resolved Object: " +
this.linkResolvedObj;
}
/**
* 使用JNDI 1.1.1中的serialVersionUID进行互操作性
*/
private static final long serialVersionUID = -7967662604076777712L;
};
七、生成API文档
1.利用javadoc指令
javadoc -d api文档目录 -sourcepath java源码目录 -subpackages 包名
具体可参考:javadocoptions
如上例子生成API文档则在dos中输入:
javadoc -d D:\javadoc -encoding utf-8 -charset utf-8 F:\AndroidProject\ShareElem\app\src\main\java\com\example\share
elem\exception\LinkException.java
2.利用IDEA或者AndroidStudio IDE开发工具自带的javadoc
打开AS工具Tools选项卡—GenerateJavaDoc
3.利用IDEJavadoc插件
setting—plugins下载javadoc插件
八、IDE设置注释模板
配置方法参考:IDEA注释模板设置
类文件配置内容
/**
* @author : user
* @createDate: datetime
* @updateUser: user
* @updateDate: datetime
* @updateRemark:
*/
八、参考资料
1.How to Write Doc Comments for the Javadoc Tool
2.The Java API Documentation Generator