[TOC]
Java编程必须遵守通用编程规范和本编程规范。
源文件结构 一个源文件包含(按顺序地):
许可证或版权信息(如有需要) package语句 import语句 一个顶级类(只有一个) 以上每个部分之间用一个空行隔开。
// good
import java.util.Date;
import java.util.Map;
// bad
import java.util.*;
每个import语句独立成行。
import语句分为以下几组,按照这个顺序,每组由一个空行分隔:
所有的静态导入独立成组
com.google imports(仅当这个源文件是在com.google包下)
第三方的包。每个顶级包为一组,字典序。例如:android, com, junit, org, sun
java imports
javax imports
组内不空行,按字典序排列。
3.4.1 只有一个顶级类声明
每个顶级类都在一个与它同名的源文件中(当然,还包含.java后缀)。
例外:package-info.java,该文件中可没有package-info类。
新的方法不能总是习惯性地添加到类的结尾
推荐按照访问权限的大小分别排列属性和方法:
- public 数据成员
- protected 数据成员
- 包级别(没有访问修饰符的,默认为friendly) 数据成员
- private 数据成员
- 构造函数
- public 方法
- protected 方法
- private 方法
- 包级别方法
一个类的多个构造函数,或是多个同名方法,这些函数/方法应该按顺序出现在一起,中间不要放进其它函数/方法。
除非作者和reviewer都认为去掉小括号也不会使代码被误解,或是去掉小括号能让代码更易于阅读,否则我们不应该去掉小括号。 我们没有理由假设读者能记住整个Java运算符优先级表。
@Override
@Nullable
public String getNameIfPresent() { ... }
@Override public int hashCode() { ... }
@Partial @Mock DataLoader loader;
具体命名参照通用编程规范,这里不再赘述。
严格来说,遵守本规范的Java代码中,标识符只有三种形式: UpperCamelCase、lowerCamelCase 和 CONSTANT_CASE。
除了常量外,所有标识符只由字母和数字组成。
// good
int count;
String name;
// bad
int iCount; // 常用 i 来表示整形
String sName; // 常用 s 来表示字符串
String nName; // 常用 m 来表示 数据成员
TODO 这条建议是否应该改成强制?
// good?
Button btnClose; // 关闭按钮
// good
com.google.util.imagetool
// bad
com.google.util.imageTool
com.google.util.image_tool
一、二级包名通常是域名的反写,因为域名不会重复。
如果有域名则采用域名的反写:
chenjianhang.com => com.chenjianhang
abc.net => net.abc
如果没有域名:
一级包名为域名后缀,如com、net、cn、me等。 公司的项目通常是com,个人的项目可以选择me,团队的项目可以采用team,其他的也可以。
二级包名是公司、组织、机构、团队或者个人的名称。
三级包名是项目名。
四级包名为模块名或层级名。
com.baidu.tieba.view
com.sina.weibo.xxx
只允许下面这两种形式
当整个 Javadoc 块能容纳于一行,且没有 Javadoc 标记 @XXX
时,可以使用单行形式。
/**
* 这是一个多行的 Javadoc 注释
* 这是一个多行的 Javadoc 注释
*/
public void foo() { ... }
/** 这是一个单行的 Javadoc 注释 */
7.1.2 段落
空行(即,只包含最左侧星号的行)会出现在段落之间和Javadoc标记(@XXX)之前(如果有的话)。 除了第一个段落,每个段落第一个单词前都有标签
,并且它和第一个单词间没有空格。
7.1.3 Javadoc标记
标准的Javadoc标记按以下顺序出现:@param, @return, @throws, @deprecated, 前面这4种标记如果出现,描述都不能为空。 当描述无法在一行中容纳,连续行需要至少再缩进4个空格。
7.2 摘要片段
每个类或成员的Javadoc以一个简短的摘要片段开始。这个片段是非常重要的,在某些情况下,它是唯一出现的文本,比如在类和方法索引中。
这只是一个小片段,可以是一个名词短语或动词短语,但不是一个完整的句子。它不会以A {@code Foo} is a...或This method returns...开头, 它也不会是一个完整的祈使句,如Save the record...。然而,由于开头大写及被加了标点,它看起来就像是个完整的句子。
Tip:一个常见的错误是把简单的Javadoc写成/** @return the customer ID /,这是不正确的。它应该写成/* Returns the customer ID. */。
// bad
/** 返回学生数量 */
public int getStudentCount() {
return studentCount;
}
当然,如果方法名中有比较偏僻的英文词汇,或者有一些相关信息是需要读者了解的, Javadoc 注释不应该省略
/** 返回符合规范的名称 */
public String getCanonicalName() {
}
boolean isOk = true;
// good
if (isOk) {
}
// good
if (!isOk) {
}
// bad
if (isOk == true) {
}
if (isOk == false) {
}
// good
i++;
i--;
// bad
i = i + 1;
i = i - 1;
// bad
i = i++; // Java 和 C 执行结果是不一样的
if (c++ = d++) {
...
}
// good
a = b + c;
d = a + r;
// bad
d = (a = b + c) + r;
if ((a == b) && (c == d)) // good
if (a == b && c == d) // 不反对
// good
i /= 4;
// bad
i = i >> 2;
// good
return booleanExpression;
// bad
if (booleanExpression) {
return true;
} else {
return false;
}
// good
i = (x >= 0) ? x : -x;
// bad
i = x >=0 ? x : -x;
// good
return (condition ? x : y);
// bad
if (condition) {
return x;
}
return y;
// god
i = (x >= 0) ? x : -x;
// bad
if (x >= 0) {
i = x;
} else {
i = -x;
}
所有 case 语句和 default 语句必须满足三条规范之一:
- 语句通过break、continue、return 或抛出异常来终止。如示例的
case 3
。 - 语句通过一条注释来说明程序将继续执行到下一个语句组。如示例的
case 2
。 - 语句希望继续执行到下一个语句组,并且语句内不需要执行任何代码,此时语句内不允许任何空行。如示例的
case 1
。
switch (input) {
case 1: // 注意case 1 与 case 2 之间不能有空行
case 2:
prepareOneOrTwo();
// 不用break
case 3:
handleOneTwoOrThree();
break;
default:
handleLargeNumber(input);
break;
}
即使 default 语句什么代码也不包含,你也必须这么做
new int[] { 0, 1, 2, 3 };
new int[] {
0, 1, 2, 3
};
new int[] {
0,
1,
2,
3
};
new int[] {
0, 1,
2, 3
};
new int[]
{0, 1, 2, 3};
中括号是类型的一部分。
// good
String[] args;
// bad
String args[];
推荐的顺序:
public protected private abstract static final transient volatile synchronized native strictfp
枚举常量间用逗号隔开,换行可选。
没有方法和文档的枚举类可写成数组初始化的格式:
private enum Suit { CLUBS, HEARTS, SPADES, DIAMONDS } 由于枚举类也是一个类,因此所有适用于其它类的格式规则也适用于枚举类。
类似的还有 XXX 和 FIXME。
// good
// TODO 这是一句必要的说明
// bad
//TODO 这是一句必要的说明
// bad
// TODO Auto-generated method stub
- TODO:代码还未编写。一般会说明需要添加什么功能。
- XXX:功能已实现,有待改进或优化。一般会说明要改进的地方。
- FIXME:代码有错误,不能正常工作,需要修改。一般会说明如何修正。
// TODO 这里需要添加XXX功能
// XXX 建议这里使用的排序改成冒泡排序,效率更高
// FIXME 当用户没有输入时,这里有bug,需加个判断
常见的处理方式就是打印日志。
如果它被认为是不可能的,则把它当作一个AssertionError重新抛出。
如果它确实是不需要在catch块中做任何响应,需要做注释加以说明。
try {
int i = Integer.parseInt(response);
return handleNumericResponse(i);
} catch (NumberFormatException ok) {
// it's not numeric; that's fine, just continue
}
return handleTextResponse(response);
例外:在测试中,如果一个捕获的异常被命名为expected,则它可以被不加注释地忽略。下面是一种非常常见的情形,用以确保所测试的方法会抛出一个期望中的异常, 因此在这里就没有必要加注释。
try {
emptyStack.pop();
fail();
} catch (NoSuchElementException expected) {
}
class Foo {
public static void foo() {
}
}
// good
Foo.foo();
// bad
new Foo().foo();
如果你觉得非要重载Object.finalize不可,请先仔细阅读和理解《Effective Java》 第7条:“Avoid Finalizers”,然后不要使用它。
@Deprecated