0%

Google Java Style Guide

不仅要写代码,还要写一手漂亮的代码

术语注释

  1. class(类),包括所有普通的类(class)、enum、interface或注解类型(@interface)

  2. member(成员),包括类中的内部类(静态内部类)、变量、方法、构造方法(除初始化块和注释)

  3. comment(注释),指实现时的解释性注释。对于文档注释,使用术语Javadoc

I. 源文件基础

  1. 所有源文件都应以UTF-8编码

  2. 关于空白符

    • 除行终止符和字面字符串外,源文件中出现的空白符只能是ascii码为10的水平空格符。这意味着不应使用tab符做缩进。
    • 使用转义字符( \b, \t, \n, \\ )等,而不是对应的八进制(如 \012 )/Unicode(如 \u000a)转义
  3. 对于非ascii字符,要么使用实际上的Unicode字符(如 ),要么使用对应的Unicode转义(如 \u221e )。
    建议使用前一种更方便理解的方法。当不得不使用Unicode转义时最好加上相应的注释

II. 源文件结构

每个源文件需要从上至下包含:

  1. License或Copywrite信息

  2. package声明

    package声明不换行

  3. import声明

    • import static外,其他import声明不使用*
    • import声明不换行
    • 所有import static声明位于第一个block,其他import声明位于第二个block,两个block间由一行空行分隔。每个block内的import声明按ascii顺序从上到下排列
    • 不使用import static来导入静态内部类,使用普通的import声明
  4. 唯一的顶层class

    关于class内容的排布:

    决定成员排列先后顺序时应注意遵循某些逻辑结构。它们的排布位置对于类的易学性有着很大影响。

    举个反例:将所有新建的方法都被添加到类的最后就不是一个好习惯,因为其破坏了类的逻辑结构。

    另外,所有的重载都应处于相邻位置。

每一部分间由一行空行分隔

III. 格式

  1. 关于大括号

    • if, else, do, while, for后必须跟大括号,即使该block为空或只有一行
    • 左括号:前不换行,后换行。右括号:前换行,当结束方法体、构造方法或一个命名类后才换行(如else前的右括号就不换行)
    • 当block为空时,右括号可以不换行直接紧接左括号闭合。除非该block为一个多block声明(如if else)的一部分
  2. 关于缩进:每级block前+2空格

  3. 每行一句statement

  4. 除特殊情况外,每行最多100个“字符”,超出部分需换行。一个“字符”等同于一个Unicode码位,意味着一个全角字符等同于两个“字符”

  5. 关于4中的换行

    • 换行位置的选择:尽量不要打破更高的句法层次。对于非赋值运算符,在运算符之前换行;对于赋值运算符(包括foreach中的:)、逗号,,在运算符之后换行。

    • 换行后,次行缩进在前一行的基础上+4空格

    • 当有多个连续行时,当且仅当两行的行首元素在句法上平行时,不需要额外的缩进

  6. 空行

    • 除其他提到的特殊情况外,单空行总是出现在类中连续的成员或初始化块间

    • 两个连续的成员变量间不要求一定出现单空行,当需要创建更清晰的逻辑结构时可以添加

    • 除以上规则外,在必要时,可以添加单空行提升代码可读性(如将代码分隔为多个逻辑块)

  7. 空格

    • 对于if, for, catch, else等关键字,在它们和括号(包括圆括号和大括号)间添加一个空格

    • 在所有左大括号{前添加一个空格,赋值操作除外(如String[][] x = {{"foo"}};

    • 所有二元/三元操作符的前后分别添加一个空格

    • 类型转换括号)后添加一个空格

    • 类型和变量声明间添加一个空格,如List<String> list

  8. 推荐在运算表达式中添加分组括号以更清晰地描述其含义。不要假定所有读代码的人都熟记运算符优先级表

  9. 特定结构

    • 枚举类
      每个枚举常量的分隔,后的换行是可选的。当枚举中类中没有声明方法,且枚举常量不含Javadoc时,可以选择使用初始化数组的格式来初始化枚举类。

    • 变量声明
      每次变量声明只指定一个变量。避免类似 int a, b; 的声明方式(for循环内除外)
      局部变量的声明应尽可能接近其初次使用位置,以最小化其作用域。避免在将所有局部变量全部声明在block的开头位置。
      局部变量的声明往往同时伴随着初始化。如果是较为复杂的初始化,相关代码应紧邻声明位置。

    • 数组
      数组声明允许被编写成block式(即在第一个大括号后换行)。
      不要使用C风格的数组声明。使用 String[] args 而不是 String args[]

    • swtich

      switch group的起始关键字case, default前使用+2空格缩进

      每个switch group内部再+2空格缩进

      每个switch group要么立刻结束(使用break, return, 抛出异常等),要么添加注释说明需要执行下一个switch group。通常只要标注即足够。本条规则不适用于最后一个switch group

      每个switch block中必须包含一个 default switch group,即使它不包含任何代码。
      (对于枚举类型的switch block,如果包含了覆盖到所有可能性的case,可以不添加default switch group)

    • 注解

      应用于方法构造器的注解紧邻文档block,且每个注解占据独立的一行。这一行不适用于上述的100字符换行规则

      1
      2
      3
      @Override
      @Nullable
      public String getNameIfPresent() { ... }

      应用于成员变量的注解紧邻文档block,但多个注解可能被列于同一行,注解间由一个空格分隔

      1
      @Partial @Mock DataLoader loader;

      对于方法参数、局部变量、类型的注解编写没有特别要求

    • 注释

      对于单行注释,// .../* ... */ 均可

      对于多行注释 /* ... */,随后的每行都应以一个和上一行对齐的 * 开头

      1
      2
      3
      4
      /*
      * This is // And so /* Or you can
      * okay. // is this. * even do this. */
      */

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

    • 类与成员的修饰关键字

      如出现,按照Java语言规范建议的顺序显示:

      1
      public protected private abstract default static final transient volatile synchronized native strictfp
    • 数字字面量

      声明 long 字面量时,使用 L 后缀而不是 l,避免与 1 混淆

III. 命名

  1. 适用于所有标识符的通用规则

    标识符只能使用ascii字母,数字和下划线(在小部分情况下)。以便于每个有效的标识符名可以被正则表达式的一个\w+匹配。

    Google不使用特殊前缀、后缀。如 name_, mName, s_name 等不是Google Style

  2. 按标识符类型

    • 包名

      使用全小写字母,连续的单词直接相连。如使用 com.example.deepspace 而不是 com.example.deepSpacecom.example.deep_space

    • 类名

      使用大写开头的驼峰(UpperCamelCase

      类名通常为名词或名词词组,如 CharacterImmutableList

      接口名词性规则通常和类名一致,但也可以是形容词或形容词词组,如 Readable

      测试类名通常以它们所测试的类名开头,以 Test 结束,如 HashTest, HashIntegrationTest

      注解类型没有明确的命名规范

    • 方法名

      使用小写开头的驼峰(lowerCamelCase

      方法名通常为动词或动词词组,如 sendMessagestop

      下划线可以出现在JUnit的测试方法中,用于分隔方法名的逻辑部分,两部分分别使用驼峰。一种典型的模式是<methodUnderTest>_<state> ,例如 pop_emptyStack

    • 常量名

      使用下划线分隔的全大写(CONSTANT_CASE

      常量名通常为名词或名词词组

      常量指那些被 static final 修饰,且内容完全不可变,自身方法不会改变自身状态的成员变量。包括所有的原始数据类型、String、不可变类型、以及不可变类型的不可变集合。

      如果某个实例的可观测状态可以改变,则其不是一个常量

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      // 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

      避免在 public 修饰的方法中使用单字符的参数名

    • 局部变量名

      使用小写开头的驼峰(lowerCamelCase

      即使它们是 final 或不可变的,也不要将它们视作常量,或使用常量的命名风格

    • 类型变量名

      可以使用下列两种风格之一

      1. 一个唯一的大写字母,可后跟一个单一的数字,如 T , E, T2
      2. 类名风格后跟一个大写字母 T,如 RequestT, FooBarT
  3. 驼峰标准

    使用下述步骤将一个英语词组转换为驼峰

    1. 去除词组中的所有 ' 号,并将其全转换为ascii形式。如 Müller’s algorithm 应该被转换为 Mullers algorithm
    2. 将第一步的结果拆分成独立的多个单词。如果结果中已经有驼峰部分,推荐将其进一步拆分成单词。如 AdWords 可以被进一步拆分成 ad words
    3. 将所有字母变为小写(包括缩略词),并将每个单词(UpperCamelCase)或除首单词外的每个单词(lowerCamelCase)的第一个字母大写
    4. 连接所有单词,形成最后的驼峰表示

    应用上述的规范化流程可以解决一些专有名词大写导致的不一致。

    XML HTTP request 最后应写成 XmlHttpRequest 而不是 XMLHTTPRequest

IV. 编码实践

  1. 除重写 @Deprecated 方法外,总是为重写的方法添加 @Override 注解

  2. 除少数例外情况,对所有被 catch 的Exception都应做出相应响应(通常是记录一条log,或将其向外抛出)

    • 例外1:如确实存在某些不需要处理的情况,需要在 catch block 中添加一条解释性注释

      1
      2
      3
      4
      5
      6
      7
      try {
      int i = Integer.parseInt(response);
      return handleNumericResponse(i);
      } catch (NumberFormatException ok) {
      // it's not numeric; that's fine, just continue
      }
      return handleTextResponse(response);
    • 例外2:在某些测试用例中,若 catch 到的Exception变量名以 expected 起始,则可以不添加注释

      1
      2
      3
      4
      5
      try {
      emptyStack.pop();
      fail();
      } catch (NoSuchElementException expected) {
      }
  3. 通过类而不是对象来使用静态成员

  4. 避免使用 Finalizer, 即避免重写 Object.finalize 方法

V. Javadoc

  1. 格式

    基本格式如下

    1
    2
    3
    4
    5
    6
    7
    8
    /**
    * Multiple lines of Javadoc text are written here,
    * wrapped normally...
    */
    public int method(String p1) { ... }

    /** An especially short bit of Javadoc. */
    public int anotherMethod(String p1) { ... }

    当存在空行时,下段段首包含一个 <p> ,后不接空格。如:

    1
    2
    3
    4
    5
    6
    7
    8
    /**
    * First paragraph ...
    *
    * <p>Second paragraph ...
    *
    * <p>Third paragraph
    */
    public int method(String p1) { ... }

    所有的标准 @ tag按 @param, @return, @throws, @deprecated 的顺序出现。同时,它们的描述不能为空。

    若某个tag的内容无法包含在一行内,下一行起始位置为本行 @ 对齐位置+4空格

  2. 摘要段

    位于所有Javadoc的开始段,用于简要描述当前被注释内容的作用。

    通常为一个名词词组或动词词组,不是一个完整的句子。

    1
    2
    3
    4
    5
    // Correct
    /** Returns the customer ID. */

    // Incorrect
    /** This method returns ... */
  3. 在何处使用Javadoc

    除一些例外情况,至少为类中的所有的 public 方法、所有 protected , public 成员添加Javadoc

    • 例外1:明显可自解释的方法可以不添加Javadoc,如Getter和Setter。

      并不是所有 Getter / Setter 都可以省略Javadoc。例如若某个Getter名为 getCanonicalName 。Javadoc不能仅为一句 /** Returns the canonical name. */ ,更不要省略。因为读者可能无法理解 “Canonical Name” 的含义

    • 例外2:重写的方法允许不添加Javadoc

    对于其他不做强制要求的部分,可以不严格遵循上述 1. 中的Javadoc格式。

    另外,对于某些定义类或成员的总体目的或行为的实现注释,使用短Javadoc格式,即 /**

Reference

Google Java Style Guide

New Programming Jargon