Google Java 编码规范

1 简介

本文档完整地定义了 Google Java™ 编程语言源代码的编码标准。如果 Java 源代码文件遵守本文档中的规则,则可称为符合 Google 风格

与其他编程风格指南一样,本文档涵盖的问题不仅包括格式的美观问题,还包括其他类型的约定或编码标准。 但是,本文档主要侧重于我们普遍遵循的硬性规定,并避免提供无法明确执行的建议(无论是通过人工还是工具)。

1.1 术语说明

除非另有说明,否则在本文档中:

  1. 术语被包含地用于表示“普通”类、记录类、枚举类、接口或注解类型 (@interface)。
  2. 术语成员(类的成员)被包含地用于表示嵌套类、字段、方法或构造函数;也就是说,类中除初始化器和注释之外的所有顶级内容。
  3. 术语注释始终指的是实现注释。 我们不使用短语“文档注释”,而是使用通用术语“Javadoc”。

其他“术语说明”将偶尔出现在文档中。

1.2 指南说明

本文档中的示例代码是非规范性的。也就是说,虽然这些示例符合 Google 风格,但它们可能没有说明表示代码的唯一时尚方式。 示例中做出的可选格式选择不应强制作为规则执行。

2 源文件基础

2.1 文件名

对于包含类的源文件,文件名由顶级类(只有一个)的大小写敏感的名称加上 .java 扩展名组成。

2.2 文件编码:UTF-8

源文件以 UTF-8 编码。

2.3 特殊字符

2.3.1 空格字符

除了行终止符序列之外,ASCII 水平空格字符0x20)是唯一出现在源文件中的空格字符。 这意味着

  1. 字符串和字符文字中的所有其他空格字符都必须进行转义。
  2. 制表符用于缩进。

2.3.2 特殊转义序列

对于任何具有特殊转义序列\b\t\n\f\r\s\"\'\\)的字符,都应使用该序列,而不是相应的八进制(例如 \012)或 Unicode(例如 \u000a)转义。

2.3.3 非 ASCII 字符

对于其余的非 ASCII 字符,可以使用实际的 Unicode 字符(例如 )或等效的 Unicode 转义符(例如 \u221e)。 选择仅取决于哪个使代码更易于阅读和理解,尽管强烈建议不要在字符串文字和注释之外使用 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 字符而降低代码的可读性。如果发生这种情况,那些程序就是坏的,必须修复

3 源文件结构

一个普通的源文件按顺序包括:

  1. 许可证或版权信息(如果存在)
  2. 包声明
  3. 导入语句
  4. 恰好一个顶级类

恰好一个空行分隔每个存在的部分。

package-info.java 文件是相同的,但没有顶级类。

module-info.java 文件不包含包声明,而是用模块声明替换单个顶级类,但其余结构相同。

如果许可证或版权信息属于文件,则它应该放在这里。

3.2 包声明

包声明不换行。 列限制(第 4.4 节,列限制:100)不适用于包声明。

3.3 导入语句

3.3.1 没有通配符导入

不使用通配符导入,无论是静态的还是非静态的。

3.3.2 没有换行

导入语句不换行。 列限制(第 4.4 节,列限制:100)不适用于导入语句。

3.3.3 排序和空格

导入按以下顺序排列

  1. 所有静态导入在一个块中。
  2. 所有非静态导入在一个块中。

如果既有静态导入又有非静态导入,则一个空行分隔这两个块。 导入语句之间没有其他空行。

在每个块中,导入的名称按 ASCII 排序顺序显示。(注意:这与按 ASCII 排序顺序排列的导入语句不同,因为 '.' 在 ';' 之前排序。)

3.3.4 没有类的静态导入

静态导入不用于静态嵌套类。 它们使用普通导入导入。

3.4 类声明

3.4.1 恰好一个顶级类声明

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

3.4.2 类内容的排序

您为类的成员和初始化器选择的顺序会对可学习性产生重大影响。 但是,没有一个正确的方法可以做到这一点; 不同的类可能以不同的方式对其内容进行排序。

重要的是每个类都使用某种逻辑顺序,如果被问及,其维护者可以解释。 例如,新方法不仅仅习惯性地添加到类的末尾,因为这将产生“按添加日期时间顺序”排序,这不是一种逻辑排序。

3.4.2.1 重载:永不分割

共享相同名称的类的方法出现在一个连续的组中,中间没有其他成员。 这同样适用于多个构造函数(它们始终具有相同的名称)。 即使诸如 staticprivate 之类的修饰符在方法之间有所不同,此规则也适用。

3.5 模块声明

3.5.1 模块指令的排序和间距

模块指令的顺序如下

  1. 所有 requires 指令在一个块中。
  2. 所有 exports 指令在一个块中。
  3. 所有 opens 指令在一个块中。
  4. 所有 uses 指令在一个块中。
  5. 所有 provides 指令在一个块中。

每个存在的块之间用一个空行分隔。

4 格式化

术语说明:类块结构是指类、方法或构造函数的主体。请注意,根据关于数组初始化器的第 4.8.3.1 节,任何数组初始化器可以选择性地被视为类块结构。

4.1 大括号

4.1.1 可选大括号的使用

大括号与 ifelsefordowhile 语句一起使用,即使主体为空或仅包含单个语句。

其他可选的大括号,例如 lambda 表达式中的大括号,仍然是可选的。

4.1.2 非空块:K & R 风格

对于非空块和类块结构,大括号遵循 Kernighan 和 Ritchie 风格(“埃及括号”)

例外:在这些规则允许以分号 (;) 结尾的单个语句的地方,可以出现语句块,并且该块的左大括号之前有一个换行符。 引入这样的块通常是为了限制局部变量的作用域。

示例

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();
    }
    {
      int x = foo();
      frob(x);
    }
  }
};

枚举类的一些例外情况在第 4.8.1 节枚举类中给出。

4.1.3 空块:可以简洁

空块或类块结构可以是 K & R 风格(如第 4.1.2 节中所述)。 另外,可以在打开后立即关闭它,中间没有字符或换行符 ({}),除非它是多块语句的一部分(直接包含多个块的语句:if/elsetry/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) {}

4.2 块缩进:+2 个空格

每次打开新的块或类块结构时,缩进都会增加两个空格。 当块结束时,缩进返回到之前的缩进级别。 缩进级别适用于整个块中的代码和注释。(参见第 4.1.2 节非空块:K & R 风格中的示例。)

4.3 每行一个语句

每个语句后跟一个换行符。

4.4 列限制:100

Java 代码的列限制为 100 个字符。“字符”是指任何 Unicode 代码点。 除非如下所述,否则任何超出此限制的行都必须换行,如第 4.5 节换行中所述。

每个 Unicode 代码点算作一个字符,即使其显示宽度更大或更小。 例如,如果使用全角字符,您可以选择比此规则严格要求的更早地换行。

例外

  1. 无法遵守列限制的行(例如,Javadoc 中的长 URL 或长的 JSNI 方法引用)。
  2. packageimport 语句(参见第 3.2 节包声明和第 3.3 节导入语句)。
  3. 文本块的内容。
  4. 注释中的命令行,可以复制并粘贴到 shell 中。
  5. 非常长的标识符在极少数情况下需要使用时,允许超出列限制。在这种情况下,周围代码的有效换行应由 google-java-format 生成。

4.5 换行

术语说明:当原本可以合法地占用单行的代码被分成多行时,此活动称为换行

没有全面、确定性的公式可以精确地展示在每种情况下如何进行换行。通常,对于同一段代码,存在几种有效的换行方式。

注意: 虽然换行的典型原因是避免超出列限制,但即使实际上可以容纳在列限制内的代码也可能由作者自行决定进行换行。

提示: 提取方法或局部变量可能解决该问题,而无需进行换行。

4.5.1 在何处断开

换行的首要原则是:优先在更高的语法级别进行断开。 此外

  1. 当在一行中在非赋值运算符处断开时,断开应位于符号之前。(请注意,这与 Google 对 C++ 和 JavaScript 等其他语言的风格不同。)
    • 这也适用于以下“类似运算符”的符号
      • 点分隔符 (.)
      • 方法引用的两个冒号 (::)
      • 类型绑定中的 & 符号 (<T extends Foo & Bar>)
      • catch 块中的管道符号 (catch (FooException | BarException e))。
  2. 当在一行中在赋值运算符处断开时,断开通常位于符号之后,但两种方式都可以接受。
    • 这也适用于增强型 for(“foreach”)语句中类似“赋值运算符”的冒号。
  3. 方法、构造函数或记录类名称应与后面的左括号 (() 保持连接。
  4. 逗号 (,) 应与它前面的标记保持连接。
  5. 一行永远不会在 lambda 或 switch 规则中的箭头附近断开,除非在箭头之后紧跟的文本由单个无括号表达式组成。 例子
    MyLambda<String, Long, Object> lambda =
        (String label, Long value, Object obj) -> {
          ...
        };
    
    Predicate<String> predicate = str ->
        longExpressionInvolving(str);
    
    switch (x) {
      case ColorPoint(Color color, Point(int x, int y)) ->
          handleColorPoint(color, x, y);
      ...
    }
    

注意: 换行的主要目标是使代码清晰,不一定是要使代码占用最少的行数。

4.5.2 续行缩进至少 +4 个空格

换行时,第一行之后的每一行(每个续行)应从原始行缩进至少 +4 个空格。

当存在多个续行时,缩进可以根据需要改变超过 +4 个空格。通常,当且仅当两个续行以语法上平行的元素开头时,它们才使用相同的缩进级别。

关于水平对齐的 4.6.3 节讨论了不鼓励使用的做法,即使用可变数量的空格来将某些标记与前几行对齐。

4.6 空格

4.6.1 垂直空格

始终显示单个空行

  1. 在类的连续成员或初始化程序之间:字段、构造函数、方法、嵌套类、静态初始化程序和实例初始化程序。
    • 例外:两个连续字段之间(它们之间没有其他代码)的空行是可选的。 根据需要使用此类空行来创建字段的逻辑分组
    • 例外:枚举常量之间的空行在第 4.8.1 节中介绍。
  2. 根据本文档的其他部分的要求(例如第 3 节源文件结构和第 3.3 节导入语句)。

单个空行也可以出现在任何可以提高可读性的地方,例如在语句之间以将代码组织成逻辑小节。 不鼓励也不反对在类的第一个成员或初始化程序之前或最后一个成员或初始化程序之后使用空行。

允许使用多个连续的空行,但从不需要(也不鼓励)。

4.6.2 水平空格

除了语言或其他样式规则要求的位置之外,并且除了字面量、注释和 Javadoc 之外,单个 ASCII 空格也出现在以下位置。

  1. 将任何保留字(如 ifforcatch)与该行上的左括号 (() 分隔开
  2. 将任何保留字(如 elsecatch)与该行上的右花括号 (}) 分隔开
  3. 在任何左花括号 ({) 之前,但有两个例外
    • @SomeAnnotation({a, b})(不使用空格)
    • String[][] x = {{"foo"}};(根据下面的第 9 项,{{ 之间不需要空格)
  4. 在任何二元或三元运算符的两侧。 这也适用于以下“类似运算符”的符号
    • 连接类型绑定中的 & 符号:<T extends Foo & Bar>
    • 用于处理多个异常的 catch 块的管道符号:catch (FooException | BarException e)
    • 增强型 for(“foreach”)语句中的冒号 (:)
    • lambda 表达式中的箭头:(String str) -> str.length()
      或 switch 规则:case "FOO" -> bar();
    但不包括
    • 方法引用的两个冒号 (::),其书写方式如 Object::toString
    • 点分隔符 (.),其书写方式如 object.toString()
  5. ,:; 或强制转换的右括号 ()) 之后
  6. 在任何内容和开始注释的双斜杠 (//) 之间。 允许使用多个空格。
  7. 在开始注释的双斜杠 (//) 和注释的文本之间。 允许使用多个空格。
  8. 在声明的类型和变量之间:List<String> list
  9. 在数组初始化程序的两个花括号内可选
    • new int[] {5, 6}new int[] { 5, 6 } 都是有效的
  10. 在类型注释和 []... 之间。

此规则永远不会被解释为要求或禁止在一行的开头或结尾添加额外的空格; 它仅处理内部空格。

4.6.3 水平对齐:从不需要

术语说明:水平对齐是指在代码中添加可变数量的额外空格,以使某些标记直接出现在前面几行中某些其他标记下方的做法。

Google Style 允许这样做,但从不需要。 甚至不需要维持已经使用水平对齐的位置。

这是一个没有对齐的示例,然后是使用对齐的示例

private int x; // this is fine
private Color color; // this too

private int   x;      // permitted, but future edits
private Color color;  // may leave it unaligned

提示:对齐可以帮助提高可读性,但为了对齐而试图保持对齐会带来未来的问题。 例如,考虑一个仅触及一行的更改。 如果该更改破坏了以前的对齐方式,则不要仅仅为了重新对齐而对附近的其他行引入额外的更改。 在原本不受影响的行上引入格式更改会破坏版本历史记录、减慢审阅者的速度并加剧合并冲突。 这些实际问题优先于对齐。

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; 之类的声明。

例外:for 循环的标头中,可以接受多个变量声明。

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 语句和表达式

由于历史原因,Java 语言对于 switch 具有两种不同的语法,我们可以将其称为旧样式新样式。 新样式 switch 在 switch 标签后使用箭头 (->),而旧样式 switch 使用冒号 (:)。

术语说明:switch 块的花括号内,可以是一个或多个 switch 规则(新样式); 或一个或多个语句组(旧样式)。 switch 规则switch 标签case ...default)后跟 -> 和表达式、块或 throw 组成。 语句组由一个或多个 switch 标签组成,每个标签后跟一个冒号,然后是一个或多个语句,或者,对于最后一个语句组,零个或多个语句。(这些定义与 Java 语言规范 §14.11 匹配。)

4.8.4.1 缩进

与其他任何块一样,switch 块的内容缩进 +2。每个 switch 标签都以这 +2 的缩进开始。

在新式 switch 中,如果 switch 规则遵循 Google 风格,则可以将其写在单行上。(它不得超过列限制,如果它包含一个非空块,则在 { 之后必须有一个换行符。)第 4.5 节的换行规则适用,包括延续行的 +4 缩进。对于箭头后跟非空块的 switch 规则,与在其他地方的块相同的规则适用:{} 之间的行相对于带有 switch 标签的行进一步缩进 +2。

switch (number) {
  case 0, 1 -> handleZeroOrOne();
  case 2 ->
      handleTwoWithAnExtremelyLongMethodCallThatWouldNotFitOnTheSameLine();
  default -> {
    logger.atInfo().log("Surprising number %s", number);
    handleSurprisingNumber(number);
  }
}

在旧式 switch 中,每个 switch 标签的冒号后跟一个换行符。语句组中的语句以进一步的 +2 缩进开始。

4.8.4.2 Fall-through(向下执行):注释

在旧式 switch 块中,每个语句组要么突然终止(使用 breakcontinuereturn 或抛出的异常),要么用注释标记以指示执行将或 *可能* 继续到下一个语句组。任何传达向下执行概念的注释都足够(通常为 // fall through)。此特殊注释在 switch 块的最后一个语句组中不是必需的。例如

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

请注意,case 1: 之后不需要注释,只需在语句组的末尾添加注释。

新式 switch 中不存在向下执行。

4.8.4.3 穷举性和 default 标签的存在

Java 语言要求 switch 表达式和许多类型的 switch 语句是 *穷举性的*。这实际上意味着可以切换的每个可能值都将与其中一个 switch 标签匹配。如果 switch 具有 default 标签,那么它就是穷举性的,例如,如果被切换的值是一个枚举,并且枚举的每个值都与一个 switch 标签匹配。Google Style 要求 *每个* switch 都是穷举性的,即使语言本身没有要求它。这可能需要添加一个 default 标签,即使它不包含任何代码。

4.8.4.4 Switch 表达式

Switch 表达式必须是新式 switch

  return switch (list.size()) {
    case 0 -> "";
    case 1 -> list.getFirst();
    default -> String.join(", ", list);
  };

4.8.5 注解

4.8.5.1 类型使用注解

类型使用注解出现在带注解的类型之前。如果注解使用 @Target(ElementType.TYPE_USE) 进行元注解,则该注解为类型使用注解。例如

final @Nullable String name;

public @Nullable Person getPersonByName(String name);
4.8.5.2 类、包和模块注解

应用于类、包或模块声明的注解紧跟在文档块之后出现,并且每个注解都单独列在一行上(即,每行一个注解)。这些换行符不构成换行(第 4.5 节,换行),因此缩进级别不会增加。例如

/** This is a class. */
@Deprecated
@CheckReturnValue
public final class Frozzler { ... }
/** This is a package. */
@Deprecated
@CheckReturnValue
package com.example.frozzler;
/** This is a module. */
@Deprecated
@SuppressWarnings("CheckReturnValue")
module com.example.frozzler { ... }
4.8.5.3 方法和构造函数注解

方法和构造函数声明的注解规则与上一节相同。例如

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

例外: 一个 *单个* 无参数注解 *可能* 与签名的第一行一起出现,例如

@Override public int hashCode() { ... }
4.8.5.4 字段注解

应用于字段的注解也紧跟在文档块之后出现,但在这种情况下,*多个* 注解(可能带有参数)可以列在同一行上;例如

@Partial @Mock DataLoader loader;
4.8.5.5 参数和局部变量注解

对于参数或局部变量上的注解没有具体格式规则(当然,除非该注解是类型使用注解)。

4.8.6 注释

本节讨论的是 *实现注释*。 Javadoc 在第 7 节 Javadoc 中单独讨论。

任何换行符之前都可以有任意空格,后跟实现注释。这样的注释会使该行变为非空行。

4.8.6.1 块注释样式

块注释的缩进级别与周围代码相同。它们可以是 /* ... */ 样式或 // ... 样式。对于多行 /* ... */ 注释,后续行必须以 * 开头,并且与上一行的 * 对齐。

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

注释不包含在用星号或其他字符绘制的框中。

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

4.8.6.2 TODO 注释

对于临时代码、短期解决方案或足够好但不够完美的代码,请使用 TODO 注释。

TODO 注释以大写单词 TODO 开头,后跟一个冒号,以及指向包含上下文的资源的链接,最好是一个 bug 引用。 bug 引用是首选,因为 bug 会被跟踪并且具有后续注释。在此上下文之后,添加一个以连字符 - 开头的解释性字符串。

目的是拥有一个一致的 TODO 格式,可以搜索该格式以找出如何获取更多详细信息。

// TODO: crbug.com/12345678 - Remove this after the 2047q4 compatibility window expires.

避免添加引用个人或团队作为上下文的 TODO

// TODO: @yourusername - File an issue and use a '*' for repetition.

如果您的 TODO 采用 “在未来某个日期做某事” 的形式,请确保您要么包含一个非常具体的日期(“在 2005 年 11 月之前修复”),要么包含一个非常具体的事件(“当所有客户端都可以处理 XML 响应时删除此代码。”)。

4.8.7 修饰符

类和成员修饰符(如果存在)以 Java 语言规范建议的顺序出现

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

模块指令 requires 上的修饰符(如果存在)按以下顺序出现

transitive static

4.8.8 数字字面量

long 值的整数文字使用大写 L 后缀,绝不使用小写(以避免与数字 1 混淆)。例如,3000000000L 而不是 3000000000l

4.8.9 文本块

文本块的起始 """ 始终位于新行上。该行可以遵循与其他构造相同的缩进规则,也可以根本没有缩进(因此它从左边距开始)。结尾 """ 位于与起始 """ 具有相同缩进的新行上,并且可以在同一行上后跟更多代码。文本块中每行的文本至少缩进到起始和结尾 """ 的程度。(如果某行缩进得更远,则文本块定义的字符串文字将在该行的开头留有空格。)

文本块的内容可能超过 列限制

5 命名

5.1 所有标识符的通用规则

标识符仅使用 ASCII 字母和数字,并且在下面提到的一小部分情况下使用下划线。因此,每个有效的标识符名称都与正则表达式 \w+ 匹配。

在 Google Style 中, 使用特殊前缀或后缀。例如,以下名称不是 Google Style:name_mNames_namekName

5.2 按标识符类型划分的规则

5.2.1 包和模块名称

包和模块名称仅使用小写字母和数字(没有下划线)。连续的单词只是简单地连接在一起。例如,com.example.deepspace,而不是 com.example.deepSpacecom.example.deep_space

5.2.2 类名

类名以 UpperCamelCase 编写。

类名通常是名词或名词短语。例如,CharacterImmutableList。接口名称也可以是名词或名词短语(例如,List),但有时也可以是形容词或形容词短语(例如,Readable)。

对于命名注解类型没有具体的规则,甚至没有既定的约定。

测试 类的名称以 Test 结尾,例如,HashIntegrationTest。如果它涵盖单个类,则其名称是该类的名称加上 Test,例如 HashImplTest

5.2.3 方法名

方法名以 lowerCamelCase 编写。

方法名通常是动词或动词短语。例如,sendMessagestop

下划线可以出现在 JUnit *测试* 方法名称中,以分隔名称的逻辑组件,*每个* 组件都以 lowerCamelCase 编写,例如 transferMoney_deductsFromSource。没有命名测试方法的唯一正确方法。

5.2.4 常量名

常量名称使用 UPPER_SNAKE_CASE:所有大写字母,每个单词之间用单个下划线分隔。但到底什么是常量呢?

常量是静态 final 字段,其内容是深度不可变的,并且其方法没有可检测到的副作用。示例包括原始类型、字符串、不可变值类以及设置为 null 的任何内容。如果实例的任何可观察状态都可以更改,则它不是常量。仅仅 *打算* 永远不改变对象是不够的。例如

// Constants
static final int NUMBER = 5;
static final ImmutableList<String> NAMES = ImmutableList.of("Ed", "Ann");
static final Map<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 = {};

// 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"};

这些名称通常是名词或名词短语。

5.2.5 非常量字段名

非常量字段名称(静态或其他)以 lowerCamelCase 编写。

这些名称通常是名词或名词短语。例如,computedValuesindex

5.2.6 参数名

参数名称以 lowerCamelCase 编写。

应避免在公共方法中使用单字符参数名称。

5.2.7 局部变量名

局部变量名以 lowerCamelCase 编写。

即使是 final 和不可变的,局部变量也不被视为常量,并且不应将其样式设置为常量。

5.2.8 类型变量名

每个类型变量都以以下两种样式之一命名

5.3 驼峰式命名:已定义

有时,将英语短语转换为驼峰式命名法有多种合理的方法,例如当存在首字母缩略词或诸如 “IPv6” 或 “iOS” 之类的非常规结构时。为了提高可预测性,Google Style 指定了以下(几乎)确定性的方案。

从名称的散文形式开始

  1. 将短语转换为纯 ASCII 并删除任何撇号。例如,“Müller's algorithm” 可能会变为 “Muellers algorithm”。
  2. 将结果分割成单词,以空格和任何剩余的标点符号(通常是连字符)作为分隔符。
    • 建议: 如果任何单词在常用语中已经具有传统的驼峰式命名外观,请将其拆分为组成部分(例如,“AdWords”变为“ad words”)。请注意,“iOS”之类的词实际上本身不是驼峰式命名;它违背了任何约定,因此此建议不适用。
  3. 现在将所有内容(包括首字母缩略词)转换为小写,然后仅将第一个字符转换为大写
    • ... 每个单词,以产生大驼峰式命名,或者
    • ... 除了第一个单词之外的每个单词,以产生小驼峰式命名
  4. 最后,将所有单词连接成一个标识符。请注意,原始单词的大小写几乎完全被忽略。

在极少数情况下(例如,多部分版本号),您可能需要使用下划线来分隔相邻的数字,因为数字没有大小写变体。

示例

散文形式 正确 不正确
"XML HTTP request" XmlHttpRequest XMLHTTPRequest
"new customer ID" newCustomerId newCustomerID
"inner stopwatch" innerStopwatch innerStopWatch
"supports IPv6 on iOS?" supportsIpv6OnIos supportsIPv6OnIOS
"YouTube importer" YouTubeImporter
YoutubeImporter*
"Turn on 2SV" turnOn2sv turnOn2Sv
"Guava 33.4.6" guava33_4_6 guava3346

*可以接受,但不推荐。

注意: 一些单词在英语中含糊不清地使用连字符:例如,“nonempty”和“non-empty”都是正确的,因此方法名称 checkNonemptycheckNonEmpty 也都是正确的。

6 编程实践

6.1 @Override: 始终使用

当方法合法时,使用 @Override 注释标记该方法。 这包括覆盖超类方法的类方法、实现接口方法的类方法、重新指定超接口方法的接口方法以及记录组件的显式声明的访问器方法。

例外: 当父方法是 @Deprecated 时,可以省略 @Override

6.2 捕获的异常:不忽略

在响应捕获的异常时,什么都不做通常是错误的。(典型的响应是记录它,或者如果它被认为是“不可能的”,则将其作为 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);

6.3 静态成员:使用类限定

当必须限定对静态类成员的引用时,使用该类的名称进行限定,而不是使用该类类型的引用或表达式。

Foo aFoo = ...;
Foo.aStaticMethod(); // good
aFoo.aStaticMethod(); // bad
somethingThatYieldsAFoo().aStaticMethod(); // very bad

6.4 Finalizers: 不使用

不要覆盖 Object.finalize。 最终化支持计划删除

7 Javadoc

7.1 格式化

7.1.1 一般形式

Javadoc 块的基本格式如本例所示

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

... 或在此单行示例中

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

基本形式始终可以接受。 当整个 Javadoc 块(包括注释标记)可以容纳在单行上时,可以替换为单行形式。 请注意,这仅适用于没有诸如 @param 之类的块标记时。

7.1.2 段落

一个空行(即,仅包含对齐的前导星号 (*) 的行)出现在段落之间,以及块标记组之前(如果存在)。 除第一个段落外,每个段落都在第一个单词前立即包含 <p>,且后面没有空格。 其他块级元素的 HTML 标记(例如 <ul><table><p> 开头。

7.1.3 块标记

使用的任何标准“块标记”都按 @param@return@throws@deprecated 的顺序出现,这四种类型永远不会出现空描述。 当块标记不适合放在一行上时,延续行从 @ 的位置缩进四个(或更多)空格。

7.2 摘要片段

每个 Javadoc 块都以一个简短的摘要片段开头。 这个片段非常重要:它是唯一出现在某些上下文(例如类和方法索引)中的文本部分。

这是一个片段 - 一个名词短语或动词短语,而不是一个完整的句子。 它A {@code Foo} is a...This method returns... 开头,也不形成一个完整的祈使句,如 Save the record.。 但是,该片段像一个完整的句子一样被大写和标点。

提示: 一个常见的错误是以 /** @return the customer ID */ 的形式编写简单的 Javadoc。 这是不正确的,应更改为 /** Returns the customer ID. *//** {@return the customer ID} */

7.3 Javadoc 的使用位置

最起码,Javadoc 存在于每个可见的类、成员或记录组件中,但以下提到了一些例外情况。 如果顶级类是 public,则它是可见的; 如果成员是 publicprotected 并且其包含类是可见的,则该成员是可见的; 如果记录组件的包含记录是可见的,则该记录组件是可见的。

还可以存在其他 Javadoc 内容,如第 7.3.4 节,非必需的 Javadoc 中所述。

7.3.1 例外:不言自明的成员

对于“简单、显而易见”的成员和记录组件(例如 getFoo() 方法),Javadoc 是可选的,如果 确实没有什么其他值得说的,只有“foo”。

重要提示: 不适合引用此例外来证明省略典型读者可能需要知道的相关信息是合理的。 例如,对于名为 canonicalName 的记录组件,如果典型读者可能不知道术语“规范名称”的含义,请不要省略其文档(理由是它只会说 @param canonicalName the canonical name)!

7.3.2 例外:重写

在覆盖超类型方法的方法上,Javadoc 并不总是存在。

7.3.4 非必需的 Javadoc

其他类、成员和记录组件根据需要或期望具有 Javadoc。

每当使用实现注释来定义类或成员的总体目的或行为时,该注释都将改为写为 Javadoc(使用 /**)。

非必需的 Javadoc 不严格要求遵循第 7.1.1、7.1.2、7.1.3 和 7.2 节的格式规则,但当然建议这样做。