官方地址?google.github.io
本文档作为 Google 的 Java? 编程语言源代码编码标准的完整定义。当且仅当它遵守此处的规则时,Java 源文件才被描述为 Google 风格。
声明:在原文挡基础上做了一次翻译并总结了一下具体内容,换了一下排版样式等等。
在此也参考了另外一篇已经有很长时间的翻译版:Google Java 编程风格指南 - Hawstein 的博客
要提一点的是,国内的话可能更熟悉阿里的开发手册📎Java开发手册(嵩山版).pdf或者《Effective Java》(Gitee:effective-java-third-edition: Effective Java 第三版全文翻译,纯属个人业余翻译,不合理的地方,望指正,感谢~?在线网:README-Effective Java (高效 Java) 第三版-面试哥
所以Google的开发规范可能不怎么熟悉,甚至和我一样有点感觉不习惯。毕竟只是一种形式而已,不能说用了Google规范,就可以进谷歌,甚至代码更流畅,只能说,相互参照一些内容,可以让代码更方便阅读以及维护吧。
(不过,个人可能还是习惯了国内的阿里,Effective java 这本书还没有看完,唉,还是太懒了)
本文档作为 Google 的 Java? 编程语言源代码编码标准的完整定义。当且仅当它遵守此处的规则时,Java 源文件才被描述为?Google 风格。
与其它的编程风格指南一样,这里所讨论的不仅仅是编码格式美不美观的问题, 同时也讨论一些约定及编码标准。然而,这份文档主要侧重于我们所普遍遵循的规则, 对于那些不是明确强制要求的,我们尽量避免提供意见。
在本文件中,除非另有说明:
@interface
)。本文档中的示例代码是非规范的。也就是说,虽然这些示例采用 Google 风格,但它们可能无法说明表示代码的唯一*****时尚方式。示例中的可选格式选择不应作为规则强制执行*。(不强制你使用这种规范)
源文件以其最顶层的类名来命名,大小写敏感,文件扩展名为.java。
源文件以?UTF-8?编码。
除了行终止符序列之外,ASCII 水平空格字符?(?0x20?) 是唯一出现在源文件中任何位置的空白字符。这意味着:
对于任何具有?特殊转义序列?(\b
、?\t
、?\n
、?\f
、?\r
、 和 )的字符\"
, 将使用该序列而不是相应的八进制(例如 )或 Unicode(例如 )转义。\'``\\``\012``\u000a
对于剩余的非 ASCII 字符,使用实际的 Unicode 字符(例如?∞
)或等效的 Unicode 转义 符(例如 )?\u221
。选择仅取决于使代码更易于阅读和理解的方式,尽管强烈建议不要使用 Unicode 转义字符串文字和注释。
提示:在 Unicode 转义情况下,有时甚至在使用实际的 Unicode 字符时,解释性注释也会很有帮助。
例子 | 讨论 |
---|---|
String unitAbbrev = "μs"; | 最佳:即使没有注释也非常清楚。 |
String unitAbbrev = "\u03bcs"; // "μs" | 允许,但没有理由这样做。 |
String unitAbbrev = "\u03bcs"; // Greek letter mu, "s" | 允许,但尴尬且容易出错。 |
String unitAbbrev = "\u03bcs"; | 很差:读者不知道这是什么。 |
return '\ufeff' + content; // byte order mark | 好:对不可打印的字符使用转义符,并在必要时进行注释。 |
?提示:?永远不要由于害怕某些程序可能无法正确处理非ASCII字符而让你的代码可读性变差。当程序无法正确处理非ASCII字符时,它自然无法正确运行, 你就会去fix这些问题的了。(言下之意就是大胆去用非ASCII字符,如果真的有需要的话)
一个源文件包含(按顺序地):
以上每个部分之间用一个空行隔开。
如果一个文件包含许可证或版权信息,那么它应当被放在文件最前面。
package语句不换行,列限制(4.4节)并不适用于package语句。(即package语句写在一行里)
不使用通配符导入import java.util.*;
,无论是静态导入还是其他导入。
import语句不换行,列限制(4.4节, 列限制: 100)并不适用于import语句。(每个import语句独立成行)
import语句可分为以下几组,按照这个顺序,每组由一个空行分隔:
如果同时存在静态和非静态导入,则用一个空行分隔这两个块。 导入语句之间没有其他空行。
静态导入不要用于静态嵌套类,它们是通过正常导入导入的。
每个顶级类都在一个与它同名的源文件中。
例外:package-info.java
,该文件中可没有package-info
类。
类的成员顺序对易学性有很大的影响,但这也不存在唯一的通用法则。不同的类对成员的排序可能是不同的。 最重要的一点,每个类应该以某种逻辑去排序它的成员,维护者应该要能解释这种排序逻辑。比如, 新的方法不能总是习惯性地添加到类的结尾,因为这样就是按时间顺序, 而非某种逻辑来排序的。
当一个类有多个构造函数,或是多个同名方法,这些函数/方法应该按顺序出现在一起,中间不要放进其它函数/方法。
术语说明:块状结构(block-like construct)指的是一个类,方法或构造函数的主体。需要注意的是,数组初始化中的初始值可被选择性地视为块状结构(4.8.3.1节)。
大括号与if,else,for,do,while
语句一起使用,即使只有一条语句(或是空),也应该把大括号写上。
对于非空块和块状结构,大括号遵循 Kernighan 和 Ritchie 风格 (Egyptian brackets):
示例:
return () -> {
while (condition()) {
method();
}
};
return new MyClass() {
@Override public void method() {
if (condition()) {
try {
something();
} catch (ProblemException e) {
recover();
}
} else if (otherCondition()) {
somethingElse();
} else {
lastThing();
}
}
};
4.8.1节给出了enum类的一些例外。
一个空的块状结构里什么也不包含,大括号可以简洁地写成{}
,不需要换行。例外:如果它是一个多块语句的一部分(if/else 或 try/catch/finally) ,即使大括号内没内容,右大括号也要换行。
示例:
// This is acceptable
void doNothing() {}
// This is equally acceptable
void doNothingElse() {
}
这是不可接受的: 多块语句中没有简洁的空块
// This is not acceptable: No concise empty blocks in a multi-block statement
try {
doSomething();
} catch (Exception e) {}
每当开始一个新的块,缩进增加2个空格,当块结束时,缩进返回先前的缩进级别。缩进级别适用于代码和注释。(见4.1.2节中的代码示例)
每个语句后跟一个换行符(a line break)。
Java 代码的列限制为100个字符,除了下述例外,任何一行如果超过这个字符数限制,必须自动换行。
例外:
package
和import
语句(见3.2节和3.3节)。术语注意: 当本来可以合法占用一行的代码被分成多行时,我们称之为自动换行(line-wrapping)。
我们并没有全面,确定性的准则来决定在每一种情况下如何自动换行。很多时候,对于同一段代码会有好几种有效的自动换行方式。
注意(Note): 虽然行换行的典型原因是为了避免溢出列限制,但即使是实际上符合列限制的代码也可能由作者自行决定是否进行行换行。
提示(Tip): 提取方法或局部变量可以解决问题,而不需要换行。
自动换行的基本准则是:更倾向于在更高的语法级别处断开。
非赋值运算符
处断开,那么在该符号前断开(比如+,它将位于下一行)。注意:这一点与Google其它语言的编程风格不同(如C++和JavaScript)。 这条规则也适用于以下“类运算符”符号:点分隔符(.),类型界限中的&(<T extends Foo & Bar>
),catch块中的管道符号(catch (FooException | BarException e
)赋值运算符
处断开,通常的做法是在该符号后断开(比如=,它与前面的内容留在同一行)。这条规则也适用于foreach
语句中的分号。MyLambda<String, Long, Object> lambda =
(String label, Long value, Object obj) -> {
...
};
Predicate<String> predicate = str -> longExpressionInvolving(str);
注意: 换行主要目标是有清晰的代码,而不一定是适合最小行数的代码。
自动换行时,第一行后的每一行至少比第一行多缩进4个空格(注意:制表符不用于缩进。见2.3.1节)。
当存在连续自动换行时,缩进可能会多缩进不只4个空格(语法元素存在多级时)。一般而言,两个连续行使用相同的缩进当且仅当它们开始于同级语法元素。
第4.6.3水平对齐一节中指出,不鼓励使用可变数目的空格来对齐前面行的符号。
以下情况需要使用一个空行:
多个连续的空行是允许的,但没有必要这样做(我们也不鼓励这样做)。
除了语言或其他风格规则的要求外,除了字面意义、注释和Javadoc外,单个ASCII空格也只出现在以下地方。
{
) 之前,有两个例外:
@SomeAnnotation({a, b})
?(不使用空格)
String[][] x = {{"foo"}};
(大括号间没有空格,见下面第八项)
合取类型界限中的 & 符号:?
<T extends Foo & Bar>
处理多个异常的 catch 块的管道:?
catch (FooException | BarException e)
增强型?
for
(“foreach”)语句中的冒号 ( : )lambda 表达式中的箭头:?
(String str) -> str.length()
但不是:
方法引用的两个冒号 (?
::
),写成Object::toString
点分隔符 (?
.
),写成?object.toString()
,:;
或右括号 (?)
)List<String> list
new int[] {5, 6}
并且?new int[] { 5, 6 }
都是有效的
[]
或...
之间。Note:此规则永远不会被解释为要求或禁止在行首或行尾添加额外的空格;它只针对内部空间。
术语说明:水平对齐是在你的代码中增加可变数量的额外空间,目的是使某些标记出现在前几行的某些其他标记的正下方。(水平对齐指的是通过增加可变数量的空格来使某一行的字符与上一行的相应字符对齐)
这种做法是允许的,但绝不是谷歌风格所要求的。在已经使用了水平对齐的地方,甚至不需要保持水平对齐。
下面是一个没有对齐的例子,然后使用对齐:
// 这很好
private int x;
// 这个也可以
private Color color;
// 允许的,但将来会被编辑
private int x;
// 可以不对齐
private Color color;
只考虑一个可能会出现的问题,但现在可能会出现未来的变化。更常见的是它会提示编码器(可能是现在)也调整这行的空白,可能触发更改行级联的重新定义。会导致没有任何意义的重要工作,但在最好的情况下,它仍然会显示历史信息,减少慢阅者的速度并混合破解。
提示:对齐可以提高可读性,但会给未来的维护带来问题。考虑到未来的变化,只需要触及一行。这种改变可能会使以前令人满意的格式被弄乱,这是被允许的。但更多的时候,它会促使编码者(也许是你)对附近几行的空白进行调整,可能会引发一连串的改写。这一行的变化现在有一个 "爆炸半径"。这在最坏的情况下会导致无意义的忙碌,但在最好的情况下,它仍然会破坏版本历史信息,拖慢审查人员的速度,加剧合并冲突。
只有当作者和审查者一致认为没有小括号,代码就不会被误解,也不会使代码更容易阅读时,才会省略可选的分组小括号。假设每个读者都记住了整个Java操作符优先级表是不合理的
在枚举的后面也允许有引号的空格,换行符是可选的。(建议每个枚举类以Enum结尾)
private enum AnswerEnum {
YES {
@Override public String toString() {
return "yes";
}
},
NO,
MAYBE
}
一个没有方法也没有常量文档的枚举类可以选择性地被格式化为数组初始化器(参见第4.8.3.1节关于数组初始化器),
private enum SuitEnum { CLUBS, HEARTS, SPADES, DIAMONDS }
因为枚举类是类,所以所有其他用于格式化类的规则都适用。
每个变量声明(字段或局部)只声明一个变量:不使用诸如int a, b;的声明。
例外:在for循环的头部可以接受多个变量的声明。
局部变量不习惯于在其包含的块或块状结构的开始处声明。相反,局部变量会在靠近它们第一次使用的地方被声明(在合理范围内),以最小化它们的范围。局部变量的声明通常有初始化器,或者在声明后立即被初始化。
任何数组的初始化器都可以有选择地被格式化为 "类块结构"。例如,下面这些都是有效的(不是一个详尽的列表)。
new int[] { new int[] {
0, 1, 2, 3 0,
} 1,
2,
new int[] { 3,
0, 1, }
2, 3
} new int[]
{0, 1, 2, 3}
方括号构成类型的一部分,而不是变量的一部分。String[] args,而不是String args[]。
术语说明:switch语句的大括号内是一个或多个语句组。每个语句组由一个或多个switch标签(either case FOO: or default:)组成,后面是一个或多个语句(或者,对于最后一个语句组,是零个或多个语句)
与其他块一样,switch块的任何内容缩进+2。
在切换标签之后,会有一个换行,并且缩进程度会增加+2,就像一个块被打开一样。接下来的切换标签会回到之前的缩进水平,就像一个区块被关闭一样。(每个switch标签后新起一行,再缩进2个空格,写下一条或多条语句。)
在switch块中,每个语句组要么突然终止(用break、continue、return或抛出的异常),要么用注释来表示执行将或可能继续到下一个语句组。任何能传达 "穿越 "概念的注释都是足够的(通常是//穿越)。这个特殊的注释在switch块的最后一个语句组中是不需要的。
例子:
switch (input) {
case 1:
case 2:
prepareOneOrTwo();
// fall through
case 3:
handleOneTwoOrThree();
break;
default:
handleLargeNumber(input);
}
注意,在case 1:之后不需要注释,只需要在语句组的末尾注释。
default
是存在的每个switch语句都包括一个default语句组,即使它不包含任何代码。
例外:枚举类型的switch语句可以省略默认语句组,如果它包括涵盖该类型所有可能值的明确案例。这使得IDE或其他静态分析工具能够在遗漏任何案例时发出警告
适用于类、方法或构造函数的注解紧接着出现在文档块之后,每个注解都被列在自己的一行中(也就是说,每行一个注解)。这些换行并不构成换行(第4.5节,换行),所以缩进程度不会增加。
例子:
@Override
@Nullable
public String getNameIfPresent() { ... }
例外:单个的注解可以和签名的第一行出现在同一行。例如:
@Override public int hashCode() { ... }
适用于一个字段的注释也紧随在文档块之后,但在这种情况下,多个注解(可能是参数化的)可以列在同一行;例如。
@Partial @Mock DataLoader loader;
对于参数、局部变量或类型上的注释的格式化,没有特定的规则。
本节讨论实现注释。 Javadoc 在 Javadoc 第7节中单独讨论。
任何断行都可以在任意的空白处,然后是一个执行注释。这样的注释使该行成为非空行。
区块注释的缩进程度与周围的代码相同。它们可以是/* ...?/风格或//...风格。对于多行的/?...?/ 注释,后面的行必须以开头,与前一行的*对齐。
/*
* This is // And so /* Or you can
* okay. // is this. * even do this. */
*/
注释不要封闭在由星号或其它字符绘制的框架里。
提示:当写多行注释时,如果你想让自动代码格式化器在必要时重新包装行,请使用/* ... */ 样式,如果你想让自动代码格式化器在必要时重新包装这些行(段落样式)。大多数格式化器不会在// ... 样式的注释块中重新换行。
类和成员的修饰语,如果存在的话,就按照《Java语言规范》推荐的顺序出现。
public protected private abstract default static final transient volatile synchronized native strictfp
长值整数字使用大写的L后缀,而不是小写的(以避免与数字1混淆)。例如,3000000000L而不是3000000000l。
识别符只使用ASCII字母和数字,在下面提到的少数情况下,还有下划线。因此,每个有效的标识符名称都由正则表达式\w+匹配。
在谷歌风格中,不使用特殊的前缀或后缀。例如,这些名称不是Google Style:name_、mName、s_name和kName。
包的名称都是小写的,连续的单词简单的连接在一起(没有下划线)。例如,com.example.deepspace,而不是com.example.deepSpace或com.example.deep_space。
类的名称用UpperCamelCase书写。
类名通常是名词或名词短语。例如,Character或ImmutableList。
接口名称也可以是名词或名词短语(例如,List),但有时可能是形容词或形容词短语(例如,可读)。
对于注解类型的命名,没有具体的规则,甚至没有完善的惯例。
测试类的命名以它们所测试的类的名称开始,并以Test结束。例如,HashTest或HashIntegrationTest。
方法名称以小写字母(lowerCamelCase)书写。
方法名称通常是动词或动词短语。例如,sendMessage或stop。
JUnit测试方法名称中可以出现下划线,以分隔名称中的逻辑组件,每个组件用小写的CamelCase书写。一个典型的模式是_,例如 pop_emptyStack。没有一个正确的方法来命名测试方法。
常量名称使用CONSTANT_CASE:全部为大写字母,每个字之间用一个下划线分开。但究竟什么是常量?
每个常量都是一个静态final字段,但不是所有静态final字段都是常量。在决定一个字段是否是一个常量时, 考虑它是否真的感觉像是一个常量。例如,如果任何一个该实例的观测状态是可变的,则它几乎肯定不会是一个常量。 只是永远不打算
改变对象一般是不够的,它要真的一直不变才能将它示为常量。
// 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
风格编写。
参数应该避免用单个字符命名。
局部变量名以lowerCamelCase
风格编写,比起其它类型的名称,局部变量名可以有更为宽松的缩写。
虽然缩写更宽松,但还是要避免用单字符进行命名,除了临时变量和循环变量。
即使局部变量是final和不可改变的,也不应该把它示为常量,自然也不能用常量的规则去命名它。
类型变量可用以下两种风格之一进行命名:
驼峰式命名法分大驼峰式命名法(UpperCamelCase
)和小驼峰式命名法(lowerCamelCase
)。 有时,我们有不只一种合理的方式将一个英语词组转换成驼峰形式,如缩略语或不寻常的结构(例如”IPv6”或”iOS”)。Google指定了以下的转换方案。
名字从散文形式
(prose form)开始:
推荐:如果某个单词已经有了常用的驼峰表示形式,按它的组成将它分割开(如”AdWords”将分割成”ad words”)。 需要注意的是”iOS”并不是一个真正的驼峰表示形式,因此该推荐对它并不适用
每个单词的第一个字母都大写,来得到大驼峰式命名。
除了第一个单词,每个单词的第一个字母都大写,来得到小驼峰式命名。
示例:
形式? | 正确 | 错误 |
"XML HTTP request" | XmlHttpRequest | XMLHTTPRequest |
"new customer ID" | newCustomerId | newCustomerID |
"inner stopwatch" | innerStopwatch | innerStopWatch |
"supports IPv6 on iOS?" | supportsIpv6OnIos | supportsIPv6OnIOS |
"YouTube importer" | YouTubeImporter YoutubeImporter* |
*可以接受,但不推荐
注意:有些词在英语中是模糊的连字符:例如 "nonempty "和 "non-empty "都是正确的,所以方法名称checkNonempty和checkNonEmpty也同样都是正确的。
只要一个方法是合法的,就会用@Override注解来标记。这包括覆盖超类方法的类方法、实现接口方法的类方法、以及重新指定超接口方法的接口方法。
例外。当父方法是@Deprecated时,@Override可以被省略。
除了下面提到的情况,很少有对捕获的异常不做任何反应的情况。(典型的反应是记录它,或者如果它被认为是 "不可能的",将它作为AssertionError重新抛出)。
当真正适合在捕获块中不采取任何行动时,将在注释中解释这样做的理由。
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) {
}
使用类名调用静态的类成员,而不是具体某个对象或表达式。
Foo aFoo = ...;
Foo.aStaticMethod(); // good
aFoo.aStaticMethod(); // bad
somethingThatYieldsAFoo().aStaticMethod(); // very bad
覆盖Object.finalize是极为罕见的。
提示:不要这样做。如果你一定要这么做,首先要仔细阅读并理解Effective Java第7项 "避免使用最终处理器",然后不要这么做。
Javadoc块的基本格式如本例所示:
/**
* Multiple lines of Javadoc text are written here,
* wrapped normally...
*/
public int method(String p1) { ... }
或者是以下单行形式:
/** An especially short bit of Javadoc. */
基本形式总是可以接受的。当Javadoc块的全部内容(包括注释标记)可以放在一行中时,可以用单行形式代替。请注意,这只适用于没有诸如@return这样的块标记的情况。
段落之间有一个空行--即只包含对齐的前导星号(*)的行,如果有块状标签组,则在块状标签组之前出现。除第一段外,每个段落的第一个词前都有
,后面没有空格。
任何使用的标准 "块标记 "都是按照@param, @return, @throws, @deprecated的顺序出现的,这四种类型永远不会以空的描述出现。当一个块状标签不适合在一行中出现时,延续行会从@的位置缩进四个(或更多)空格。
每个Javadoc块都以一个简短的摘要片段开始。这个片段非常重要:它是文本中唯一出现在某些上下文中的部分,比如类和方法的索引。
这是一个片段--名词短语或动词短语,不是一个完整的句子。它不是以A {@code Foo} 是一个......开始,或者这个方法返回......,也不是构成一个完整的命令句,如Save the record......。然而,该片段被大写,并使用标点符号,就像它是一个完整的句子一样。
提示:一个常见的错误是把简单的Javadoc写成/** @return the customer ID?/,这是不正确的。它应该写成/* Returns the customer ID. */。
至少,每个公有类,以及公有类中的每个公有或受保护的成员都要有Javadoc,但下面提到的几个例外情况。
额外的Javadoc内容也可以出现,如第7.3.4节 "非必需的Javadoc "所解释。
Javadoc对于像getFoo这样的 "简单、明显 "的方法来说是可选的,在这种情况下,除了 "getFoo",确实没有其他值得说的东西。
重要的是:如果有一些相关信息是需要读者了解的,那么以上的例外不应作为忽视这些信息的理由。例如,对于方法名getCanonicalName, 就不应该忽视文档说明,因为读者很可能不知道词语canonical name指的是什么。
Javadoc并不总是出现在重写超类型方法的方法上。
对于包外不可见的类和方法,如有需要,也是要使用Javadoc的。如果一个注释是用来定义一个类,方法,字段的整体目的或行为, 那么这个注释应该写成Javadoc,这样更统一更友好
其他的类和成员根据需要或需要有Javadoc。
每当一个实现注释被用来定义一个类或成员的整体目的或行为时,该注释被写成Javadoc(使用/**)。
非必要的Javadoc并不严格要求遵循第7.1.2节、7.1.3节和7.2节的格式规则,但是当然推荐使用它。