不仅要写代码,还要写一手漂亮的代码
术语注释
class(类),包括所有普通的类(class)、enum、interface或注解类型(
@interface
)member(成员),包括类中的内部类(静态内部类)、变量、方法、构造方法(除初始化块和注释)
comment(注释),指实现时的解释性注释。对于文档注释,使用术语Javadoc
I. 源文件基础
所有源文件都应以UTF-8编码
关于空白符
- 除行终止符和字面字符串外,源文件中出现的空白符只能是ascii码为10的水平空格符。这意味着不应使用tab符做缩进。
- 使用转义字符(
\b
,\t
,\n
,\\
)等,而不是对应的八进制(如\012
)/Unicode(如\u000a
)转义
对于非ascii字符,要么使用实际上的Unicode字符(如
∞
),要么使用对应的Unicode转义(如\u221e
)。
建议使用前一种更方便理解的方法。当不得不使用Unicode转义时最好加上相应的注释
II. 源文件结构
每个源文件需要从上至下包含:
License或Copywrite信息
package
声明package
声明不换行import
声明- 除
import static
外,其他import
声明不使用*
import
声明不换行- 所有
import static
声明位于第一个block,其他import
声明位于第二个block,两个block间由一行空行分隔。每个block内的import
声明按ascii顺序从上到下排列 - 不使用
import static
来导入静态内部类,使用普通的import
声明
- 除
唯一的顶层
class
关于
class
内容的排布:决定成员排列先后顺序时应注意遵循某些逻辑结构。它们的排布位置对于类的易学性有着很大影响。
举个反例:将所有新建的方法都被添加到类的最后就不是一个好习惯,因为其破坏了类的逻辑结构。
另外,所有的重载都应处于相邻位置。
每一部分间由一行空行分隔
III. 格式
关于大括号
if
,else
,do
,while
,for
后必须跟大括号,即使该block为空或只有一行- 左括号:前不换行,后换行。右括号:前换行,当结束方法体、构造方法或一个命名类后才换行(如
else
前的右括号就不换行) - 当block为空时,右括号可以不换行直接紧接左括号闭合。除非该block为一个多block声明(如
if else
)的一部分
关于缩进:每级block前+2空格
每行一句statement
除特殊情况外,每行最多100个“字符”,超出部分需换行。一个“字符”等同于一个Unicode码位,意味着一个全角字符等同于两个“字符”
关于4中的换行
换行位置的选择:尽量不要打破更高的句法层次。对于非赋值运算符,在运算符之前换行;对于赋值运算符(包括foreach中的
:
)、逗号,
,在运算符之后换行。换行后,次行缩进在前一行的基础上+4空格
当有多个连续行时,当且仅当两行的行首元素在句法上平行时,不需要额外的缩进
空行
除其他提到的特殊情况外,单空行总是出现在类中连续的成员或初始化块间
两个连续的成员变量间不要求一定出现单空行,当需要创建更清晰的逻辑结构时可以添加
除以上规则外,在必要时,可以添加单空行提升代码可读性(如将代码分隔为多个逻辑块)
空格
对于
if
,for
,catch
,else
等关键字,在它们和括号(包括圆括号和大括号)间添加一个空格在所有左大括号
{
前添加一个空格,赋值操作除外(如String[][] x = {{"foo"}};
)所有二元/三元操作符的前后分别添加一个空格
类型转换括号
)
后添加一个空格类型和变量声明间添加一个空格,如
List<String> list
推荐在运算表达式中添加分组括号以更清晰地描述其含义。不要假定所有读代码的人都熟记运算符优先级表
特定结构
枚举类
每个枚举常量的分隔,
后的换行是可选的。当枚举中类中没有声明方法,且枚举常量不含Javadoc时,可以选择使用初始化数组的格式来初始化枚举类。变量声明
每次变量声明只指定一个变量。避免类似int a, b;
的声明方式(for循环内除外)
局部变量的声明应尽可能接近其初次使用位置,以最小化其作用域。避免在将所有局部变量全部声明在block的开头位置。
局部变量的声明往往同时伴随着初始化。如果是较为复杂的初始化,相关代码应紧邻声明位置。数组
数组声明允许被编写成block式(即在第一个大括号后换行)。
不要使用C风格的数组声明。使用String[] args
而不是String args[]
swtich
switch group的起始关键字
case
,default
前使用+2空格缩进每个switch group内部再+2空格缩进
每个switch group要么立刻结束(使用
break
,return
, 抛出异常等),要么添加注释说明需要执行下一个switch group。通常只要标注即足够。本条规则不适用于最后一个switch group每个switch block中必须包含一个
default
switch group,即使它不包含任何代码。
(对于枚举类型的switch block,如果包含了覆盖到所有可能性的case
,可以不添加default
switch group)注解
应用于类、方法或构造器的注解紧邻文档block,且每个注解占据独立的一行。这一行不适用于上述的100字符换行规则
1
2
3
public String getNameIfPresent() { ... }应用于成员变量的注解紧邻文档block,但多个注解可能被列于同一行,注解间由一个空格分隔
1
DataLoader loader;
对于方法参数、局部变量、类型的注解编写没有特别要求
注释
对于单行注释,
// ...
和/* ... */
均可对于多行注释
/* ... */
,随后的每行都应以一个和上一行对齐的*
开头1
2
3
4/*
* This is // And so /* Or you can
* okay. // is this. * even do this. */
*/不要将注释包含在用星号或其他字符绘制的框中
类与成员的修饰关键字
如出现,按照Java语言规范建议的顺序显示:
1
public protected private abstract default static final transient volatile synchronized native strictfp
数字字面量
声明
long
字面量时,使用L
后缀而不是l
,避免与1
混淆
III. 命名
适用于所有标识符的通用规则
标识符只能使用ascii字母,数字和下划线(在小部分情况下)。以便于每个有效的标识符名可以被正则表达式的一个
\w+
匹配。Google不使用特殊前缀、后缀。如
name_
,mName
,s_name
等不是Google Style按标识符类型
包名
使用全小写字母,连续的单词直接相连。如使用
com.example.deepspace
而不是com.example.deepSpace
和com.example.deep_space
类名
使用大写开头的驼峰(
UpperCamelCase
)类名通常为名词或名词词组,如
Character
和ImmutableList
接口名词性规则通常和类名一致,但也可以是形容词或形容词词组,如
Readable
测试类名通常以它们所测试的类名开头,以
Test
结束,如HashTest
,HashIntegrationTest
注解类型没有明确的命名规范
方法名
使用小写开头的驼峰(
lowerCamelCase
)方法名通常为动词或动词词组,如
sendMessage
和stop
下划线可以出现在JUnit的测试方法中,用于分隔方法名的逻辑部分,两部分分别使用驼峰。一种典型的模式是
<methodUnderTest>_<state>
,例如pop_emptyStack
常量名
使用下划线分隔的全大写(
CONSTANT_CASE
)常量名通常为名词或名词词组
常量指那些被
static final
修饰,且内容完全不可变,自身方法不会改变自身状态的成员变量。包括所有的原始数据类型、String
、不可变类型、以及不可变类型的不可变集合。如果某个实例的可观测状态可以改变,则其不是一个常量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// Constants
static final int NUMBER = 5;
static final ImmutableList<String> NAMES = ImmutableList.of("Ed", "Ann");
static final ImmutableMap<String, Integer> AGES = ImmutableMap.of("Ed", 35, "Ann", 32);
static final Joiner COMMA_JOINER = Joiner.on(','); // because Joiner is immutable
static final SomeMutableType[] EMPTY_ARRAY = {};
enum SomeEnum { ENUM_CONSTANT }
// Not constants
static String nonFinal = "non-final";
final String nonStatic = "non-static";
static final Set<String> mutableCollection = new HashSet<String>();
static final ImmutableSet<SomeMutableType> mutableElements = ImmutableSet.of(mutable);
static final ImmutableMap<String, SomeMutableType> mutableValues =
ImmutableMap.of("Ed", mutableInstance, "Ann", mutableInstance2);
static final Logger logger = Logger.getLogger(MyClass.getName());
static final String[] nonEmptyArray = {"these", "can", "change"};非常量成员变量名
使用小写开头的驼峰(
lowerCamelCase
)它们的名字通常为名词或名词词组
参数名
使用小写开头的驼峰(
lowerCamelCase
)避免在
public
修饰的方法中使用单字符的参数名局部变量名
使用小写开头的驼峰(
lowerCamelCase
)即使它们是
final
或不可变的,也不要将它们视作常量,或使用常量的命名风格类型变量名
可以使用下列两种风格之一
- 一个唯一的大写字母,可后跟一个单一的数字,如
T
,E
,T2
- 类名风格后跟一个大写字母
T
,如RequestT
,FooBarT
- 一个唯一的大写字母,可后跟一个单一的数字,如
驼峰标准
使用下述步骤将一个英语词组转换为驼峰
- 去除词组中的所有
'
号,并将其全转换为ascii形式。如 Müller’s algorithm 应该被转换为 Mullers algorithm - 将第一步的结果拆分成独立的多个单词。如果结果中已经有驼峰部分,推荐将其进一步拆分成单词。如 AdWords 可以被进一步拆分成 ad words
- 将所有字母变为小写(包括缩略词),并将每个单词(UpperCamelCase)或除首单词外的每个单词(lowerCamelCase)的第一个字母大写
- 连接所有单词,形成最后的驼峰表示
应用上述的规范化流程可以解决一些专有名词大写导致的不一致。
如 XML HTTP request 最后应写成
XmlHttpRequest
而不是XMLHTTPRequest
- 去除词组中的所有
IV. 编码实践
除重写
@Deprecated
方法外,总是为重写的方法添加@Override
注解除少数例外情况,对所有被
catch
的Exception都应做出相应响应(通常是记录一条log,或将其向外抛出)例外1:如确实存在某些不需要处理的情况,需要在
catch
block 中添加一条解释性注释1
2
3
4
5
6
7try {
int i = Integer.parseInt(response);
return handleNumericResponse(i);
} catch (NumberFormatException ok) {
// it's not numeric; that's fine, just continue
}
return handleTextResponse(response);例外2:在某些测试用例中,若
catch
到的Exception变量名以expected
起始,则可以不添加注释1
2
3
4
5try {
emptyStack.pop();
fail();
} catch (NoSuchElementException expected) {
}
通过类而不是对象来使用静态成员
避免使用 Finalizer, 即避免重写
Object.finalize
方法
V. Javadoc
格式
基本格式如下
1
2
3
4
5
6
7
8/**
* Multiple lines of Javadoc text are written here,
* wrapped normally...
*/
public int method(String p1) { ... }
/** An especially short bit of Javadoc. */
public int anotherMethod(String p1) { ... }当存在空行时,下段段首包含一个
<p>
,后不接空格。如:1
2
3
4
5
6
7
8/**
* First paragraph ...
*
* <p>Second paragraph ...
*
* <p>Third paragraph
*/
public int method(String p1) { ... }所有的标准
@
tag按@param
,@return
,@throws
,@deprecated
的顺序出现。同时,它们的描述不能为空。若某个tag的内容无法包含在一行内,下一行起始位置为本行
@
对齐位置+4空格摘要段
位于所有Javadoc的开始段,用于简要描述当前被注释内容的作用。
通常为一个名词词组或动词词组,不是一个完整的句子。
1
2
3
4
5// Correct
/** Returns the customer ID. */
// Incorrect
/** This method returns ... */在何处使用Javadoc
除一些例外情况,至少为类中的所有的
public
方法、所有protected
,public
成员添加Javadoc例外1:明显可自解释的方法可以不添加Javadoc,如Getter和Setter。
并不是所有 Getter / Setter 都可以省略Javadoc。例如若某个Getter名为
getCanonicalName
。Javadoc不能仅为一句/** Returns the canonical name. */
,更不要省略。因为读者可能无法理解 “Canonical Name” 的含义例外2:重写的方法允许不添加Javadoc
对于其他不做强制要求的部分,可以不严格遵循上述 1. 中的Javadoc格式。
另外,对于某些定义类或成员的总体目的或行为的实现注释,使用短Javadoc格式,即
/**