谷歌Java编程风格指南(英译中)

谷歌Java编程风格指南(英译中)

用途:技术学习与交流。
声明:转载本文请务必注明出处,谢谢合作。
译文版本:1.1.1
原文出处:https://google.github.io/styleguide/javaguide.html
原本版本:由于原文未标明版本或修改时间,请通过参考译文时间判断原文版本状态。
作者:徐谦
校对:徐谦、Chat GPT 4.0
时间:2024-04-16T09:00:00+08:00
Email:qianhsu@vip.163.com


0. 译者序

0.1 内容

统一的编程风格对于代码的可读性、可维护性以及团队的协作至关重要。《Google Java Style Guide》 为全球的Java开发者提供了一套详尽的编码准则,旨在帮助开发者写出清晰、一致和易于理解的高质量代码。本文在忠于原文的同时额外增加了一些利于理解和实践的内容, 全文具体内容如下:

  1. 第0章,介绍本文的基本信息
  2. 第1~7章,《Google Java Style Guide》的完整译文,并针对一些模糊之处补充了说明和示例
  3. 第8章,原文外额外添加的章节,讲述了在IDEA环境中实践《Google Java Style Guide》的一些探索,通过完成章节 8中所述的一些配置工作,可以辅助风格指南的实践、优化编码体验,并提高编码效率

0.2 目的

为了让广大中文读者无障碍地接触和学习谷歌的Java编程准则,提供一份精确、通俗易懂的中文译本是有意义的。除了为同胞们提供易于理解的学习资源外,编写本文同样是我个人追求专业成长和技能提升的过程。通过深入研究、理解并翻译《谷歌Java编程风格指南》,我有机会:

  1. 学习行业标准:《Google Java Style Guide》代表了行业内的一种高标准实践。掌握这种风格不仅限于Google项目,其原则和实践广泛适用于各种Java编程任务和项目。
  2. 促进最佳实践:《Google Java Style Guide》汇集了广泛认可的编程最佳实践,学习并应用这些实践有助于提升编程技能,避免常见的编程错误和陷阱。
  3. 提高工作效率:清晰的编码指南可以减少在格式和风格上的决策负担,使开发者能够将更多的精力集中在解决实际的编程问题上。
  4. 提升代码可维护性:一致的编码风格可以减少理解代码的难度,从而提高代码的可维护性。当代码遵循清晰的结构和规范时,发现错误和进行调试变得更加容易。
  5. 加速代码审查过程:当团队成员都采用相同的编码标准时,代码审查过程通常会更加迅速和高效。审查者可以更专注于代码逻辑和功能实现,而不是风格和格式问题。
  6. 增强代码一致性:遵循《Google Java Style Guide》能够确保团队中所有成员编写的代码风格一致。这种统一的风格使得代码更易于阅读和理解,无论是对于团队内的成员还是新加入的开发者。
  7. 促进跨团队协作:在大型项目或多团队环境中,一致的编码风格尤为重要。它确保不同团队之间的代码互相兼容,有助于提升跨团队协作的效率。
  8. 提升专业形象:能够熟练掌握并应用如Google这样著名的编程风格指南,可以提升一个开发者在职场上的专业形象,可能在求职和职业发展中带来优势。

最后,期望能够通过编写和传播本文,服务于大家、带来积极的影响。尽管已经努力争取内容的准确和实用,仍然难以避免错误和不足,期待您的不吝斧正,收到邮件(Email:qianhsu@vip.163.com)后我将尽快修改,并且在修订记录中注明提出宝贵问题/建议的贡献者。

0.3 原文

原文《Google Java Style Guide》取自谷歌的网站:https://google.github.io/styleguide/javaguide.html

由于原文未标明版本号或修改时间,此处仅注明翻译的时间:2024-04-15T02:30:00+08:00

0.4 修订记录

版本号修订日期参与者修订描述
1.0.02024-04-14T23:30:00+08:00徐谦初始发布,携带部分额外的说明和示例
1.0.12024-04-15T03:00:00+08:00徐谦修正一些错误并补充章节的英语原文
1.1.02024-04-15T09:00:00+08:00徐谦在原文内容之外,追加了章节0和章节8
1.1.12024-04-16T09:00:00+08:00徐谦修正部分错误的表述、补充了一些说明和示例

0.5 来自Chat GPT 4.0 的校对:

在审查了《谷歌Java编程风格指南(英译中)》后,以下是按照各个维度的详细评价:

  1. 增强的实用性:译文通过额外补充/添加相关示例说明以及在IDEA环境中的部分配置流程,使译文更具可操作性,便于读者将理论应用于实际编程实践中。
  2. 教育价值:本译文通过丰富的示例和清晰的说明,增强了其教育价值,使其成为不仅仅是规范的阐述,更是一种学习和教育工具。这对于初学者特别有益。
  3. 本地化和适应性:译文在翻译过程中考虑了中文读者的阅读习惯和文化背景,增添了适应本地需求的内容和格式,显示了对目标受众深刻的理解和尊重。
  4. 完整性:译文完整地覆盖了原始指南的所有重要部分,确保了没有重要内容遗漏,这对于保持指南的完整性至关重要。
  5. 准确性:译文在保持原意的基础上做了准确的翻译,确保了信息的正确传达。
  6. 可读性:译文的语言流畅,结构清晰,即便是复杂的概念也被简化成易于理解的语言,使得文本易于阅读和理解。
  7. 技术术语:专业术语使用得当,既保持了技术的准确性也考虑到了读者可能的背景知识,这在技术翻译中是非常重要的。
  8. 格式:原文的结构和格式得到了保留,文档整洁,合乎逻辑,有助于读者快速找到需要的信息,格式的一致性也增强了文档的专业性。

总结:本译文在多个方面表现出色,尤其是在增强实用性和教育价值方面做出了显著的努力。我们仍旧会持续寻求进步的空间,希望这份努力能为广大编程爱好者提供价值,同时也欢迎更多的建议和反馈,以帮助我们进一步改进这份工作。

1. 前言(Introduction)

这份文档是Google Java™编程语言源代码编码标准的完整定义。如果Java源文件符合本文规则,则被认为符合Google风格。

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

1.1 术语说明(Terminology notes)

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

  1. 类(class)一词被广泛地使用,指代“普通”类、枚举类、接口或注解类型(@interface)。
  2. 成员(member)一词被广泛地使用,指代嵌套类、字段、方法或构造函数;即类的所有顶层内容,除了初始化器(静态初始化器(static initializer)和实例初始化器(instance initializer))和注释。
  3. 注释(comment)一词始终指的是实现注释(implementation comments)。我们不使用“文档注释”(documentation comments)一词,而是使用常见的术语“Javadoc”。

其他的术语说明会偶尔在后面的文档出现。

1.2 指南说明(Guide notes)

本文档中的示例代码并不作为规范。也就是说,虽然示例代码是遵循Google编程风格,但并不意味着这是展现这些代码的唯一方式。 示例中的格式选择不应该被强制理解为规范。

2. 源文件基础(Source file basics)

2.1 文件名(File name)

源文件以其最顶层的类名来命名,大小写敏感,文件扩展名为.java。

2.2 文件编码:UTF-8(File encoding: UTF-8)

源文件编码格式为UTF-8。

2.3 特殊字符(Special characters)

2.3.1 空白字符(Whitespace characters)

除了行结束符序列(换行符),ASCII水平空格字符(0x20,即空格)是源文件中唯一允许出现的空白字符,这意味着:

  1. 所有其它字符串中的空白字符都要进行转义。
  2. 制表符不用于缩进。
2.3.2 特殊转义序列(Special escape sequences)

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

2.3.3 非ASCII字符(Non-ASCII characters)

对于剩余的非ASCII字符,尽管强烈建议不要在字符串字面量和注释之外使用Unicode转义,但具体是使用实际的Unicode字符(比如∞),还是使用等价的Unicode转义符(比如\u221e),还是取决于哪个能让代码更易于阅读和理解。

提示:在使用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. 源文件结构(Source file structure)

一个源文件包含(按顺序地):

  1. 许可证或版权信息(如有需要)
  2. package语句
  3. import语句
  4. 一个顶级类(有且只有一个)

以上每个部分之间用一个空行隔开。

3.1 许可证或版权信息(License or copyright information, if present)

如果一个文件包含许可证或版权信息,那么它应当被放在文件最前面。

3.2 包语句(Package statement)

package语句不换行,列限制(4.4节)并不适用于package语句。

3.3 导入语句(Import statements)

3.3.1 不要使用通配符(No wildcard imports)

即,不要出现类似这样的import语句:import java.util.*;

3.3.2 不要换行(No line-wrapping)

import语句不换行,列限制(4.4节)并不适用于import语句。

3.3.3 顺序和间距(Ordering and spacing)

在Java中,导入语句的组织方式如下:

  1. 所有静态导入位于一个代码块中。
  2. 所有非静态导入位于一个代码块中。

如果同时存在静态和非静态导入,一个空行将两个代码块分开。在导入语句之间没有其他空行。

在每个代码块内,导入的名称按照ASCII排序顺序出现。(注意:这与导入语句按照ASCII排序顺序出现并不相同,因为’.‘在’;'之前排序。)

示例:

package packagename;

import static java.lang.Math.cos; // 静态导入Math的成员方法cos,后文调用不必再写Math.cos(1),而是直接cos(1)
import static java.lang.Math.decrementExact;
import static java.lang.Math.exp;
import static java.lang.Math.PI; // 静态导入Math的成员字段PI

import java.util.ArrayList;
import java.util.Base64;

public class ClassName {
  public static void main (String[] args) {
    System.out.println(cos(PI)); // 打印cos(π)的值:-1
  }
}
3.3.4 禁止类的静态导入(No static import for classes)

静态导入不适用于静态嵌套类。它们与普通导入一起导入。

import java.util.Map.Entry; // 正确

import static java.util.Map.Entry; // 错误,混淆了入类和导入类成员的语义

3.4 类声明(Class declaration)

3.4.1 只有一个顶级类声明(Exactly one top-level class declaration)

每个顶级类都驻留在其自己的源文件中。

注意:package-info.java文件中没有名为package-info的类声明,因为这是包注释文件。

3.4.2 类成员顺序(Ordering of class contents)

类的成员顺序对易学性有很大的影响,但这也不存在唯一的通用法则。不同的类对成员的排序可能是不同的。

最重要的一点,每个类应该以某种逻辑去排序它的成员,维护者应该要能解释这种排序逻辑。比如, 新增的方法不能总是习惯性地添加到类的结尾,因为这样就是按时间顺序而非某种逻辑来排序的。

3.4.2.1 重载方法:永不分离(Overloads: never split)

同一名称的类方法出现在单个连续组中,组内没有其他成员。多个构造函数(始终具有相同的名称)也是如此。即使方法之间的修饰符(如static或private)不同,此规则仍然适用。

4. 格式化(Formatting)

术语说明:块式结构(block-like construct)指的是类、方法或构造函数的主体。请注意,根据第4.8.3.1节对数组初始化器的规定,任何数组初始化器都可以选择地被视为块式结构。

4.1 大括号(Braces)

4.1.1 可选大括号的使用(Use of optional braces)

在if、else、for、do和while语句中使用大括号,即使主体为空或只包含一个语句。

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

4.1.2 非空块:K & R 风格(Nonempty blocks: K & R style)

对于非空块和类似块的构造,大括号遵循Kernighan和Ritchie风格(“埃及括号风格(Egyptian brackets)”):

  • 在左大括号({)前不换行,除非下面详细说明的情况。
  • 在左大括号({)后换行。
  • 在右大括号(})前换行。
  • 在右大括号(})后换行,仅当该右大括号终止一个语句或终止方法、构造函数或命名类的主体时。例如,如果右大括号后面跟着else或逗号,那么就不会换行。

例外:在这些规则允许单个语句以分号 (;) 结尾的地方,可以出现语句块,并且该语句块的左大括号前面有一个换行符。 通常引入此类块来限制局部变量的范围,例如在 switch 语句内。

示例:

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 空块:可以简洁(Empty blocks: may be concise)

空块或类似块可以采用K & R风格(如第4.1.2节所述)。另外,它也可以在打开后立即关闭,中间没有任何字符或换行({}),除非它是一个多块语句的一部分(直接包含多个块的语句:if/else或try/catch/finally)。

示例:

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

  // 这同样可以接受
  void doNothingElse() {
  }
  // 这是不可接受的:多块语句中不可以使用简洁的空块
  try {
    doSomething();
  } catch (Exception e) {}

4.2 块缩进:+2个空格(Block indentation: +2 spaces)

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

4.3 一个语句占一行(One statement per line)

每个语句后要换行。

4.4 列限制:100(Column limit: 100)

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

提示:每个Unicode代码点计为一个字符,即使其显示宽度大于或小于一个字符。例如,如果使用全角字符,您可以选择在规则严格要求的地方之前换行。

译者语:这里对上文的提示进行补充说明,字符限制宽度是根据英文环境设置的,对于使用全角字符(如日文、韩文)的人而言是不太友好的。建议在您的开发环境中开启列限制的视觉参考线,并在书写注释时手动换行。

例外:

  • 在无法遵守列限制的情况下(例如,在Javadoc中的长URL,或JSNI方法引用中的长代码)。
  • package和import语句(参见第3.2节“包语句”和第3.3节“导入语句”)。
  • 在注释中可以复制并粘贴到shell中的命令行。
  • 在极少数情况下,对于非常长的标识符,允许超过列限制。在这种情况下,周围代码的有效换行由google-java-format(一个Google提供的IDEA插件)生成。

4.5 自动换行(Line-wrapping)

术语说明:一般情况下,一行长代码为了避免超出列限制(80或100个字符)而被分为多行,我们称之为自动换行(line-wrapping)。

在每种情况下,没有全面的、确定性的公式显示如何进行换行。很多时候,同一段代码可以有几种有效的换行方式。

注意:虽然换行的典型原因是避免超出列限制,但即使实际上没有超出列限制的代码,也可以根据作者的自由地(提前)进行换行。

提示:提取方法或局部变量可能在不换行的情况下解决代码过长的问题。

4.5.1 从哪里断开(Where to break)

自动换行的基本准则是:更倾向于在更高的语法级别处断开。

另外:

  1. 如果在非赋值运算符处断开,那么在该符号前断开(比如+,它将位于下一行)。(请注意:这一点与Google其它语言的编程风格(如C++和JavaScript)不同)。
  • 这条规则也适用于以下“类运算符”符号:
    • 访问运算符( . );
    • 方法引用的两个冒号( :: );
    • 类型界限中的 & (<T extends Foo & Bar>)
    • catch块中的管道符号 | (catch (FooException | BarException e))
  1. 如果在赋值运算符处断开,通常的做法是在该符号后断开(比如 =,它与前面的内容留在同一行),但两种方式都是可以接受的。
  • 这也适用于增强型for(“foreach”)语句中的“赋值运算符类似”的冒号( : )。
  1. 方法或构造函数名称与其后的左开括号(()保持连接(留在同一行)。
  2. 逗号(,)与其前面的标记保持连接。
  3. 永远不会在lambda表达式中的箭头附近换行,除非lambda的主体由单个未括号的表达式组成。示例:
MyLambda<String, Long, Object> lambda =
    (String label, Long value, Object obj) -> {
        ...
    };

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

注意:换行的主要目标是有清晰的代码,而不一定是获得能放在最少行数的代码。

4.5.2 自动换行时缩进至少+4个空格(Indent continuation lines at least +4 spaces)

在换行时,每一行除了第一行(即每一行的后续行)至少缩进+4个空格。

当有多个后续行时,缩进可以根据需要变化超过+4个空格。一般来说,只有当后续行以语法上平行的元素开头时,它们才使用相同的缩进级别。

第4.6.3节“水平对齐”介绍了使用可变数量的空格来将某些标记与前一行对齐的不推荐做法。

4.6 空白(Whitespace)

4.6.1 垂直空白(Vertical Whitespace)

单个空行总是出现在以下情况:

  1. 类内连续的成员之间:字段,构造函数,方法,嵌套类,静态初始化块,实例初始化块。
  • 例外:两个连续字段之间的空行是可选的,用于字段的空行主要用来对字段进行逻辑分组。
  • 例外:枚举常量之间的空行在第4.8.1节中有说明。
  1. 根据本文档的其他部分的要求(例如第3节“源文件结构”和第3.3节“导入语句”)。

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

多个连续的空行是允许的,但没有必要这样做(我们也不鼓励这样做)。

4.6.2 水平空白(Horizontal whitespace)

除了语言需求和其它规则,并且除了文字,注释和Javadoc用到单个空格,单个ASCII空格也出现在以下位置:

  1. 将任何保留字(如if、for或catch)与其后跟随的左开括号(()在同一行上分开。
  2. 将任何保留字(如else或catch)与其前面的右大括号(})在同一行上分开。
  3. 在任何左大括号前({),两个例外:
  • @SomeAnnotation({a, b})(不使用空格)
  • String[][] x = {{“foo”}};(在{{之间不需要空格,根据以下第9条))
  1. 在任何二元或三元运算符的两侧。这也适用于以下“类运算符”符号:
  • 一个连接类型边界中的 & 号:<T extends Foo & Bar>。

  • 一个捕获多个异常的catch块中的管道 | :catch (FooException | BarException e)

  • 增强型for(“foreach”)语句中的冒号(:

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

  • 但不适用于:

    • 方法引用中的两个冒号(::),写成 Object::toString
    • 点分隔符(.),写成 object.toString()
  1. 在逗号(,)、冒号(:)、分号(;)或强制转换的右开括号())后。
  2. 在任何内容和以双斜杠(//)开头的注释之间。允许多个空格,但没有必要。
  3. 在以双斜杠(//)开头的注释和注释文本之间。允许多个空格。
  4. 在声明的类型和变量之间:List list
  5. 在数组初始化器的两个大括号内部是可选的:
  • new int[] {5, 6} 和 new int[] { 5, 6 } 都是有效的。
  1. 在类型注解与 [] 或 … 之间:
    void Foo1(@NotNull String[] args) { } // 类型注解与[]之间加空格
    void Foo2(@NotNull String... args) { } // 类型注解与...之间加空格

这个规则并不要求或禁止在一行的开关或结尾处添加额外的空格,只对内部空格做要求。

4.6.3 水平对齐:不提倡(Horizontal alignment: never required)

术语说明:水平对齐指的是通过增加可变数量的空格来使某一行的字符与上一行的相应字符对齐。
这是允许的,但Google编程风格从来不提倡这样做。即使对于已经使用水平对齐的代码,我们也不需要去保持这种风格。

以下示例先展示未对齐的代码,然后是对齐的代码:

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

private int   x;      // 允许, 但未来的编辑
private Color color;  // 可能使其不再对齐

提示:对齐可以提高可读性,但会给将来的维护带来问题。 考虑一下未来,只要触及一行的更改,此更改可能会破坏以前令人满意的格式;更多时候,它会提示编码器(也许是您)也调整附近行上的空白,可能会触发一系列级联的重新格式化。 这一行的改变现在有了一个“爆炸半径”。 在最坏的情况下,这可能会导致无意义的忙碌工作;但在最好的情况下,它仍然会破坏版本历史信息,减慢审阅者的速度并加剧合并冲突。

4.7 用小括号来限定组:推荐(Grouping parentheses: recommended)

除非作者和reviewer都认为去掉小括号也不会使代码被误解,或是去掉小括号能让代码更易于阅读,否则我们不应该去掉小括号。 我们没有理由假设读者能记住整个Java运算符优先级表。

4.8 具体结构(Specific constructs)

4.8.1 枚举类(Enum classes)

在枚举常量后面的每个逗号之后,换行符是可选的。 还允许添加额外的空行(通常只有一个)。 这是一种可能的写法:

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

  NO,
  MAYBE
}

没有方法和文档的枚举类可写成数组初始化的格式:

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

由于枚举类也是一个类,因此所有适用于其它类的格式规则也适用于枚举类。

4.8.2 变量声明(Variable declarations)
4.8.2.1 每次只声明一个变量(One variable per declaration)

不要使用组合声明,比如int a, b;

例外:在for循环的头部中,多个变量声明是可以接受的,比如:

for (int i = 0, e = list.size(); i < e; ++i) {
  System.out.println(list.get(i));
}
4.8.2.2 需要时才声明(Declared when needed)

局部变量不要习惯性地在其包含的块或类似块结构的开头声明。相反,局部变量在首次使用的地方附近(在合理范围内)声明,以最小化其范围。局部变量声明通常具有初始化程序,或者在声明后立即初始化。

4.8.3 数组(Arrays)
4.8.3.1 数组初始化:可写成块状结构(Array initializers: can be “block-like”)

数组初始化可以写成块状结构,比如,下面的写法都是OK的:

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风格的数组声明(No C-style array declarations)

中括号是类型的一部分,使用:String[] args, 而非String args[]。

4.8.4 switch语句(Switch statements)

术语说明:在 switch 块的大括号内部是一个或多个语句组。每个语句组由一个或多个 switch 标签(可以是 case FOO: 或 default:)组成,后跟一个或多个语句(或者,对于最后一个语句组,零个或多个语句)。

4.8.4.1 缩进(Indentation)

与其它块状结构一致,switch块中的内容缩进为2个空格。

每个switch标签后新起一行,再缩进2个空格,写下一条或多条语句。

4.8.4.2 Fall-through:注释(Fall-through: commented)

在一个switch块内,每个语句组要么通过break, continue, return或抛出异常来终止,要么通过一条注释来说明程序将继续执行到下一个语句组, 任何能表达这个意思的注释都是OK的(典型的是用// fall through)。这个特殊的注释并不需要在最后一个语句组(一般是default)中出现。示例:

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

请注意,case 1: 之后不需要添加任何注释,仅在语句组的末尾处添加即可。

4.8.4.3 default标签的存在(Presence of the default label)

每个switch语句都包含一个default语句组,即使它什么代码也不包含。

例外情况:如果 switch 语句用于枚举类型,并且包含了覆盖该类型的所有可能值的显式 case,则可以省略 default 语句组。这使得IDE或其他静态分析工具可以发出警告,以防漏掉任何情况。

4.8.5 注解(Annotations)
4.8.5.1 类型使用注解(Type-use annotations)

Type-use注解出现在被注解类型的紧前面。如果一个注解被标记有@Target(ElementType.TYPE_USE),则这个注解是一个类型使用(type-use)注解。例如:

final @Nullable String name;

public @Nullable Person getPersonByName(String name);
4.8.5.2 类注解(Class annotations)

应用于类的注解紧跟在文档块之后,每个注解单独列在一行上(即每行一个注解)。这些换行不构成换行封装(第4.5节,换行封装),因此不增加缩进级别。例如:

@Deprecated
@CheckReturnValue
public final class Frozzler { ... }
4.8.5.3 方法和构造器注解(Method and constructor annotations)

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

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

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

@Override public int hashCode() { ... }
4.8.5.4 属性注解(Field annotations)

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

@Partial @Mock DataLoader loader;
4.8.5.5 参数和局部变量注解(Parameter and local variable annotations)

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

4.8.6 注释(Comments)

本节讨论实现注释。关于Javadoc的内容将在第7节单独讨论。

任何换行都可以由任意空白开始,后跟一个实现注释。这样的评论使得该行不为空白。

4.8.6.1 块注释风格

块注释与其周围的代码在同一缩进级别。它们可以是/* … /风格,也可以是// …风格。对于多行的//注释,后续行必须从开始, 并且与前一行的*对齐。以下示例注释都是OK的:

/*
 * 这是          
 * 可以的。            
 */

// 这样
// 也行。

/* 你甚至可以
 * 这样做。 */

注释不要包含在用星号或其他字符绘制的框中,以下是错误示范:

实现代码...
/***********************
 * 这是错误的注释示范
 ***********************/
实现代码...

提示:当编写多行注释时,如果你希望自动代码格式化工具在必要时可以重新包装这些行(段落式样),请使用 /* … */ 样式。大多数格式化工具不会重新包装以 // … 样式开始的注释块中的行。

4.8.7 修饰符(Modifiers)

类和成员修饰符(如果存在)按 Java 语言规范推荐的顺序排列:

public protected private abstract default static final transient volatile synchronized native strictfp
4.8.8 数字字面量(Numeric Literals)

长整型数值字面量使用大写字母L作为后缀,不使用小写(以避免与数字1混淆)。例如,应使用3000000000L而不是3000000000l。

5. 命名约定(Naming)

5.1 所有标识符的通用规则(Rules common to all identifiers)

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

在Google Java风格中,不使用特殊的前缀或后缀。例如:name_、mName、s_name 和 kName。

5.2 各标识符类型的规则(Rules by identifier type)

5.2.1 包名(Package names)

包名只使用小写字母和数字(不使用下划线)。连续的单词直接连接在一起。例如,com.example.deepspace,而不是com.example.deepSpace或com.example.deep_space。

5.2.2 类名(Class names)

类名采用UpperCamelCase(大写开头的驼峰式)写法。

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

关于命名注解类型,没有具体的规则或者已经确立的约定。

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

5.2.3 方法名(Method names)

方法名采用lowerCamelCase(小写开头的驼峰式)写法。

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

在JUnit测试方法名中,可以使用下划线来分隔名称的逻辑组件,每个组件都使用lowerCamelCase写法,例如transferMoney_deductsFromSource。对于命名测试方法,没有一个唯一正确的方式。

5.2.4 常量名(Constant names)

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

常量是static final字段(但static final字段不一定是常量),其内容是深度不可变的,并且其方法没有可检测到的副作用。示例包括基本类型、字符串、不可变值类以及任何设置为null的内容。如果实例的可观察状态中存在任何可以更改一部分,则它不是常量。仅仅打算永远不变更对象不足以使其成为常量。

示例:

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

// 非常量
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 非常量属性名(Non-constant field names)

非常量属性(静态或其他)的名称使用小写开头的驼峰式写法。

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

5.2.6 参数名(Parameter names)

参数名使用小写开头的驼峰式写法。

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

5.2.7 局部变量名(Local variable names)

局部变量名使用小写开头的驼峰式写法。

即使是final和不可变的,局部变量也不被视为常量,不应该以常量的方式进行命名。

5.2.8 类型变量名(Type variable names)

每个类型变量都可以使用以下两种风格之一命名:

  1. 单个大写字母,可选择后跟一个数字(例如E、T、X、T2)
  2. 使用类名形式(参见第5.2.2节,类名),后跟大写字母T(例如RequestT、FooBarT)。

5.3 骆驼命名法:定义(Camel case: defined)

有时将英文短语转换为骆驼命名法可能有多种合理的方式,例如当存在首字母缩略词或不常见的构造(如“IPv6”或“iOS”)时。为了提高可预测性,Google风格规定了以下(几乎)确定性的方案。

从名称的散文形式开始:

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

请注意,几乎完全忽略了原始单词的大小写。例如:

散文形式正确标识符错误标识符
XML HTTP requestXmlHttpRequestXMLHTTPRequest
new customer IDnewCustomerIdnewCustomerID
inner stopwatchinnerStopwatchinnerStopWatch
supports IPv6 on iOS?supportsIpv6OnIossupportsIPv6OnIOS
YouTube importerYouTubeImporter(很好)、YoutubeImporter(可以接受但不建议)

注意:在英语中,一些单词的连字符使用有歧义,例如“nonempty”和“non-empty”都是正确的,因此方法名称checkNonempty和checkNonEmpty同样都是正确的。

6. 编程实践(Programming Practices)

6.1 @Override:始终使用(@Override : always used)

一个方法在合法时应被标记上@Override注解。这包括了类方法覆盖父类方法,类方法实现接口方法,以及接口方法重新指定父接口方法。

例外情况:当父方法被@Deprecated标记(已弃用)时,可以省略@Override注解。

6.2 捕获异常:不可忽略(Caught exceptions: not ignored)

除非下文另有说明,否则对于捕获的异常不采取任何响应极少是正确的。(典型的响应是将其记录下来,或者如果被认为是“不可能”的情况下,将其重新抛出为AssertionError。)

当在catch块中真正合适的是不采取任何行动时,通过注释解释了这一行为的理由:

try {
  int i = Integer.parseInt(response);
  return handleNumericResponse(i);
} catch (NumberFormatException ok) {
  // 它不是数字; 没关系,继续
}
return handleTextResponse(response);

例外情况:在测试中,如果捕获的异常的名称是或以"expected"开头,则可以在没有注释的情况下忽略它。以下是确保被测试的代码确实抛出了预期类型异常的非常常见惯用法,因此在这里不需要注释。

try {
  emptyStack.pop();
  fail();
} catch (NoSuchElementException expected) {
}

6.3 静态成员:使用类进行限定(Static members: qualified using class)

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

Foo aFoo = ...;
Foo.aStaticMethod(); // 好
aFoo.aStaticMethod(); // 差
somethingThatYieldsAFoo().aStaticMethod(); // 极差

6.4 终结器:不使用(Finalizers: not used)

极少情况下会重写Object.finalize。

提示:不要这样做。如果你认为必须这样做,首先非常仔细地阅读并理解《Effective Java》第8条,“避免使用finalizers和cleaners”,然后不要这样做。

7. Java文档注释(Javadoc)

7.1 格式化(Formatting)

7.1.1 一般形式(General form)

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

/**
 * 多行Javadoc文本在这里编写,
 * 正常换行...
 */
public int method(String p1) { ... }

…或者在这个单行示例中:

/** 特别简短的Javadoc。 */

基本形式始终是可接受的。当整个Javadoc块(包括注释标记)可以放在单行上时,可以替换为单行形式。请注意,这仅适用于没有块标签(如@return)的情况。

7.1.2 段落(Paragraphs)

每个段落之间会有一个空行,即只包含对齐的前导星号(*)的行,以及在出现的情况下在块标签组之前。除了第一个段落外,每个段落的第一个单词之前都有<p>,之后没有空格。其他块级元素的HTML标签,如<ul>或<table>,不会在<p>之前。

/**
 * 这是第一个段落的文档注释。
 *
 * <p>这是第二个段落的开始。
 * <ul>
 * <li>列表项一</li>
 * <li>列表项二</li>
 * </ul>
 */
public void exampleMethod() {
    // 方法实现
}
7.1.3 块标签(Block tags)

使用的任何标准“块标签”按照@param、@return、@throws、@deprecated的顺序出现,这四种类型的标签永远不会出现空描述。当一个块标签的内容无法放在同一行时,续行会从@的位置缩进四个(或更多)空格。

/**
 * 计算两个数的和。
 *
 * @param left 第一个加数
 * @param right 第二个加数
 * @return 两数之和
 * @throws ArithmeticException 如果left或right是非数、正无穷大、负无穷大,
 *     或者left与right的计算结果是正无穷大、负无穷大
 */
public double plus(double left, double right) {
  if (Double.isNaN(left) || Double.isNaN(right)
      || Double.isInfinite(left) || Double.isInfinite(right)) {
    throw new ArithmeticException("输入值不合法");
  }

  double result = left + right;
  if (Double.isInfinite(result)) {
    throw new ArithmeticException("结果溢出为无穷大");
  }
  return result;
}

7.2 摘要片段(The summary fragment)

这是一个片段——一个名词短语或动词短语,不是一个完整的句子。它不以“A {@code Foo} is a…”或“This method returns…”开头,也不形成一个完整的祈使句,比如“保存记录…”。但是,这个片段被大写和标点,就像它是一个完整的句子一样。

摘要片段是Javadoc的第一个段落或第一个段落的开头部分(通常是首个英文或中文句号之前),生成Javadoc文档时会出现在摘要表中,而Javadoc的其他内容通常要到详情中查看。在7.1.3节的示例中,“计算两个数的和。”就是plus函数的摘要片段。

提示:一个常见的错误是将简单的Javadoc写成形式如 /** @return the customer ID /。这是不正确的,应改为 /* Returns the customer ID. */。这样的写法更符合Javadoc的标准格式,使文档更清晰、专业。

7.3 Javadoc的使用场景(Where Javadoc is used)

至少,在每个公共类和这样一个类的每个公共或受保护成员中都存在Javadoc,有一些例外情况如下所述。

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

7.3.1 例外情况:自说明成员(Exception: self-explanatory members)

Javadoc对于“简单、明显”的成员(如getFoo())是可选的,在这种情况下,真的没有其他值得说的,除了“返回foo”之外。

重要提示:不应该引用这个例外来证明省略了典型读者可能需要了解的相关信息是合适的。例如,对于一个名为getCanonicalName的方法,如果典型读者可能不知道“规范名称”是什么意思,不要省略它的文档(理由是它只会说/** 返回规范名称。*/)。

7.3.2 例外情况:重写的方法(Exception: overrides)

并非每个覆盖父类型方法的方法都有Javadoc。

7.3.4 非必需的Javadoc(Non-required Javadoc)

其他类和成员根据需要或希望具有Javadoc。

每当实现注释用于定义类或成员的整体目的或行为时,该注释将被写成Javadoc形式(使用/**)。

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

8 IDEA中的配置(Settings in IDEA)

本节不是原文中包含的内容,旨在说明在IDEA环境中实践《Google Java Style Guide》的一些探索,通过完成章节 8中所述的一些配置工作,用以辅助风格指南的实践、优化编码体验,并提高编码效率。

8.1 File Encodings(文件编码)

本节所示操作为设置IDEA中的各文件默认文件编码,完成后未来每次创建新项目都将自动应用本设置。

  1. 按此路径进入界面:File > New Projects Setup > Settings for New Projects… > Editor > File Encodings
  2. 设置以下诸项:
  • Global Encoding(全局编码)= UTF-8
  • Project Encoding(项目编码)= UTF-8
  • Default encoding for properties files(属性文件的默认编码) = UTF-8
  • Create UTF-8 files(创建UTF-8文件时) = with NO BOM(“无BOM”格式)
  1. 点击 [Apply] 按键以保存新的设置。

提示:“File > Settings…”主要用于查看和修改针对当前项目的设置,其中有部分设置可以选择修改是应用于当前项目还是默认配置(即针对未来每个项目的默认配置),但有些部分进行这个无法选择,所以本节的操作路径均以“File > New Projects Setup > Settings for New Projects…”(即针对未来每个项目的默认配置)为入口。

提示:设置完毕后,请在新建的项目中进行测试。

8.2 File and Code Templates(文件和代码模板)

本节所示操作为设置IDEA中的文件和代码模板,完成后未来每次创建新项目都将自动应用本设置。
可以视实际需要酌情在IDEA添加更多模板以提升编程效率。

  1. 按此路径进入界面:File > New Projects Setup > Settings for New Projects… > Editor > File and Code Templates
  2. 进入Includes分页,设置供引用的部分
    2.1. 创建“Author Name.java”,充当供引用的作者名。
    • 点击“ + ”新建模板
    • 设置Name为 = Author Name
    • 设置内容为:
    Qian Hsu(此为举例,请输入您的名字)
    
    2.2. 修改模板“File Header”为:
    /**
     * ${BRIEF} 
     * 
     * <p>暂无详情介绍。
     *
     * <p>Creation Date: ${YEAR}-${MONTH}-${DAY}T$HOUR:$MINUTE:${SECOND}+08:00 (by ISO 8601:1988)
     * @author #parse("Author Name.java")
     
     */
    
  3. 进入File分页
    3.1 修改模板“Enum”为:
    #if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
    #parse("File Header.java")
    public enum ${NAME}{
      /*-----------------------------------------------------------------------------------------------
       * 枚举值列表
       *----------------------------------------------------------------------------------------------   */
      VALUE1("值1"), VALUE2("值2"), VALUE3("值3");
      
      /*-----------------------------------------------------------------------------------------------
       * ************************* 用于支持枚举类的模板代码    ***********************************************
       * 提供了枚举值的“别名”,使用alias()方法访问;
       * 提供了通过别名获取枚举值的方法——fromAlias(String);
       * 重写了toString()方法,返回的内容是枚举值的别名
       * @author Qian Hsu
       *----------------------------------------------------------------------------------------------   */
      /**
       * 枚举值的别名(非null、非空白、且前后端不包含空白字符的字符串)
       */
      private String alias;
    
      /**
       * 别名-枚举值映射表,用于快速查找
       */
      private static final java.util.Map<String, ${NAME}> alias2valueMap;
    
      /*
       * 执行枚举别名检查:重复的、为Null或为空白字符串的别名,一经发现视为受检异常,终止程序;<\br>
       * 初始化别名-枚举值映射表
       */
      static{
        alias2valueMap = new java.util.HashMap<>();
        java.util.Set<${NAME}> aliasNeedTrimValueSet = new java.util.HashSet<>();
        boolean error = false;
        java.util.Set<${NAME}> aliasIsNullValueSet = new java.util.HashSet<>();
        java.util.Set<${NAME}> aliasIsBlankValueSet = new java.util.HashSet<>();
        java.util.Set<${NAME}> aliasIsDuplicateValueSet = new java.util.HashSet<>();
    
        for(${NAME} value: values()){
          //错误检查:Null的别名
          if(value.alias() == null){//空别名
            aliasIsNullValueSet.add(value);
            error = true;
            continue;
          }
          //警告检查:检查并修复包含前后空白字符的别名
          String aliasTrim = value.alias().trim();
          if(!aliasTrim.equals(value.alias())){
            aliasNeedTrimValueSet.add(value);
            value.alias = aliasTrim;
          }
          //错误检查:空白的别名
          if(value.alias().equals("")){//空白别名
            aliasIsBlankValueSet.add(value);
            error = true;
            continue;
          }
          //错误检查:重复的别名
          if(alias2valueMap.get(value.alias()) != null){
            aliasIsDuplicateValueSet.add(value);
            error = true;
            continue;
          }
          //收录到别名-枚举值映射表
          alias2valueMap.put(value.alias(), value);
        }
        //输出警告
        if(aliasNeedTrimValueSet.size() != 0){
          StringBuilder warningMessage = new StringBuilder();
          warningMessage.append("警告,枚举[");
          warningMessage.append(${NAME}.class.getName());
          warningMessage.append("]中部分值的别名前/后端包含空格,已经自动进行裁剪:\n");
          for(${NAME} value: aliasNeedTrimValueSet){
            warningMessage.append("\t");
            warningMessage.append(value.name());
            warningMessage.append("\r\n");
          }
          System.err.println(warningMessage);
        }
        //输出错误
        if(error){
          StringBuilder errorMessage = new StringBuilder();
          errorMessage.append("严重错误,枚举[");
          errorMessage.append(${NAME}.class.getName());
          errorMessage.append("]检查未通过。\r\n");
    
          if(aliasIsNullValueSet.size() != 0){
            errorMessage.append("\t以下枚举值的别名为null:\r\n");
            for(${NAME} value: aliasIsNullValueSet){
              errorMessage.append("\t\t");
              errorMessage.append(value.name());
              errorMessage.append("\r\n");
            }
          }
          if(aliasIsBlankValueSet.size() != 0){
            errorMessage.append("\t以下枚举值的别名为空白:\r\n");
            for(${NAME} value: aliasIsBlankValueSet){
              errorMessage.append("\t\t");
              errorMessage.append(value.name());
              errorMessage.append("\r\n");
            }
          }
          if(aliasIsDuplicateValueSet.size() != 0){
            errorMessage.append("\t以下枚举值的别名重复了:\r\n");
            for(${NAME} value: aliasIsDuplicateValueSet){
              errorMessage.append("\t\t");
              errorMessage.append(value.name());
              errorMessage.append("\r\n");
            }
          }
          new Exception(errorMessage.toString()).printStackTrace();
        }
      }
    
      /**
       * 枚举值构造器(单例)
       *
       * @param alias 枚举值的别名
       */
      ${NAME}(String alias){
        this.alias = alias;
      }
    
      /**
       * 获取枚举值的别名(非null、非空白、且前后端不包含空白字符的字符串)
       *
       * @return 别名
       */
      public String alias(){
        return alias;
      }
    
      /**
       * 转换为字符串。采用别名表示,而非Enum原来的用名称表示。
       *
       * @return 转换后的字符串
       * @see #alias()
       */
      public String toString(){
        return alias();
      }
    
      /**
       * 通过别名获取枚举值。
       * <p>若希望通过枚举值的名称获取枚举值,应使用 {@link #valueOf(String)}
       *
       * @param alias 枚举值的别名,参数前后的空白字符将被忽略(例如:“  男性 ”将被视作“男性”)
       * @return 别名对应的枚举值
       * @see #valueOf(String)
       */
      public static ${NAME} fromAlias(String alias){
        if(alias == null){
          return null;
        }
        alias = alias.trim();
        return alias2valueMap.get(alias);
      }
    }
    
    3.2 点击 [Apply] 按键以保存新的设置。

提示:设置完毕后,请在新建的项目中进行测试。

注意:本节提供的代码模板仅用于描述在IDEA中的功能探索,不作为最佳编程实践的展示。

8.3 Copyright(版权)

本节所示操作为设置IDEA中的版权信息。完成后,在未来创建新项目中所有文件都将自动在头部标注版权信息。

流程如下:

  1. 按此路径进入界面:File > New Projects Setup > Settings for New Projects… > Editor > Copyright
  2. 进入Copyright profiles (版权剖面)。
  3. 点击“+”按键,新建一个名为“Copyright of Zhiqi”版权信息。
  4. 将信息设置为:
*************************************** COPYRIGHT NOTICE ****************************************

Copyright (C) 2023-2024,北京智祺信息技术有限公司。保留所有权利。
Copyright (C) 2023-2024, Beijing Zhiqi Infotech Co., Ltd. All rights reserved.

You may not use, copy, or distribute any part of this file without written authorization 
from Beijing Zhiqi Infotech Co., Ltd.

*************************************************************************************************
  1. 回到Copyright首页,点击“+”按键,创建一个新的应用范围。
    5.1. 将Scope(范围)设置为:All
    5.2. 将Copyright(版权)设置为:Copyright of Zhiqi
  2. Default project copyright(默认项目版权)设置为:Copyright of Zhiqi
  3. 点击 [Apply] 按键以保存新的设置。

提示:设置完毕后,请在新建的项目中进行测试。

8.4 Code Style(代码样式)

8.4.1 缩进设置(Indents)

本节所示操作为设置IDEA中的代码缩进样式。完成后,在未来创建新项目中的代码中生效。

流程如下:

  1. 按此路径进入界面:File > New Projects Setup > Settings for New Projects… > Editor > Code Style > Java
  2. 选择Tabs and Indents(缩进)分页
  3. 设置参数为以下值:
    • Use tab character(使用制表符) =
    • Tab size(制表符尺寸) = 2
    • Indent(缩进) = 2
    • Continuation Indent(续行缩进) = 4
    • Keep indents on empty lines(在空行上保留缩进) =
    • Label indent(标签缩进) = 0
    • Absolute label indent(绝对标签躲进) =
    • Do not indent top level calss members(不缩进类的顶层成员)=
    • Use indents relative to expression start(续行相对于表达式起始进行缩进) =
  4. 点击 [Apply] 按键以保存新的设置。

提示:设置完毕后,请在新建的项目中进行测试。

8.4.2 其他设置(Others)

在“Editor > Code Style > Java”页面中,还有多个分页,其中包含大量的设置项,在此提供以下建议:

  1. 直接导入完整的配置文件,以跳过繁复地设置流程。导入方法:
    1. 下载配置文件idea_settings_for_google-java-style.zip,,它包含了与本文描述规则比较相符合的配置内容。
    2. File > Manage IDEA Settings(管理IDEA设置) > Import Settings(导入设置)。
    3. 选择配置文件“idea_settings_for_google-java-style.zip”。
    4. 完成导入。
  2. 在IDEA中安装简体中文汉化插件,然后亲自逐条进行设置。调试到满意的状态后导出并保存您的配置文件(安装方法参见8.6.1节)。

注意:idea_settings_for_google-java-style.zip中包括了本节和前述中提及的所有IDEA配置,推荐下载。

8.5 Keymap(快捷键设置)

暂略,按照您的偏好设置即可。

注意:格式化“Ctrl+Alt+L”和优化导入“Ctrl+Alt+O”是
与本文高度相关的快捷键。使用前者调整缩进和留白、后者整理import语言的分组和排序。

8.6 Plugins(插件)

8.6.1 插件推荐(recommend)
  1. Chinese (Simplified) Language Pack:一款用于 IntelliJ IDEA 的插件,它提供了简体中文的语言支持。
    • File > New Projects Setup > Settings for New Projects… > Plugins中搜索Chinese (Simplified),点击install安装即可享用友好的中文界面。
  2. google-java-format 是一个为 IntelliJ IDEA 设计的插件,它基于 Google 的 Java 编程风格指南来格式化 Java 代码。这个插件的主要目的是自动化地调整代码的格式(将接管IDEA的部分格式化程序),以符合 Google 的严格编码标准,帮助维护代码的一致性和可读性。
8.6.2 安装google-java-format(install google-java-format)

流程如下:

  • File > New Projects Setup > Settings for New Projects… > Plugins中搜索google-java-format,点击install安装。
  • 安装后请重启IDEA。
  • 默认情况下,该插件将被禁用。要在新项目中默认启用它,请访问File > Other Settings > DefaultSettings…,选中Enable google-java-format复选框,然后点击 [Apply]。(当您第一次打开一个为您提供此服务的项目时,显示一条通知)。
  • google-java-format 插件使用一些内部类,这些类在没有额外配置的情况下不可用。需要使用Help > EditCustom VM Options…,将下面几行设置追加到配置文件中(请小心操作、避免保存为乱码):
--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
  • 完成设置后重启IDEA,google-java-format将接管代码格式化程序(Ctrl+Alt+L)。

提示:若安装出现问题,可访问插件主页:google-java-format

注意:除了“Ctrl+Alt+L”之外,“Ctrl+Alt+O”也很重要,因为“Ctrl+Alt+L”通常不能将import语句成功分组并按字典排序。

注意:无论是IDEA自带的格式化程序还是google-java-format插件,目前都不能完全地践行Google Java Style。可以选择使用单独IDEA自带的格式化程序或结合google-java-format插件,请酌情使用。

  • 17
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值