Translation of Google Java Style Guide,by Lee.

转载请注明出处.This Translation is under CC BY-NC-SA 3.0 CN License.

下载pdf

目录



1. 介绍

返回目录

本文档作为Java编程语言中源代码编码规范的完整定义.当且仅当一个Java源文件遵守这里的规定时,才被称作符合Google Style.

像其他编程规范一样,这个项目涵盖的不仅仅是格式和审美问题,也包括了其他的规则和编码标准.然而,本文档主要专注于我们都遵守的明确的规定,并且避免给出不明确的规则.(无论是人还是工具)

1.1 术语说明

在此文档中,除非特殊说明:

  1. 术语 class 用来表示普通的类,枚举类,接口或注释类型(@interface).

  2. 术语 member(一个类的) 用来表示 一个嵌套类,字段,方法和构造器.这些全都是一个类的顶级内容,除了初始化和注释.

  3. 术语 comment 总是指实施注释(implementation comments).我们不使用文档注释(documentation comments),取而代之的是,术语 Javadoc.

其他术语偶尔会出现在后续文档里.

1.2 指南说明

文档中的示例代码是非规范的.也就是说,虽然这些示例代码是符合Google Style的,但并不意味着这是唯一的一种代码风格.示例中可选的代码格式不应该成为强硬的规定.


2. 源文件

返回目录

2.1 文件名

源文件以其顶层的类名唯一性来命名,是大小写敏感的.以 .java 为扩展名.

2.2 文件编码

源文件的编码格式是 UTF-8.

2.3 特殊字符

2.3.1 空白字符

除了行结束符序列,源文件中的所有空白字符都应该ASCII水平空白字符(0x20).这意味着:

  1. 字符串和字符文字中的所有其他空白字符都将被转义.
  2. 制表符(Tab)不能用来缩进.

2.3.2 特殊转义序列

任何有特殊转义序列(\b, \t, \n, \f, \r, \", \' ,\\)的字符,使用此序列而不是八进制(例如: \012)或Unicode(例如: \u000a)进行转义.

2.3.3 非ASCII字符

对于剩下的非ASCII字符,可以使用实际的Unicode字符(例如: ),也可以使用等价的Unicode转义字符(例如: \u221e).这取决于那种方式更易理解,尽管Unicode转义字符串和注释是不鼓励使用的.

提示:在用转义表示Unicode甚至有时用一些实际的Unicode时,写上注释很有用.

例子 描述
String unitAbbrev = "\u03bcs"; // "μs" 允许,但是没有理由这样做.
String unitAbbrev = "\u03bcs"; // Greek letter mu, "s" 允许,但是容易引起错误
String unitAbbrev = "\u03bcs"; 糟,读者无法理解
String unitAbbrev = "μs"; 赞!十分清楚,即使没有注释
return '\ufeff' + content; // byte order mark 好,对于非打印字符,使用转义.并且写上注释.

提示:不要因为某些程序可能无法正确处理非ASCII字符而使您的代码变得不太可读.如果这发生了,这些程序必须被修复.


3. 源文件格式

返回目录

3.1 许可或版权信息-如果存在

如果有文件内有许可或版权信息,那他只属于这个文件.

3.2 包语句

包语句是不能换行的.列限制(第4.4节,列限制:100)不适用于包语句

3.3 引入语句

3.3.1 不使用通配符引入

不要使用通配符导入,不论静态或其他方式.

3.3.2 不要换行

引入语句是不能换行的.列限制(第4.4节,列限制:100)不适用于引入语句

3.3.3 顺序和间距

导入语句的顺序如下:

  1. 所有静态导入在一个区块内.
  2. 所有非静态导入在一个区块内.

3.3.4 没有静态导入类

静态导入不用于静态嵌套类。他们需要正常导入。

3.4 类申明

3.4.1 唯一的一个顶层类

每个顶级类都位于自己的源文件中。

3.4.2 类成员顺序

类的成员和初始化的顺序对易学性有很大的影响。然而,没有一个正确的方法来做到这一点;不同的类可能会以不同的方式排序其成员。

重要的是每个类都使用一些逻辑顺序,如果被问到,维护者可以解释清楚.比如说,新的方法不应该仅仅被添加到类的末尾,因为这是按时间排序而不是按逻辑排序.

3.4.2.1 重载:不间断

当一个类有很多构造器或同名方法时,他们应该是连续的,不应该被间隔开(即使是私有方法).


4. 格式化

返回目录

4.1 大括号

4.1.1 使用大括号即使可选

if, else, for, do, while 语句中应该使用大括号,即使是空语句或者只有一个语句.

4.1.2 非空块:K&R样式

在非空块和块状结构中,大括号遵守 Kernighan 和 Ritchie 风格(Egyptian brackets):

  1. 左括号前不换行.

  2. 左括号后换行.

  3. 右括号前换行.

  4. 当右括号是一个语句,函数体,构造方法,嵌套类的结尾时,右括号后换行.如果右括号后是 else 或者逗号,则不换行.

例子:

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节,枚举类.

4.1.3 空块:简洁

一个空块状结构或类块状结构可以用K & R 风格(在第4.1.2 节中).或者,大括号可以在打开后马上关闭,在{}之间没有字符或换行符,除非它是多块语句(直接包含多个块:if/elsetry/catch/finally)的一部分.

例子:

  // 这是可接受的
  void doNothing() {}

  // 这也可以接受
  void doNothingElse() {
  }
  //这是不可以的,因为多块语句的大括号不能简化
  try {
    doSomething();
  } catch (Exception e) {}

4.2 块缩进:+2空格

每当一个空块状结构或类块状结构被打开时,缩进就增加2个空格.当块结构结束时,缩进有回到和之前一样.(当然这些IDE都帮你做了.译者注)缩进级别适用于整个块中的代码和注释(参见第4.1.2节中的示例,非空块:K&R样式)

4.3 一行一句

每个语句独占一行.

4.4 列限制:100

Java代码的列限制为100个字符.除了如下所述,任何超过此限制的行都必须换行,如第4.5节,换行中所述.

例外:

  1. 不能遵守列限制的行(比如:Javadoc中一个超长URL或JSNI方法的引用).
  2. packageimport 语句(见第3.2节 包语句 和第3.3节 引入语句 ).
  3. 注释中可以复制到终端执行的命令行.

4.5 换行(Line-wrapping)

术语注解:当可以占用单行的代码被划分成多行时,这被称为换行.

没有一个全面的,确定性的公式,表明在每种情况下如何换行。通常有几种有效的方式来对同一行代码换行.

注意:尽管换行的主要原因是避免超过列限制,但是实际符合列限制的代码也可以由作者自行决定换行。


提示:提取方法或局部变量可以不换行就解决问题.

4.5.1 从哪里断开

自动换行的首要准则是:更倾向于在更高的语法级别处断开.

  1. 如果在一个非赋值运算符处断开,应该在该符号之前断开.(比如 +,他应该在下一行.)这和其他语言的Google Style不同,比如 C++ 和 JavaScript.

    • 这也适用于以下“类运算符”符号
      • 点分隔符 (.)
      • 双冒号方法引用 (::)
      • 类型界限中的和号(&) (<T extends Foo & Bar>)
      • catch块中的管道符号(FooException | BarException e))
  2. 如果在一个赋值运算符处断开,应该在该符号之后断开.(比如 =,它与前面的内容留在同一行.译者注)但是两种方案都是可以的.

    • 这也适用于"类赋值运算符”,比如 foreach 中的 :.
  3. 方法名或构造方法名与左括号留在同一行。

  4. 逗号 ,于前面的内容在同一行.

  5. Lambda表达式的箭头处一般不会换行,除非lambda表达式只包含一个单独的无大括号的语句.例子:

MyLambda<String, Long, Object> lambda =
    (String label, Long value, Object obj) -> {
        ...
    };

Predicate<String> predicate = str ->
    longExpressionInvolving(str);

注意:换行的主要目的是使代码清晰,不一定是最小行数的代码.

4.5.2 换行的语句至少+4空格

换行时,每个延续的行都要至少+4空格的缩进.

当有多个连续的行时,缩进可能不止4个空格.通常,当且仅当它们是语法上的并行元素时,才采用相同的缩进级别.

第4.6.3水平对齐一节中指出,不鼓励使用可变数目的空格来对齐前面行的符号。

4.6 空白

4.6.1 垂直空白

一个空白行出现在:

  1. 在类的连续成员或初始化之间:字段,构造方法,嵌套类,静态初始化块,实例初始化块.

    • 例外:两个连续的字段(它们之间没有其他代码)之间的空白行是可选的.这种情况下,空白行用来对字段进行逻辑分组的.
    • 例外:枚举常数之间的空白行(第4.8.1节 )
  2. 语句之间,根据需要把语句按逻辑分成组.

  3. 可选地,在第一个成员或初始化器之前,或者在该类的最后一个成员或初始化器之后(既不鼓励也不反对).

  4. 根据本文件其他部分的要求(比如第3节,源文件格式, 第3.3节引入语句 )

允许多个连续的空白行,但不鼓励.

4.6.2 水平空白

除了语言和其他风格规则的要求和文字,注释,Javadoc之外,单个的ASCII空格出现在下面这几个地方.

  1. 分离保留字(比如 if,for ,catch)和紧随其后的左括号(().

  2. 分隔保留字与其前面的右大括号(})(如else, catch)。

  3. 在任意的左括号({)前,除了两个例外:

    • @SomeAnnotation({a, b}) 没有使用空格
    • String[][] x = {{"foo"}}; 没有使用空格
  4. 在任意二元或三元操作符两侧.这也适用于"类操作符”:

    • 类型界限中的&: <T extends Foo & Bar>

    • catch块中的管道符号: (FooException | BarException e))

    • foreach语句中的冒号(:).

    • Lambda表达式的箭头: (String str) -> str.length()

    • 但是不适用于:

    • 双冒号(::)的方法应用: Object::toString

    • 句点操作符(.): object.toString()

  5. , : ;及右括号)之后.

  6. 在注释的两个斜杠(//)两侧,这里允许多个空格,但不是必须的.

  7. 在声明的类型和变量名之间: List<String> list

  8. 可选的,在数组初始化的大括号两侧

    • new int[] {5, 6}new int[] { 5, 6 } 都可以.

4.6.3 水平对齐:不作要求

术语解释:*水平对齐(Horizontal alignment )*是在代码中添加可变数量空格的做法,目的是使某些内容与先前行的内容对齐.

这个做法是被允许,但是不作要求.甚至不需要在已经使用的地方保持水平对齐.

例子:

private int x; // 这很好
private Color color; // 这也是

private int   x;      // 允许的, 但以后的编辑
private Color color;  // 可能导致它不再对齐.

提示:对齐可增加代码的可读性,但也造成以后维护的麻烦.考虑以后,我们需要修改一堆对齐代码中的一行.这种变化可能会使代码不再对齐,但这是允许的.很多时候,这会促使你去调整附近行的空格,可能会触发一连串的修改.这一行代码的改变造成了连环反应.这最坏时将会导致无用功,但是它至少也造成了历史版本的破坏,减慢了审查人员的速度,并增加了合并冲突的可能。

4.7 括号分组:推荐

除非作者和审查员都认为去掉小括号也不会使代码被误解,或是去掉小括号能让代码更易于阅读,否则我们不应该去掉小括号.我们没有理由认为每位读者都知道Java运算符的优先级.

4.8 具体构造

4.8.1 枚举类

在每个逗号后,紧跟这一个枚举常量,也可以选择换行.额外的空行(通常只有一个)也是允许的。这是一种可能:


private enum Answer {
  YES {
    @Override public String toString() {
      return "yes";
    }
  },

  NO,
  MAYBE
}

一个没有方法和文档的枚举类,可以选择像声明数组一样的格式(详见:第4.8.3.1节 数组初始化 ):

private enum Suit { CLUBS, HEARTS, SPADES, DIAMONDS }

枚举类也是类,因此适用于类的所有其他规则都适用.

4.8.2 变量声明

4.8.2.1 每次只声明一个变量

每次只声明一个变量,像 int a, b; 这样是不允许的.

4.8.2.2 按需声明

不要习惯性地把局部变量一次性在代码块的开头声明,而是在第一次用到它的时候再声明,最小化它们的作用范围.局部变量声明通常要初始化,或在声明后立即初始化.

4.8.3 数组

4.8.3.1 数组可以"块状"初始化

任何数组都可以块状结构一样初始化.比如下面的例子都是合法的(不是详尽的列表):

new int[] {           new int[] {
  0, 1, 2, 3            0,
}                       1,
                        2,
new int[] {             3,
  0, 1,               }
  2, 3
}                     new int[]
                          {0, 1, 2, 3}

4.8.3.2 非C风格的数组声明

方括号是类型的一部分,而不是变量名.采用 String[] args 而不是 String args[].

4.8.4 Switch语句

术语解释:switch大括号内是一个或多个语句组.每个语句组包含一个或多个的switch 标签(casedefault),跟这一个或多个语句(如果是最后一个语句组,则是零个或多个).

4.8.4.1 缩进

与任何其他代码块一样,switch块内的缩进+2.

在switch标签后需要换行,并且再+2的缩进.下一个标签返回之前的缩进级别.

4.8.4.2 注释Fall-through

在switch块中,每个语句组那么通过 break, continue,return或者抛出异常来结束语句,要么写明注释来表明它会进入下一个语句组.任何表达这个意思的注释都可以.(一般用 // fall through )这个特殊的注释在最后一个语句组时不需要.比如:

switch (input) {
  case 1:
  case 2:
    prepareOneOrTwo();
    // fall through
  case 3:
    handleOneTwoOrThree();
    break;
  default:
    handleLargeNumber(input);
}

4.8.4.3 default 要写出

每个switch语句都要包含 default 语句组,即使它是空的.

例外:一个对枚举常量的Switch语句,如果写出了所有的情况,则可以省略 default语句组.

4.8.5 注解(Annotations)

用于类,方法和构造方法的注解,应该立即出现在文档后面,并且每个注解占一行,这不属于换行( line-wrapping )所以缩进不增加.

@Override
@Nullable
public String getNameIfPresent() { ... }

例外:单个无参的注解可以和语句同行,比如:

@Override public int hashCode() { ... }

对于字段的注解也要立即出现在文档之后,但是这种情况下,多个注释可以在同一行.比如:

@Partial @Mock DataLoader loader;

参数,局部变量和类型的注解没有特定规则。

4.8.6 注释(Comment)

这节讲implementation comments,Javadoc在第7节Javadoc标记单独讲.

4.8.6.1 块状注释风格

注释块的缩进和周围的代码保持一致.可以写成 /*...*///....多行注释中,子行必须以*开头和前一行对齐.

/*
 * This is          // And so           /* Or you can
 * okay.            // is this.          * even do this. */
 */

注释不包含在带有星号或其他字符的框框中.

提示: 如果你希望自动代码格式化程序在必要时换行,则编写多行注释时,请使用/*...*/. 大多数格式化程序不会在//样式的注释块中重新换行

4.8.7 修饰符

类和成员的修饰符,以下面推荐的顺序出现:

public protected private abstract default static final transient volatile synchronized native strictfp

4.8.8 数字

long类型的整型数字,使用大写L后缀(不要以小写l后缀,为了不和数字1混淆).比如:使用 3000000000L 而不是 3000000000l.


5. 命名规则

返回目录

5.1 对所有标识符的基本规则

标识符只包含ASCII中的字母和数字,并且在下面列举的少数情况下,使用下划线.所以每个合法的标识符多可以被 \w+ 正则匹配.

在Google Style中,前缀和后缀是从不使用的,像这些:name_, mName, s_name, kName.

5.2 各种标识符的命名

5.2.1 包名的命名

包名全部是小写的,可以是几个单词连接起来(不包括下划线).比如:使用com.example.deepspace而不是com.example.deepSpacecom.example.deep_space.

5.2.2 类名的命名

类名都以驼峰命名法命名.

类名通常是名词或名词短语.比如Character或者ImmutableList接口名也是名词或名词短语.(比如:List),但是有时候可以是形容词或形容词短语(比如:Readable).

注解类型没有特定的命名规则,甚至没有完善的约定.

Test类以他们需要测试的类命名,并加上Test.例子:HashTestHashIntegrationTest.

5.2.3 方法的命名

方法名也都以驼峰命名法命名.

方法名一般可以是动词或动词短语.比如:sendMessagestop.

下划线可能出现在JUnit的测试方法中用以分隔名称的逻辑组件.一个典型的格式是:test<MethodUnderTest>_<state>,比如,testPop_emptyStack.没有准确的方法去命名测试类.

5.2.4 常量的命名

常量的命名可以使用 CONSTANT_CASE 规则:全部大写,包含下划线.但是讲真的,什么是常量?

常量是以 static final 修饰的,并且其内容永远不变,其方法没有可察觉的副作用.这包括 primitives, String, 不变的类型和不可变类型的不可变集合.如果任意的可观察的实例发生变化,那么它就不是常量.仅仅表现为改变的对象是远远不够的,比如:

// 常量
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 }

// 不是常量
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"};

这些常量名一般都是名词或名词短语.

5.2.5 非常量字段的命名

非常量字段(不论是static的或其他的)是以驼峰命名法命名的.

这些名称一般都是名词或名词短语.比如 computedValuesindex.

5.2.6 参数的命名

参数名也都以驼峰命名法命名.

应避免在公共方法中使用一个字符的参数名称.

5.2.7 局部变量的命名

局部变量名以驼峰命名法命名.

即使用 finalstatic修饰,局部变量也不会被看做是常量,也不应该以常量的方式来命名.

5.2.8 类型变量的命名

类型变量可用以下两种风格之一进行命名:

  • 单个的大写字母,后面可以跟一个数字(如:E,T,X,T2).
  • 以类命名方式(详见5.2.2节 ),后面加个大写的T(如:RequestT,FooBarT).

5.3 驼峰命名法

有时会有几种不同"驼峰化"的方案,例如缩写或不寻常的单词像IPv6iOS.为了更准确的结果,Google Style作了以下(接近)精确的方案:

步骤:

  1. 把短语转换成只含ASCII码,并去掉'.比如,"Müller's algorithm",应该变成 "Mullers algorithm"

  2. 把得到的结果以其中的空格和任意标点符号(一般是连字符)进行分割.

    • 建议:如果任何一个词已经有了骆驼状外观,则把他分割成几个部分(比如,"AdWords" 变成 ad words).请注意,诸如“iOS”这样的词不是真的骆驼式;它违反任何惯例,所以这个建议不适用.
  3. 现在把每个词都小写(包括缩写),然后,大写第一个字.

  4. 最后,把每个单词组合起来.

例子:

名称 正确 错误
“XML HTTP request” XmlHttpRequest XMLHTTPRequest
“YouTube importer” YouTubeImporter YoutubeImporter*
“inner stopwatch” innerStopwatch innerStopWatch
“new customer ID” newCustomerId newCustomerID
“supports IPv6 on iOS?” supportIpv6OnIos supportsIPv6OnIOS

*允许的,但不推荐.

注意:英语中有些词既可以带连字符又可以不带.比如,nonemptynon-empty是一样的.所以checkNonemptycheckNonEmpty的命名方式都是对的.


6. 编程实践

返回目录

6.1 @Override:总是使用

只要是合法的,都要使用Override.这包括了一个类方法重载父类方法,一个类方法实现接口的方法,一个接口方法重写父接口的方法.

例外: @Override可以在它的父方法被@Deprecated标记时省略.

6.2 捕捉的异常:不要忽视

除了下面的例外以外,对一个被捕捉的异常什么也不做是不对的.(一般的做法是去打印异常,),如果被认为是impossible,则重新抛出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) {
}

6.3 静态成员:使用类进行调用

使用类名调用静态方法,而不是具体对象或表达式.

Foo aFoo = ...;
Foo.aStaticMethod(); // 好
aFoo.aStaticMethod(); // 糟糕
somethingThatYieldsAFoo().aStaticMethod(); // 垃圾

6.4 Finalizers:禁用

真的非常少有人回去重载Object.finalize.

提示:不要这样做.如果你铁了心要去做,请先仔细阅读并理解Effective Java Item 7中的"Avoid Finalizers”.然后你会放弃这个想法的.XD


7. Javadoc

返回目录

7.1 格式

7.1.1 一般格式

通常的Javadoc格式就像例子中的那样:

/**
 * Multiple lines of Javadoc text are written here,
 * wrapped normally...
 */
public int method(String p1) { ... }

或者单行的Javadoc:

/** An especially short bit of Javadoc. */

基本格式总是可行的.在Javadoc的内容(包括评论标记)可以包含在一行之内时,可以使用单行的Javadoc.注意这只能在没有块标记(例如,@return)时使用.

7.1.2 段落

一个空行(也就是除了行最左边的*外没有内容的),应该的段落之间,块标记(如果有的话)之前出现.每个段落,第一个但此前要有 <p>,并且之后没有空格.

7.1.3 块标记

任何标准的"块标记"都有以@param,@return,@throws,@deprecated 的顺序出现,并且这四个标签(如果出现)都要有内容.当描述无法在一行中容纳,连续行需要至少再缩进4个空格(从@开始).

7.2 摘要片段

每个Javadoc块都应以一个简短的摘要片段开始.这个片段十分重要,在某些情况下,比如类或方法索引中,它是唯一出现的文本.

片段是名词或动词短语,而不是一个完整的句子.它不是以A {@code Foo} is a...This method returns...开头的,也不是一个像Save the record.这样完整的祈使句.然而,由于开头大写及被加了标点,它看起来像是个完整的句子.

提示:一个普遍的错误例子/** @return the customer ID */.它应该这样写/** Returns the customer ID. */.

7.3 在哪里使用Javadoc

至少,每个public的类及它的每个publicprotected的成员都有写Javadoc.以下是几个例外.

额外的Javadoc也是可以的,详见第7.3.4节可选的Javadoc.

7.3.1 例外:不言自明的方法

Javadoc对于简单明白的方法可以省略,比如,getFoo,因为这真的真的没有什么值得说的,就是"返回foo”.

重要提示:如果有一些相关信息是需要读者了解的,那么以上的例外不应作为忽视这些信息的理由.例如,对于方法名getCanonicalName,就不要省略它的文档,基本上只要写上/** Returns the canonical name. */.因为一个普通的读者可能不知道"canonical name"是什么.

7.3.2 例外:重载

Javadoc在重载父方法时可以省略.

7.3.4 可选的Javadoc

其他类和成员根据需要或你的意愿写上Javadoc.

每当一个implementation comment用来描述类或类成员的目的或行为时,该注释要用Javadoc(用/**取代).

可选的Javadoc对以下的格式要求不严格(第7.1.2节,第7.1.3节和第7.2节),当然建议你遵守.