Google Java 编程风格指南(中文版)

官方原文:Google Java Style Guide
抓取日期:2026-09-03
本文是非官方中文翻译;若有歧义,以英文原文为准。

目录

1 引言

本文档是 Google 针对 Java™ 编程语言源代码制定的编码标准的完整定义。当且仅当一个 Java 源文件遵守本文中的规则时,才称其采用 Google 风格

与其他编程风格指南一样,本文涵盖的问题不仅包括格式方面的美观问题,还包括其他类型的约定或编码标准。不过,本文主要关注我们普遍遵循的硬性规则,并避免给出无法明确强制执行(无论是由人工还是工具执行)的建议

1.1 术语说明

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

  1. 这一术语作广义使用,指普通类、record 类、enum 类、接口或注解类型(@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. 所有其他空白字符在 char 字面量、字符串字面量和文本块中都必须转义。

  2. Tab 字符不得用于缩进。

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. package 声明
  3. import
  4. 有且仅有一个顶层类声明

每两个实际存在的部分之间以恰好一个空行分隔。

package-info.java 文件具有相同的结构,但不含类声明。

module-info.java 文件不含 package 声明,并以 module 声明取代类声明,除此之外遵循相同的结构。

3.1 许可证或版权信息(如有)

如果某个文件需要包含许可证或版权信息,就必须将其置于此处。


3.2 package 声明

每个源文件都必须有 package 声明。不得使用紧凑源文件。(这条规则显然不适用于 module-info.java 文件;这类文件采用另一种语法,其中不包含 package 声明。)

package 声明不得折行。列宽限制(第 4.4 节,列宽限制:100)不适用于 package 声明。


3.3 import

3.3.1 不得使用通配符 import

通配符(“按需”)import,无论是否为 static,均不得使用

3.3.1.1 不得使用 module import

module import 不得使用

示例:

import module java.base;

3.3.2 不得折行

import 不得折行。列宽限制(第 4.4 节,列宽限制:100)不适用于 import。

3.3.3 顺序与间隔

import 按以下顺序排列:

  1. 所有 static import 组成一组。
  2. 所有非 static import 组成一组。

如果 static import 和非 static import 同时存在,则两组之间以一个空行分隔。import 之间不得有其他空行。

在每一组内,被导入的名称按 ASCII 排序。(注意: 这与 import 按 ASCII 排序并不相同,因为 . 排在 ; 之前。)

3.3.4 类不得使用 static import

静态嵌套类不得使用 static import,而是通过普通 import 导入。

3.4 类声明


3.4.1 有且仅有一个顶层类声明

每个顶层类都位于各自独立的源文件中。


3.4.2 类内容的顺序

类的成员和初始化器采用何种顺序,会极大影响这个类是否易于理解。不过,对此并不存在唯一正确的做法;不同的类可以用不同方式排列其内容。

重要的是,每个类都采用某种符合逻辑的顺序,并且其维护者在被问及时能够解释这种顺序。例如,新方法不得只是习惯性地添加到类的末尾,因为这会形成“按添加日期排序”的时间顺序,而这并不是一种符合逻辑的顺序。


3.4.2.1 重载:不得拆分

类中同名的方法必须集中出现在一个连续的组中,中间不得夹有其他成员。多个构造器同样如此。即使方法或构造器的 staticprivate 等修饰符不同,这条规则也同样适用。

3.5 module 声明

3.5.1 module 指令的顺序与间隔

module 指令按以下顺序排列:

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

每两个实际存在的块之间以一个空行分隔。

4 格式

术语说明: 类块结构指类、方法、构造器或 switch 的主体。注意,根据关于数组初始化器的第 4.8.3.1 节,任何数组初始化器都可以选择按类块结构处理。


4.1 花括号

4.1.1 可选花括号的使用

即使主体为空或只包含一条语句,ifelsefordowhile 语句也使用花括号。

其他可选花括号(例如 lambda 表达式中的花括号)仍是可选的。

4.1.2 非空块:K & R 风格

对于非空块和类块结构,花括号遵循 Kernighan 和 Ritchie 风格:

  • 左花括号前不换行,下文详述的情况除外。

  • 左花括号后换行。

  • 右花括号前换行。

  • 右花括号后换行,仅当该花括号结束一条语句,或者结束方法、构造器或具名类的主体时才如此。例如,如果花括号后面是 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();
    }
    {
      int x = foo();
      frob(x);
    }
  }
};

关于 enum 类的一些例外,见第 4.8.1 节“enum 类”。


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. package 声明和 import(参见第 3.2 节“package 声明”和第 3.3 节“import”)。

  3. 文本块的内容。

  4. 注释中可以复制并粘贴到 shell 的命令行。

  5. 在极少数确有需要的情况下,允许非常长的标识符超出列宽限制。此时,周围代码的有效折行方式应与 google-java-format 生成的方式一致。

4.5 折行

术语说明: 当原本可能占据一行的代码被拆分成多行时,这种做法称为折行

并不存在一个全面、确定的公式,能够说明在每种情况下究竟应如何折行。很多时候,同一段代码有多种有效的折行方式。

注意: 尽管折行通常是为了避免超出列宽限制,但即使代码实际上能容纳在列宽限制内,作者也可以自行决定将其折行。

提示: 提取方法或局部变量可能无需折行就能解决问题。

4.5.1 在何处断行

折行的首要原则是:优先在较高的语法层级断行。此外:

  1. 非赋值运算符处断行时,在该符号之前断行。(请注意,这与 C++ 和 JavaScript 等其他语言的 Google 风格所采用的做法不同。)

    • 这也适用于以下“类似运算符的”符号:

      • 点分隔符(.
      • 方法引用的两个冒号(::
      • 类型边界中的 & 符号(<T extends Foo & Bar>
      • catch 块中的竖线(catch (FooException | BarException e))。
  2. 赋值运算符处断行时,通常在该符号之后断行,但两种方式都可以接受。

    • 这也适用于增强型 for(“foreach”)语句中的冒号。
  3. 方法名、构造器名或 record 类名与其后的左圆括号(()保持在一起。

  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. 类中相邻的成员或初始化器之间:字段、构造器、方法、嵌套类、静态初始化器和实例初始化器。

    • 例外: 两个相邻字段之间(两者之间没有其他代码)的空行是可选的。根据需要使用这种空行,将字段划分成不同的逻辑组
    • 例外: enum 常量之间的空行见第 4.8.1 节
  2. 本文档其他章节要求的位置(例如第 3 节“源文件结构”和第 3.3 节“import”)。

也可以在任何能提升可读性的位置出现一个空行,例如在语句之间用空行将代码组织成逻辑上的小节。对于类中第一个成员或初始化器之前的空行,以及最后一个成员或初始化器之后的空行,既不鼓励也不反对。

允许连续出现多个空行,但绝不要求(也不鼓励)这样做。

4.6.2 水平空白

除语言或其他风格规则要求的位置外,并且不考虑字面量、注释和 Javadoc 内部,在下列位置出现一个 ASCII 空格:

  1. 将任何关键字(例如 ifforcatch)与同一行中紧随其后的左圆括号(()分隔开。

  2. 将任何关键字(例如 elsecatch)与同一行中位于其前面的右花括号(})分隔开。

  3. 在任何左花括号({)之前,但有两个例外:

    • @SomeAnnotation({a, b})(不使用空格)
    • String[][] x = {{"foo"}};(根据下文第 10 项,{{ 之间不要求使用空格)
  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 风格允许这种做法,但绝不要求这样做。即使原有代码已经使用了水平对齐,也不要求维持这种对齐。

下面先给出不使用对齐的示例,再给出使用对齐的示例:

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 enum 类

enum 常量后面的逗号之后,换行是可选的。也允许添加额外的空行(通常只有一行)。以下是一种可行的格式:

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

  NO,
  MAYBE
}

没有方法、其常量也没有文档的 enum 类,可以选择按数组初始化器的形式进行格式化(参见第 4.8.3.1 节的数组初始化器)。

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

由于 enum 类本身就是类,因此其他所有类格式规则均适用。


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 中,只要在其他方面符合 Google 风格,switch 规则就可以写在一行内。(它不得超过列宽限制;如果包含非空块,则 { 后必须换行。)第 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(贯穿)含义的注释都足够(通常为 // fall through)。switch 块的最后一个语句组不需要这一特殊注释。示例:

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

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

新式 switch 中不存在 fall-through(贯穿)。

4.8.4.3 穷尽性与 default 标签的存在

Java 语言要求 switch 表达式和多种 switch 语句必须是穷尽的。这实际上意味着,用作 switch 选择依据的值,其每一个可能取值都会被某个 switch 标签匹配。有 default 标签的 switch 是穷尽的;但也存在其他情况,例如,当 switch 的选择值是 enum,且该 enum 的每个值都由 switch 标签匹配时,它也是穷尽的。Google 风格要求每一个 switch 都是穷尽的,即使 Java 语言本身并未作此要求。这可能需要添加一个 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") // TODO: b/123 - Fix existing CRV violations.
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 package 和 module 名称

package 和 module 名称只能使用小写字母和数字(不得使用下划线)。连续的单词直接连接在一起。例如,应使用 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:全部使用大写字母,每个单词与下一个单词之间用一个下划线分隔。但确切地说,常量究竟是什么

常量是这样的 static 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 非常量字段名称

非常量字段名称(无论是否为 static)采用 lowerCamelCase 书写。

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

5.2.6 参数名称

参数名称采用 lowerCamelCase 书写。

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

5.2.7 局部变量名称

局部变量名称采用 lowerCamelCase 书写。

即使局部变量是 final 且不可变,也不将其视为常量,并且不应采用常量样式。

5.2.8 类型变量名称

每个类型变量的名称采用以下两种样式之一:

  • 一个大写字母,后面可以选择再跟一个数字(例如 ETXT2)。

  • 采用类名称所用形式的名称(参见第 5.2.2 节类名称),后面再跟大写字母 T(例如 RequestTFooBarT)。

5.2.9 未命名变量

未命名变量和参数可以在任何适用的位置使用 _ 语法。例如:

Predicate<String> alwaysTrue = _ -> true;



5.3 Camel case:定义

有时,将英语短语转换为 Camel case 的合理方式不止一种,例如短语中存在首字母缩略词,或存在“IPv6”“iOS”这类不寻常的结构时。为提高可预测性,Google Style 规定了以下(近乎)确定性的方案。

从名称的自然语言形式开始:

  1. 将短语转换为纯 ASCII,并移除所有撇号。例如,“Müller’s algorithm” 可以变为 “Muellers algorithm”。

  2. 将所得结果划分为单词,在空格和所有剩余标点符号(通常是连字符)处拆分。

    • 建议: 如果某个单词在常见用法中已经具有约定俗成的 Camel case 外观,请将其拆分成各个组成部分(例如,“AdWords” 变为 “ad words”)。请注意,"iOS"本身其实并不属于 Camel case;它不符合任何约定,因此此建议不适用。
  3. 现在,将所有内容(包括首字母缩略词)转换为小写,然后仅将以下字符改为大写:

    • ……每个单词的首字符,从而得到 UpperCamelCase(大驼峰式);或

    • ……除第一个单词外,每个单词的首字符,从而得到 lowerCamelCase(小驼峰式)

  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 的方法,都必须标注该注解。这包括:在类中重写超类方法的方法、在类中实现接口方法的方法、在接口中重新声明超接口方法的方法,以及为 record 组件显式声明的访问器方法。

例外: 当父方法带有 @Deprecated 注解时,可以省略 @Override


6.2 捕获的异常:不得忽略

对捕获的异常不作任何响应,只有极少数情况下才是正确的。(典型响应是将其记录到日志;或者,如果认为该异常“不可能”发生,则以 AssertionError 的形式重新抛出。)

当在 catch 块中确实适合完全不采取任何操作时,必须在注释中说明这样做合理的原因。

try {
  int i = Integer.parseInt(response);
  return handleNumericResponse(i);
} catch (NumberFormatException _) {
  // it's not numeric; that's fine, just continue
}
return handleTextResponse(response);

6.3 static 成员:使用类进行限定

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

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


6.4 finalizer:不得使用

不得重写 Object.finalize。finalization 支持已列入移除计划


7 Javadoc

7.1 格式

7.1.1 通用形式

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

/**
 * 这里写有多行 Javadoc 文本,
 * 按常规方式换行...
 */
public int method(String p1) { ... }

……或者如下单行示例所示:

/** 一段特别短的 Javadoc。 */

基本形式始终可以接受。当整个 Javadoc 块(包括注释标记)都能放在一行中时,可以改用单行形式。请注意,这仅适用于不存在 @param 等块标签的情况。

7.1.2 段落

段落之间,以及块标签组(如果存在)之前,须有一个空行——也就是一个仅包含对齐的行首星号(*)的行。除第一个段落外,每个段落都在第一个词的紧前方放置 <p>,且其后不留空格。<ul><table> 等其他块级元素的 HTML 标签前放置 <p>


7.1.3 块标签

所有使用的标准“块标签”都按 @param@return@throws@deprecated 的顺序出现,并且这四类标签的说明绝不可为空。当一个块标签无法放在一行中时,续行相对于 @ 所在位置缩进四个(或更多)空格。

7.2 摘要片段

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

这是一个片段——名词短语或动词短语,而不是完整的句子。它A {@code Foo} is a...This method returns... 开头,也不构成像 Save the record. 这样的完整祈使句。不过,该片段的首字母要大写,并且要像完整句子一样使用标点。

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


7.3 Javadoc 的使用位置

最低限度是,每个可见的类、成员或 record 组件都要有 Javadoc,但下文指出的少数例外除外。顶级类在其为 public 时可见;成员在其为 publicprotected 且其所属类可见时可见;record 组件在其所属 record 可见时可见。

也可以提供额外的 Javadoc 内容,如第 7.3.4 节非必需的 Javadoc所述。

7.3.1 例外:不言自明的成员

对于“简单、显而易见”的成员和 record 组件,例如 getFoo() 方法,如果除了“这个 foo”以外的的确确没有其他值得说明的内容,则 Javadoc 是可选的。

重要: 援引这一例外来为省略一般读者可能需要了解的相关信息辩解是不恰当的。例如,对于名为 canonicalName 的 record 组件,如果一般读者可能根本不知道“canonical name”一词是什么意思,就不要省略其文档(理由是文档只会写成 @param canonicalName the canonical name)!

7.3.2 例外:重写

重写超类型方法的方法不一定总要有 Javadoc。

7.3.4 非必需的 Javadoc

其他类、成员和 record 组件根据需要或意愿提供 Javadoc。

每当原本要用实现注释来说明类或成员的总体用途或行为时,都应将该注释改写为 Javadoc(使用 /**)。

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

翻译术语说明

  • block-like construct:译为“类块结构”,指类、方法、构造器或 switch 的主体。
  • line-wrapping / continuation line:分别译为“折行”和“续行”。
  • fall-through:保留英文并辅以“贯穿”,指旧式 switch 中执行继续进入下一个语句组。
  • Camel case:保留英文;具体形式写作 UpperCamelCase(大驼峰式)和 lowerCamelCase(小驼峰式)。
  • API、ASCII、UTF-8、Unicode、Javadoc、enum、record、lambda、switch、package、import、module、TODO 与 google-java-format 保留英文。