Java & Android代码规范指南

本文为Google Java 风格指南的汉语翻译。无论你是个人开发还是团队协同开发,一个良好的代码规范,能够在项目当中发挥举足轻重的作用;它不仅能使你们的开发更加高效,而且还会减少BUG产生的几率,增强代码可维护性及稳定性。

遇到不理解的地方,一定要多看看谷歌的Java代码规范官方原文。多思考才能慢慢进步。

1 介绍

本文档是指导使用 Java 编写源代码的完全定义。 一个 Java 源代码文件只有严格遵守这里的规则,才能被称作为 Google 风格

就像其他的编码风格指南,这些问题的覆盖空间不仅涵盖格式化的美学问题,还有一些传统和代码标准。本文档主要着重于通用的 hard-and-fast(确定不可改变的,不可避免,不可忽视) 规则,尽量避免一些非人性化的建议,无论是对人还是工具而言。

1.1 术语说明

在这个文档中,除非有其他的声明:

  1. 类是指普通的类、枚举类、接口和注解类型(@Interface)。
  2. 类中的成员可以是成员变量、构造器、方法、内部类,就是类中比较顶层的内容。
  3. 注释总是指具体实现的注释。使用 Javadoc 指代文档注释。

1.2 指南注释

本文档中的代码片段只是实现本指南的一种方式,不应该用来直接作为规范。

2 源文件基础

2.1 文件名

源文件名由大写敏感的顶层类名和文件扩展 .java组成。

2.2 编码格式

源文件使用UTF-8编码格式。

2.3 特殊字符

2.3.1 空白字符

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

  1. 所有其它字符串中的空白字符都要进行转义。
  2. 制表符不用于缩进。

2.3.2 特殊转义序列

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

2.3.3 非ASCII字符

对于剩余的非 ASCII 字符,是使用实际的 Unicode 字符(例如, ),还是使用等价的 Unicode 转义符(例如,\u221e),取决于哪个能让代码更易于阅读和理解。

在使用 Unicode 转义符或是一些实际的 Unicode 字符时,建议做些注释给出解释,这有助于别人阅读和理解。

Example Discussion
String unitAbbrev = “μs”; Best: perfectly clear even without a comment.
String unitAbbrev = “\u03bcs”; // “μs” Allowed, but there’s no reason to do this.
String unitAbbrev = “\u03bcs”; // Greek letter mu, “s” Allowed, but awkward and prone to mistakes.
String unitAbbrev = “\u03bcs”; Poor: the reader has no idea what this is.
return ‘\ufeff’ + content; // byte order mark Good: use escapes for non-printable characters, and comment if necessary.

永远不要因为害怕某些程序可能无法正确处理非 ASCII 字符而让你的代码可读性变差。当程序无法正确处理非ASCII字符时,自然无法正确运行, 然后必须去解决这些问题就好了。

3 源文件结构

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

  1. 许可证或版权信息(如有)
  2. Package 语句
  3. Import 语句
  4. 有且仅有一个顶级

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

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

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

3.2 package语句

Package 语句不会自动换行,列限制(4.4节,列限制:100 )并不适用于 Package 语句(即 Package 语句写在一行里)。

3.3 import语句

3.3.1 import不要使用通配符

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

3.3.2 不要换行

Import 语句不换行,列限制(4.4节,列限制:100 )并不适用于 Import 语句(即 Import 语句写在一行里)。

3.3.3 顺序和间距

Import 语句按照以下规则排序:

  1. 所有静态导入独立成块
  2. 所有非静态导入独立成块

如果静态块和非静态块同时存在,用一个空行分隔。Import语句之间不会有多余的空行。

每个导入块之间的排列顺序按照 ASCII 排序。

3.3.4 不要使用静态导入的类

静态导入不适用静态内部类。他们要使用正常导入。

3.4 类声明

3.4.1 只有一个顶级类声明

每个顶级类都在一个与它同名的源文件中(当然,还包含.java后缀)。
例外,package-info.java,该文件中可没有package-info类。

3.4.2 类成员顺序

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

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

3.4.2.1 重载:永不分离

当一个类有多个构造函数,或是多个同名方法,这些函数/方法应该按顺序出现在一起,中间不要放进其它函数/方法。不受修饰符的影响。

4 格式化

术语说明:块状结构指的是一个类,方法或构造函数的主体。需要注意的是,数组初始化中的初始值可被选择性地视为块状结构(4.8.3.1节)。

4.1 大括号

4.1.1 使用大括号(即使是可选的)

大括号与 if, else, for, do, while 语句一起使用,即使只有一条语句(或是空),也应该把大括号写上。

其他的可选择大括号,例如 lambda 表达式中,保留可选择性。

4.1.2 非空块:K & R 风格

对于非空块和块状结构,大括号遵循 KernighanRitchie 风格(Egyptian brackets):

  1. 左大括号前不换行
  2. 左大括号后换行
  3. 右大括号前换行
  4. 如果右大括号是一个语句、函数体或类的终止,则右大括号后换行; 否则不换行。例如,如果右大括号后面是else或逗号,则不换行。 例如,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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节给出了enum类的一些例外。

4.1.3 空块:可以用简洁版本

一个空的块状结构里什么也不包含,大括号可以简洁地写成{},不需要换行。例外:如果它是一个多块语句的一部分(例如,if/elsetry/catch/finally),即使大括号内没内容,右大括号也要换行。 例如,

1
2
3
4
5
6
// This is acceptable
void doNothing() {}

// This is equally acceptable
void doNothingElse() {
}
1
2
3
4
// This is not acceptable: No concise empty blocks in a multi-block statement
try {
doSomething();
} catch (Exception e) {}

4.2 块缩进:2个空格

每当开始一个新的块,缩进增加2个空格,当块结束时,缩进返回先前的缩进级别。缩进级别适用于代码和注释。(见4.1.2节中的代码示例)

4.3 一行一个语句

每个语句后要换行。

4.4 列限制:100

一个项目可以选择一行100个字符的列限制,除了下述例外,任何一行如果超过这个字符数限制,必须自动换行。
例外:

  1. 不可能满足列限制的行(例如,Javadoc 中的一个长 URL,或是一个长的 JSNI 方法参考)。
  2. PackageImport 语句(见3.2节3.3节)。
  3. 注释中那些可能被剪切并粘贴到 shell 中的命令行。
  4. 非常长的标识符。

4.5 自动换行

术语说明:代码否则已经要合法的占据一整行的情况下,这段代码就会被分成多行。这个活动称之为自动换行(line-wrapping)。
我们并没有全面,确定性的准则来决定在每一种情况下如何自动换行。很多时候,对于同一段代码会有好几种有效的自动换行方式。

提取方法或局部变量可以在不换行的情况下解决代码过长的问题(是合理缩短命名长度吧)

4.5.1 从哪里断开

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

  1. 如果在非赋值运算符处断开,那么在该符号前断开(比如+,它将位于下一行)。注意:这一点与Google其它语言的编程风格不同(如C++和JavaScript)。 这条规则也适用于以下“类运算符”符号:

    1. 点分隔符(.
    2. 方法引用(::
    3. 类型界限中的 &<T extends Foo & Bar>
    4. catch 块中的管道符号(catch (FooException | BarException e)
  2. 如果在赋值运算符处断开,通常的做法是在该符号后断开(比如=,它与前面的内容留在同一行)。

    1. 这条规则也适用于foreach语句中的分号。
  3. 方法名或构造函数名与左括号留在同一行。

  4. 逗号(,)与其前面的内容留在同一行。

  5. lambda 箭头后不允许换行,除非后面跟着一个单语句。例如,

    1
    2
    3
    4
    5
    6
    7
    MyLambda<String, Long, Object> lambda =
    (String label, Long value, Object obj) -> {
    ...
    };

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

4.5.2 自动换行时缩进至少+4个空格

自动换行时,第一行后的每一行至少比第一行多缩进4个空格(注意:制表符不用于缩进。见2.3.1节)。
当存在连续自动换行时,缩进可能会多缩进不只4个空格(语法元素存在多级时)。一般而言,两个连续行使用相同的缩进当且仅当它们开始于同级语法元素。
4.6.3节水平对齐一节中指出,不鼓励使用可变数目的空格来对齐前面行的符号。

4.6 空白

4.6.1 垂直空白

以下情况需要使用一个空行:

  1. 类内连续的成员之间:字段,构造函数,方法,嵌套类,静态初始化块,实例初始化块。
    • 例外:两个连续字段之间的空行是可选的,用于字段的空行主要用来对字段进行逻辑分组。
    • 例外:枚举常量之间的空行见4.8.1节
  2. 要满足本文档中其他节的空行要求(比如,3节源码文件结构3.3节import语句)

一个单独的空行可以出现在任何地方,只要它能够改善源码的可读性,例如,在语句中间加空行整理代码,让代码变成逻辑上的子集合。一个空行出现在第一个成员或者初始化器之前,或者在最后一个成员或者初始化器之后,既不鼓励也不抨击这样的行为。

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

4.6.2 水平空白

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

  1. 分隔任何保留字与紧随其后的左括号((),比如,iffor,或者 catch

  2. 分隔任何保留字与其前面的右大括号(}),比如,elsecatch

  3. 在任何左大括号前({),两个例外:

    • @SomeAnnotation({a, b})(不使用空格)
    • String[][] x = {{"foo"}};(括号间没有空格,见下面的)
  4. 在任何二元或三元运算符的两侧。这也适用于以下“类运算符”符号:

    • 类型界限连接中的 &<T extends Foo & Bar>
    • catch块中的管道符号| catch (FooException | BarException e)
    • 增强 for 语句(foreach)语句中的分号(:)。
    • lambda 表达式中的箭头符号:(String str) -> str.length()

    不适用于:

    • 双引号(::)的方法引用,写法例如: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编程风格对此不做要求。即使对于已经使用水平对齐的代码,我们也不需要去保持这种风格。
以下示例先展示未对齐的代码,然后是对齐的代码:

1
2
3
4
5
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

对齐可增加代码可读性,但它为日后的维护带来问题。考虑未来某个时候,我们需要修改一堆对齐的代码中的一行。 这可能导致原本很漂亮的对齐代码变得错位。很可能它会提示你调整周围代码的空白来使这一堆代码重新水平对齐(比如程序员想保持这种水平对齐的风格), 这就会让你做许多的无用功,增加了reviewer的工作并且可能导致更多的合并冲突。

4.7 用小括号来限定组:推荐

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

4.8 特定结构

4.8.1 枚举类

枚举常量间用逗号隔开,换行可选。额外的空行(通常只有一个)也是被允许的。例如:

1
2
3
4
5
6
7
8
9
10
private enum Answer {
YES {
@Override public String toString() {
return "yes";
}
},

NO,
MAYBE
}

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

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 数组初始化:可写成块状结构

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
new int[] {
0, 1, 2, 3
}

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

new int[] {
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 标签(case FOO:default:),后面跟着一条或多条语句。

4.8.4.1 缩进

与其它块状结构一致,switch 块中的内容缩进为2个空格。 每个 switch 标签后新起一行,再缩进2个空格,写下一条或多条语句。

4.8.4.2 Fall-through:注释

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

1
2
3
4
5
6
7
8
9
10
11
switch (input) {
case 1:
case 2:
prepareOneOrTwo();
// fall through
case 3:
handleOneTwoOrThree();
break;
default:
handleLargeNumber(input);
}

注意一下,case 1 后面不需要注释,只有在每个语句组的结尾才需要。

4.8.4.3 default 存在的情况

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

例外情况:枚举类型的 switch 语句可以省略 default 语句组,如果已经详细列出了它的所有可能。这将保证 IDE 代码检测工具不会报警报。

4.8.5 注解

4.8.5.1 类型使用注解

类型使用立即出现在被注解的类型之前。一个注解是类型使用的是指它的元注解是 @Target(ElementType.TYPE_USE)。例如:

1
2
3
final @Nullable String name;

public @Nullable Person getPersonByName(String name);

4.8.5.2 类注解

类注解立即出现在文档块之后,每一个注解都单独成行,也就是注解行。这些换行不属于自动换行(4.5节Line-Wrapping),所以缩进等级不会提高。例如:

1
2
3
@Deprecated
@CheckReturnValue
public final class Frozzler { ... }

4.8.5.3 方法和构造器注解

使用方法同上节。例如:

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

例外情况:单个无参数的注解可以和签名的第一行出现在一起。例如:

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

4.8.5.4 字段注解

应用于字段的注解紧随文档块出现,应用于字段的多个注解允许与字段出现在同一行。例如:

1
@Partial @Mock DataLoader loader;

4.8.5.5 参数和局部变量的注解

参数和局部变量注解没有特定规则(除非这个注解是类型使用注解)。

4.8.6 注释

本节重点在于代码注释的实现。Javadoc 相关见7节Javadoc

4.8.6.1 块注释风格

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

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

不能再注释里面写注释。

小贴士:多行注释使用 /* ... */ 风格,这样格式化的时候会自动换行。大多数的格式化插件下,单行注释 // ... 块不会自动换行。

4.8.7 Modifiers

类和成员的 **modifiers如果存在,则按Java语言规范中推荐的顺序出现。

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

4.8.8 数字字义

long 值的整型字义使用大写的 L 后缀,永远不要使用小写 l。例如,300000000000L 而不是 300000000000l

5 命名

5.1 对所有标识符都通用的规则

标识符只能使用ASCII字母和数字,在如下极少数情况下使用下划线。因此每个有效的标识符名称都能匹配正则表达式\w+。
Google 风格中,特殊前缀和后缀不再使用。例如,这些命名都不是 Google 风格:name_, mName(这个Android 成员变量好像一直在用233), s_namekName

5.2 标识符类型的规则

5.2.1 包名

包名全部小写,连续的单词只是简单地连接起来,不使用下划线。例如:com.example.deepspace, 不是 com.example.deepSpac 或者 com.example.deep_space

5.2.2 类名

类名都以 UpperCamelCase 风格编写。
类名通常是名词或名词短语。例如:Character 或者 ImmutableList 。接口名可以也是名词或名词性短语;例如:List;但有时可能是形容词或形容词短语,例如:Readable

现在还没有特定的规则或行之有效的约定来命名注解类型。
测试类名以 Test 结束,例如,HashIntegrationTest。如果要测试的是一个单类,命名就要用单类名加上 Test,例如,HashImplTest

5.2.3 方法名

方法名使用 lowerCamelCase 风格编写。
方法名通常是动词或动词短语。例如:sendMessage 或者 stop
下划线可能出现在 JUnit 测试方法名称中用以分隔名称的逻辑组件,其中每个组件都要使用 lowerCamelCase,例如,transforMoney_deductsFromSource 。测试方法命名不止 一种正确方式。

5.2.4 常量名

常量名命名使用 UPPER_SNAKE_CASE:全部字母大写,用下划线分隔单词。那,到底什么算是一个常量?
常量都是 静态 final 字段,其内容都是深度不可变的,其包含的方法不会有任何可检测的副作用。例如:primitives,字符串,值不可变的类,或者是任何设置成 null 的变量。如果任何一个实例的可观测状态是可变的,那它就不是一个常量。仅仅只是意图永远不去改变一个对象是不够的。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 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 Logger logger = Logger.getLogger(MyClass.getName());
static final String[] nonEmptyArray = {"these", "can", "change"};

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

5.2.5 非常量字段名

非常量字段名(静态或其他)使用 lowerCamelCase 风格编写。
这些名字通常是名词或名词短语。例如,computeValues 或者 index

5.2.6 参数名

参数名使用 lowerCamelCase 风格编写。
参数应该避免用单个字符命名。

5.2.7 局部变量名

局部变量名使用 lowerCamelCase 风格编写,比起其它类型的名称,局部变量名可以有更为宽松的缩写。
即使局部变量是final和不可改变的,也不应该把它示为常量,自然也不能用常量的规则去命名它。

5.2.8 类型变量名

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

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

5.3 驼峰式命名法(CamelCase)

驼峰式命名法分大驼峰式命名法(UpperCamelCase)和小驼峰式命名法(lowerCamelCase)。 有时,有不只一种合理的方式将一个英语词组转换成驼峰形式,如缩略语或不寻常的结构,例如,IPv6iOS。为了改善可预测性,Google 风格指定了以下近乎确定的方案。

名字从散文形式(prose form)开始:

  1. 把短语转换为纯ASCII码,并且移除任何所有格符号。例如,"Müller’s algorithm 将变成 Muellers algorithm

  2. 把这个结果切分成单词,在空格或其它标点符号(通常是连字符)处分割开。

    • 推荐:如果某个单词已经有了常用的驼峰表示形式,按它的组成将它分割开(如 AdWords 将分割成 ad words)。 需要注意的是 iOS 并不是一个真正的驼峰表示形式,因此该推荐对它并不适用。
  3. 现在将所有字母都小写(包括缩写),然后将单词的第一个字母大写:

    • 每个单词的第一个字母都大写,来得到大驼峰式命名。

    • 除了第一个单词,每个单词的第一个字母都大写,来得到小驼峰式命名。

  4. 最后将所有的单词连接起来得到一个标识符。

例如:

Prose form Correct Incorrect
“XML HTTP request” XmlHttpRequest XMLHTTPRequest
“new customer ID” newCustomerId newCustomerID
“inner stopwatch” innerStopwatch innerStopWatch
“supports IPv6 on iOS?” supportsIpv6OnIos supportsIPv6OnIOS
YouTube importer” YouTubeImporter
YoutubeImporter*

加星号处表示可以接受,但不推荐。

小贴士:有些单词在英语中被隐式的连字符连接起来。例如,nonemptynon-empty 都是正确的,所以方法名 checkNonemptycheckNonEmpty 两者同样都是正确的。

6 编程实践

6.1 @Override:总是使用

只要是合法的,就把@Override注解给用上。包括负载的父类方法,实现的接口方法,接口中继承父类接口的方法。

例外:@override 在父类方法被标注为 @Deprecated 时是可以被忽略不写的。

6.2 捕获的异常:不能忽视

除了下面的例子,对捕获的异常不做响应是极少正确的。(典型的响应方式是打印日志,或者如果它被认为是不可能的,则把它当作一个 AssertionError 重新抛出。) 如果它确实是不需要在catch块中做任何响应,需要做注释加以说明(如下面的例子)。

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);

例外:在测试中,如果一个捕获的异常被命名为 expected,则它可以被不加注释地忽略。下面是一种非常常见的情形,用以确保所测试的方法会抛出一个期望中的异常, 因此在这里就没有必要加注释。

1
2
3
4
5
try {
emptyStack.pop();
fail();
} catch (NoSuchElementException expected) {
}

6.3 静态成员:使用类授权调用

如果一个静态类的引用一定要授权,那么使用类名授权,即使用类名调用静态的类成员,而不是具体某个对象或返回对象的表达式。

1
2
3
4
Foo aFoo = ...;
Foo.aStaticMethod(); // good
aFoo.aStaticMethod(); // bad
somethingThatYieldsAFoo().aStaticMethod(); // very bad

6.4 Finalizers: 禁用

极少会去重载 Object.finalize

不要使用 finalize。如果你非要使用它,请先仔细阅读和理解 Effective Java 第7条款:Avoid Finalizers,然后不要使用它。

7 Javadoc

7.1 格式

7.1.1 一般形式

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

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

或者是以下单行形式:

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

基本格式总是能接受的。当整个 Javadoc 块能容纳于一行时(包括注释标注),可以使用单行形式。注意,这仅仅适用于没有 Javadoc 标记 @XXX 的情况,例如: @return

7.1.2 段落

空行(即只包含最左侧星号(*)的行)会出现在段落之间和 Javadoc 标记( @XXX )(如果有的话)之前。 除了第一个段落,每个段落第一个单词前都有标签 <p>,并且它和第一个单词间没有空格。其他块级元素的 HTML 标签前面不用加 <p>, 例如:<ul> 或者 <table>

7.1.3 块标签

标准的块标签按以下顺序出现:@param, @return, @throws, @deprecated, 前面这4种标记如果出现,描述都不能为空。 当描述无法在一行中容纳,连续行需要至少在 @ 再缩进4个空格。e.g.:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* Called when the activity is starting. This is where most initialization
* should go: calling {@link #setContentView(int)} to inflate the
* activity's UI, using {@link #findViewById} to programmatically interact
* with widgets in the UI, calling
* {@link #managedQuery(android.net.Uri , String[], String, String[], String)} to retrieve
* cursors for data being displayed, etc.
*
* <p>You can call {@link #finish} from within this function, in
* which case onDestroy() will be immediately called after {@link #onCreate} without any of the
* rest of the activity lifecycle ({@link #onStart}, {@link #onResume}, {@link #onPause}, etc)
* executing.
*
* <p><em>Derived classes must call through to the super class's
* implementation of this method. If they do not, an exception will be
* thrown.</em></p>
*
* @param savedInstanceState If the activity is being re-initialized after
* previously being shut down then this Bundle contains the data it most
* recently supplied in {@link #onSaveInstanceState}. <b><i>Note: Otherwise it is null.</i></b>
*
* @see #onStart
* @see #onSaveInstanceState
* @see #onRestoreInstanceState
* @see #onPostCreate
*/

7.2 摘要片段

每个 Javadoc 块以一个简短的摘要片段开始。这个片段是非常重要的,在某些情况下,比如在类和方法索引中,它是唯一出现的文本。

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

小贴士:一个常见的错误是把简单的Javadoc写成 /** @return the customer ID */,这是不正确的。它应该写成 /** Returns the customer ID. */

7.3 哪里需要使用Javadoc

至少在每个 public 类及它的每个 publicprotected 成员上使用Javadoc,以下是一些例外:

额外的 Javadoc 内容也可以展示,7.3.3 章节会解释。

7.3.1 例外:不言自明的方法

对于简单明显的成员如 getFoo()Javadoc 是可选的。这种情况下除了写 Returns the foo,确实也没有什么值得写了。 单元测试类中的测试方法可能是不言自明的最常见例子了,我们通常可以从这些方法的描述性命名中知道它是干什么的,因此不需要额外的文档说明。

重要小贴士:如果有一些相关信息是需要读者了解的,那么以上的例外不应作为忽视这些信息的理由。例如,对于方法名getCanonicalName, 就不应该忽视文档说明,因为读者很可能不知道词语canonical name指的是什么。

7.3.2 例外:重载

如果一个方法重载了超类中的方法,那么 Javadoc 并非必需的。

7.3.3 非必需Javadoc

其他的类或者成员已经有了需要的 Javadoc

非必需 Javadoc 不需要严格遵守 7.1.1 7.1.2 7.1.37.2 的格式化规则,尽管我们的建议还是按照规则来做。

8 Android部分

8.1 代码命名规则

  1. Activity 命名一律使用 功能名+Activity 的方式。例如:LoginActivitySignupActivity

  2. Fragment 命名一律使用 功能名+Fragment 的方式。例如:HomeFragmentMineFragment

  3. 自定义View:**Custom(建议)+功能名+View/ViewGroup(具体的组件名称)**。例如:CustomImageScrollerCustomRatingBar

  4. Widget 命名一律使用 功能名 + Widget。例如: ScanWidgetWeatherWidget

  5. Dialog 对话框功能名+Dialog。例如:LoginDialog、ProgressDialog。

  6. 尽量在每一个Activity或类中加入TAG,方便我们查看Activity的信息

    Tip : 使用Android Studio提供的快捷键logt可快速生成当前 类的常量)。

  7. 对于使用Intent传递数据,声明一些 Key 的时候:

    EXTRA_KEY_+具体Key名称,例如我们现在有一个人的名字和年龄要传那么首先定义:

1
2
public static final String EXTRA_KEY_PERSON_NAME="EXTRA_KEY_PERSON_NAME"
public static final String EXTRA_KEY_PERSON_AGE="EXTRA_KEY_PERSON_AGE"

然后在具体的页面 new Intent(),依次传递进去值,这样写其实没什么问题;但是试想一下,如果你要调用的Activity是类似于一个工具性质或通用的 Activity(图片选择器、登录、注册等等),这时候你要传递的 Key 又很多,如果业务复杂的话,你应该会被这样冗余且不易阅读的代码直接搞崩溃掉。

所以最好的办法就是在你要调用Activity提供一个静态工厂方法,要知道静态工厂方法所带来的好处太多了,由于 Activity 是不允许通过 new 的方式来初始化的,所以静态工厂方法的好处在此就不那么明显,但是已经足够我们优化我们的代码了。举个例子,我们有一个笔记 NoteActivity,用于创建笔记和修改笔记,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//笔记Id
private static final String EXTRA_KEY_NOTE_ID ="EXTRA_KEY_NOTE_ID" ;
//笔记内容
private static final String EXTRA_KEY_NOTE_CONTENT ="EXTRA_KEY_NOTE_CONTENT" ;
//笔记模式
private static final String EXTRA_KEY_NOTE_MODE ="EXTRA_KEY_NOTE_MODE" ;

//用于创建笔记
public static void startForCreate(Context context, int noteId) {
start(context, noteId, null, MODE_CREATE);
}

//用于编辑笔记
public static void startForEdit(Context context, int noteId, String content) {
start(context, noteId, content, MODE_UPDATE);
}

public static void start(Context context, int noteId, String content, int mode) {
Intent starter = new Intent(context, TableShareListSettingActivity.class);
starter.putExtra(EXTRA_KEY_NOTE_ID,noteId);
starter.putExtra(EXTRA_KEY_NOTE_CONTENT,content);
starter.putExtra(EXTRA_KEY_NOTE_CONTENT,mode);
context.startActivity(starter);
}

通过以上方法,我们能够很好的解耦复杂的 Activity 之间的调用,再加上静态方法工厂方法名,代码可阅读行大大提高,最终我们看到的调用 NoteActivity 将会是很简洁的一段代码:

1
2
NoteActivity.startForCreate(this,noteId);
NoteActivity.startForEdit(this,noteId,content);

此外,Android Studio 工具中其实已经在 Live Template 中提供了这样的代码: CMD+J( For MAC OS) ,简单的输入starter就可以快速地在当前的Activity中添加一个 Intent 的静态操作方法,这其实也说明了 Android 官方团队也鼓励我们这么做。

  1. Activity 中变量采用m开头+类名。例如,mTablemPerson
  2. Activity中的控件:m + 控件类型名称缩写 + 模块名。例如,mEtLogin,mTvLogin;

8.2 资源 Res

按照资源的类型,分为以下几种:

8.2.1 控件Id命名

控件缩写 _模块(module) _功能名(function)

1
2
3
4
5
6
7
8
9
10
11
12
13
控件类型	        ID命名规则
TextView tv_module_function
EditText et_module_function
ImageView iv_module_function
Button btn_module_function
ListView lv_module_function
GridView gv_module_function
CheckBox check_module_function
RadioButton radio_module_function
LinearLayout ll_module_function
RelativeLayout rl_module_function
FrameLayout fl_module_function
GridLayout gl_module_function

8.2.2 Color资源命名

1
2
Resources Type	命名规则
color 组件名+模块名+具体作用名。例 R.color.login_button_text

8.2.3 String资源命名

1
2
Resources Type	命名规则
string 模块名+具体功能。 例 R.string.login_hello

8.2.4 Drawable资源命名

1
2
3
4
5
6
Resources Type	命名规则
launcher icon ic_launcher。例R.drawable.ic_launcher
normal icon 具体模块_ic_功能。例R.drawable.ic_audio_pause
Toolbar icon 具体模块_ic_tb_功能名。例如login_ic_tb_search
selector selector_模块_功能名。例如 selector_login_button
shape shape_模块功能名状态。例如 R.drawable.shape_login_button_normal

8.2.5 Layout资源命名

1
2
3
4
5
6
7
8
9
类型	命名规则
activity activity_功能名。例如 R.layout.activity_login
fragment fragment_功能名。例如 R.layout.fragment_login_layout_header
include layout_模块名_功能名。例如 @layout/layout_login_bottom
adapter adapter_item_模块名_功能名。例如 R.layout.adapter_item_simple_text
dialog dialog_模块_功能名。例如 R.layout.dialog_time_picker
list header header_模块_功能。例如 R.layout.header_login_top_ad
list footer footer_模块_功能。例如 R.layout.footer_login_bottom_action
widget widget_模块_功能。例如 R.layout.widget_login_clock

8.2.6 Menu资源命名

1
2
Resources Type	命名规则
menu menu_模块名。例如 menu_login

8.2.7 Values资源命名

1
2
3
4
5
Resources Type	命名规则
color 模块名_color。例如 material_design_color
dimens 模块名_dimens。例如 material_design_dimens
style 模块名_style。例如 material_design_style
themes 模块名_themes。例如 material_design_themes

参考资料