Java 注释
Java 注释
标签 : Java基础
五月份得知入职阿里云OS, 才开始学Java, 断断续续学习/使用半年, 越来越喜欢这个语言/工具. 后来被拥抱变化之后, 拿到的大部分offer是Java服务端研发; 一路走来, 踩了很多坑, 也有了一点小小的心得, 而且博客已经停更几个月, 今天就以博客形式把他记录下来吧.
2015下半年第一篇博客, 从最基础的Java注释开始:
程序员圈有一个笑话
最讨厌在写代码的时候写注释, 最讨厌别人的代码里面不写注释.
为什么写注释?
我自己亲身经历:
这段时间在微店实习, 第一个接手的项目是将原先北京团队的代码迁移到杭州, 由于底层技术架构的更换, 大部分代码需要重写, 前提是要理解原先的业务逻辑, 但当我在SVN上把代码拉下来, 看到意大利面似的一大坨代码里只有寥寥几行注释时, 整个人都不好了…
另一个遇到场景, 有时自己的代码有Bug, 或者需要重构, 此时就需要Review代码, 可是突然发现自己已经很难理解原先逻辑了(很可能这段代码只是你前几天刚刚写的), 因为我们已经很难回到当时状态.
还有一个重要的原因就是文档, 往往一个系统被开发出来, 文档要么不全, 要么更新落后, 如果后人要接手这一套系统, 就必须直接阅读源码, 如果此时在代码的关键逻辑之处能够有一两行注释提示, 新人就没有必要绞尽脑汁去猜测当时的设计方案了.
后来自己写代码时就尽量写注释提示, 虽然不一定完全按照下面的注释规范, 但会尽量用 最简单的语言把问题阐述清楚, 在逻辑转折之处添加几行说明, 无论是自己还是未来的接手人, 都会对现在的你感激不尽.
注释类型
Java提供三种注释方式: 单行注释、多行注释、文档注释.
- 单行/多行注释
单行注释与多行注释的作用就不再赘, IDEA快捷键分别如下:
command+/
: 以//
快速注释一行或多行 :
// Integer[] array = new Integer[10];
// for (int i = 0; i < array.length; ++i){
// array[i] = new Integer(i);
// }
command+option+/
: 以/**/
快速注释一行或多行
/*
Integer[] array = new Integer[10];
for (int i = 0; i < array.length; ++i){
array[i] = new Integer(i);
}
*/
- 文档注释
Java提供了一种功能非常强大的注释形式: 文档注释. 如果编写Java源代码时添加了文档注释, 然后通过JDK提供的javadoc工具就可以直接将代码里的注释提取成一份系统的API文档. 其注释形式为/** */
/**
* Initializes a newly created {@code String} object so that it represents
* the same sequence of characters as the argument; in other words, the
* newly created string is a copy of the argument string. Unless an
* explicit copy of {@code original} is needed, use of this constructor is
* unnecessary since Strings are immutable.
*
* @param original
* A {@code String}
*/
# javadoc 注释标签语法
标签 | 作用域 | 说明 |
---|---|---|
@author | 类 | 标明开发该类模块作者 |
@version | 类 | 标明该类模块的版本 |
@see | 类, 属性, 方法 | 参考转向(相关主题) |
@param | 方法 | 对方法中某参数的说明 |
@return | 方法 | 对方法返回值的说明 |
@exception | 方法 | 抛出的异常类型 |
@throws | 方法 | 与@exception相同 |
@deprecated | 方法 | 不建议使用该方法 |
注释原则
下面是我自己看到和用过的注释原则:
- 注释准确简洁
内容简单明了、含义准确, 尽量用最少的语言把问题阐述清楚, 防止注释的多义性,错误的注释不但无益反而有害. - 避免复杂注释
如果需要用复杂的注释来解释代码, 请检查此代码是否应该重写. 尽一切可能不注释难以理解的代码, 最好选择重构. - TODO List
为尚未完成的代码添加TODO注释, 提醒自己还需后续完善. - 注释形式统一
在整个项目中,使用一致的结构样式来构造注释. - 注释与代码同步更新
边写代码边注释,因为以后很可能没有时间来写注释了(可能在今天看来很明显的东西六周以后或许就不明显了). 通常描述性注释先于代码创建、解释性注释在开发过程中创建、提示性注释在代码完成之后创建. 修改代码的同时修改注释,保证代码与注释同步. - 注释就近
保证注释与其描述的代码相邻, 在代码上方或右方(最好上方)进行注释. - 注释不要过多
注释必不可少,但也不应过多,注释占程序代码的比例少于20%为宜.注释是对代码的“提示”,而不是文档. 如果代码本来就一目了然就不加注释. - 删除无用注释
在代码交付或部署发布之前, 删除临时或无关注释, 避免日后维护中产生混乱. - 必加注释之处
- 典型算法必有注释
- 代码不明晰处必有注释
- 在循环/逻辑分支组成的代码中加注释
- 为他人提供的接口必有注释
- 在代码修改处加修改标识
JDK注释参考:
- 类/接口注释
/**
* The <code>String</code> class represents character strings. All
* string literals in Java programs, such as <code>"abc"</code>, are
* implemented as instances of this class.
* (其他描述)
* @author Lee Boynton
* @author Arthur van Hoff
* @author Martin Buchholz
* @author Ulf Zibis
* @see java.lang.Object#toString()
* @see java.lang.StringBuffer
* @see java.lang.StringBuilder
* @see java.nio.charset.Charset
* @since JDK1.0
*/
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
...
}
- 构造器注释
/**
* Initializes a newly created {@code String} object so that it represents
* the same sequence of characters as the argument; in other words, the
* newly created string is a copy of the argument string. Unless an
* explicit copy of {@code original} is needed, use of this constructor is
* unnecessary since Strings are immutable.
*
* @param original
* A {@code String}
*/
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
- 方法注释
/**
* Returns <tt>true</tt> if, and only if, {@link #length()} is <tt>0</tt>.
*
* @return <tt>true</tt> if {@link #length()} is <tt>0</tt>, otherwise
* <tt>false</tt>
*
* @since 1.6
*/
public boolean isEmpty() {
return value.length == 0;
}
- 字段/属性注释
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
/**
* Class String is special cased within the Serialization Stream Protocol.
*
* A String instance is written initially into an ObjectOutputStream in the
* following format:
* <pre>
* <code>TC_STRING</code> (utf String)
* </pre>
* The String is written by method <code>DataOutput.writeUTF</code>.
* A new handle is generated to refer to all future references to the
* string instance within the stream.
*/
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
- 方法内注释
public boolean regionMatches(boolean ignoreCase, int toffset,
String other, int ooffset, int len) {
char ta[] = value;
int to = toffset;
char pa[] = other.value;
int po = ooffset;
// Note: toffset, ooffset, or len might be near -1>>>1.
if ((ooffset < 0) || (toffset < 0)
|| (toffset > (long)value.length - len)
|| (ooffset > (long)other.value.length - len)) {
return false;
}
while (len-- > 0) {
char c1 = ta[to++];
char c2 = pa[po++];
if (c1 == c2) {
continue;
}
if (ignoreCase) {
// If characters don't match but case may be ignored,
// try converting both characters to uppercase.
// If the results match, then the comparison scan should
// continue.
char u1 = Character.toUpperCase(c1);
char u2 = Character.toUpperCase(c2);
if (u1 == u2) {
continue;
}
// Unfortunately, conversion to uppercase does not work properly
// for the Georgian alphabet, which has strange rules about case
// conversion. So we need to make one last check before
// exiting.
if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
continue;
}
}
return false;
}
return true;
}
String(char[] value, boolean share) {
// assert share : "unshared not supported";
this.value = value;
}
char[] val = value; /* avoid getfield opcode */
public boolean contentEquals(CharSequence cs) {
if (value.length != cs.length())
return false;
// Argument is a StringBuffer, StringBuilder
if (cs instanceof AbstractStringBuilder) {
char v1[] = value;
char v2[] = ((AbstractStringBuilder) cs).getValue();
int i = 0;
int n = value.length;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
// Argument is a String
if (cs.equals(this))
return true;
// Argument is a generic CharSequence
char v1[] = value;
int i = 0;
int n = value.length;
while (n-- != 0) {
if (v1[i] != cs.charAt(i))
return false;
i++;
}
return true;
}
附: 开发者工具
- IDEA: 比较给力的Java IDE, 用过都说好(“自从用了IDEA, 写代码越来越有劲儿了”). 非常多很赞的功能, 如对Git、Maven的原生支持, 自动代码提示, 自带命令行, 黑色主题, UML类图生成… 而且现在IDEA家族也越来越强, Android开发的Android Studio、IOS的AppCode、C/C++的CLion、Python的PyCharm、PHP的PhpStorm、前端的WebStorm等等, 详细可查看Jet Brains官网.
- Git&GitHub: Git就不用多做介绍了, 算是开发者居家旅行之必备吧. 这方面的书/视频很多, 但在此我只推荐一部文档: 廖雪峰的Git教程. 虽然Git内容繁多, 但这篇文档却直取要害, 非常实用, 读了很多遍.
- Maven: Maven方面我也是新手, 在此我只推荐一部还不错的Maven方面的书, Maven实战, 一部国人写的实战类书籍.
- SQLPro for MySQL: 一款MySQL-GUI客户端, 用过MySQL-WorkBench、Navicat、Sequel Pro, 但最后还是选择的SQLPro for MySQL.
- ProcessOn: 免费绘图网站, UML、流程图、网络拓扑… 上手容易.
- Markdown: 这方面我也是初学者, 推荐两个客户端Cmd Markdown、MWeb.
- JSON查看&编辑
这方面除了TextLab没有发现其他好用的客户端, 只能推荐几个网站:
- RegExRx
Mac上非常好用的正则表达式匹配引擎, 用于测试正则表达式书写的正确与否. - iTerm2
忘掉Mac自带的终端吧, iTerm你值得拥有. - Alfred
最后, 推荐Mac效率神器Alfred, 推荐博客: 从零开始学习 Alfred(上):基础功能及设置, 其他功能还在探索.
在此只推荐了我所知道和常用一些小工具, 欢迎同学补充.