Apache Log4j 代码样式准则
Introduction
本文档是 Java™编程语言中 Log4j 项目的源代码编码标准的“完整”定义。它起源于 Google 编码标准,但结合了反映 Log4j 社区需求的修改。
与其他编程风格指南一样,这些问题不仅涵盖格式方面的美学问题,而且还涵盖其他类型的约定或编码标准。但是,本文档主要侧重于我们普遍遵循的“严格规则” **,并避免提供无法明确执行的建议(无论是通过人工还是通过工具)。
Terminology notes
在本文档中,除非另有说明:
-
术语类包括在内,表示“普通”类,枚举类,接口或 Comments 类型(@interface)。
-
术语 Comments 始终指实施 Comments。我们不使用短语“文档 Comments”,而是使用通用术语“ Javadoc”。
其他“术语 Comments”有时会出现在整个文档中。
Guide notes
本文档中的示例代码是 non-normative 。也就是说,尽管这些示例采用的是 Log4j 样式,但它们可能无法说明表示代码的唯一时尚方式。在示例中做出的可选格式选择不应作为规则执行。
源文件基础
File name
源文件名由它包含的顶级类的区分大小写的名称以及.javaextensions 组成。
2.2 文件编码:UTF-8
源文件以 UTF-8 编码。
Special characters
Whitespace characters
除了行终止符序列之外,ASCII 水平空格字符 ( 0x20 **)是唯一出现在源文件中任何地方的空格字符。这意味着:
-
字符串和字符 Literals 中的所有其他空白字符均被转义。
-
制表符“不”用于缩进。
特殊转义序列
对于具有特殊转义序列( b, t, n, f, r,“,'和)的任何字符,将使用该序列而不是相应的八进制字符(例如 012)或 Unicode(例如 u000a)转义。
Non-ASCII characters
对于其余的非 ASCII 字符,使用实际的 Unicode 字符(例如∞)或等效的 Unicode 转义(例如 u221e),这仅取决于使代码 更易于阅读和理解 。
提示: 在 Unicode 转义的情况下,有时甚至使用实际的 Unicode 字符时,解释性 Comments 也会非常有帮助。
Examples:
Example | Discussion |
---|---|
字符串 unitAbbrev =“μs”; | 最佳:即使没有 Comment,也非常清晰。 |
字符串 unitAbbrev =“\u03bcs”; //“μs” | 允许,但是没有理由这样做。 |
字符串 unitAbbrev =“\u03bcs”; //希腊字母 mu,“ s” | 允许,但笨拙且容易出错。 |
字符串 unitAbbrev =“\u03bcs”; | 差:Reader 不知道这是什么。 |
返回' ufeff'内容; //字节 Sequences 标记 | 很好:对不能打印的字符使用转义符,并在必要时进行 Comments。 |
提示: 不要因为担心某些程序可能无法正确处理非 ASCII 字符而使代码的可读性降低。如果发生这种情况,这些程序将被“破坏”,并且必须被“修复”。
源文件结构
源文件由 按 Sequences 组成:
-
Apache license
-
Package statement
-
Import statements
-
一门顶级类
恰好有一个空白行 分隔出现的每个部分。
Apache License
Apache 许可证属于此处。不应出现其他许可证。其他适用的许可证应在“公告”文件中引用
Package statement
package 语句是“非换行的”。列限制(栏数上限:120)不适用于 package 语句。
Import statements
主树中没有通配符导入
通配符导入 ,静态或其他方式, 不使用 。
在测试树中导入静态通配符
对于诸如 JUnit,EasyMock 和 Hamcrest 之类的测试导入,建议使用通配符静态导入 。
No line-wrapping
导入语句是“非换行的”。列限制(栏数上限:120)不适用于导入语句。
Order 和间距
导入语句按此 Sequences 分为以下几组,每组之间用一个空白行分隔:
-
java
-
javax
-
org
-
com
-
单个组中的所有静态导入
在一个组中没有空行,并且导入的名称以 ASCII 排序 Sequences 显示。 ( 注意: 这与 ASCII 排序 Sequences 中的 import 语句不同;分号的出现会使结果扭曲.)
可在源代码发行版的 src/ide 下找到用于自动 Order 导入的 IDE 设置。例如:
-
Eclipse: src/ide/eclipse/4.3.2/organize-imports.importorder
-
IntelliJ: src/ide/Intellij/13/IntellijSettings.jar
Class declaration
只是一个顶级类声明
每个顶级类都驻留在其自己的源文件中。
类成员 Order
类成员应按以下 Sequences 分组。
-
静态变量按如下所示的 Sequences 分组。组内的变量可以按任何 Sequences 出现。
-
-
public
-
protected
-
package
-
private
-
-
实例变量按如下所示的 Sequences 分组。组内的变量可以按任何 Sequences 出现
-
-
public
-
protected
-
package
-
private
-
-
constructors
-
方法可以按以下 Sequences 指定,但是如果可以提高程序的清晰度,则可以按其他 Sequences 出现。
-
-
public
-
protected
-
package
-
private
-
超载:永不拆分
当一个类具有多个构造函数或多个具有相同名称的方法时,这些将按 Sequences 显示,并且没有中间成员。
Formatting
术语注: 块状构造是指类,方法或构造函数的主体。请注意,通过array initializers,可以选择将任何数组初始值设定项视为类似于块的构造。
Braces
大括号用于可选的位置
括号与 if,else,for,do 和 while 语句一起使用,即使主体为空或仅包含单个语句也是如此。
非空块:K 和 R 样式
大括号遵循 Kernighan 和 Ritchie 样式(“ Egyptian brackets”)用于非空块和类似块的构造:
-
开括号之前没有换行。
-
开括号后换行。
-
右括号前的换行符。
-
如果右括号终止一个语句或方法,构造函数或命名类的主体,则在右括号后换行。例如,如果大括号后跟着 else 或逗号,则没有换行符。
Example:
return new MyClass() {
@Override public void method() {
if (condition()) {
try {
something();
} catch (ProblemException e) {
recover();
}
}
}
};
枚举类的一些 exceptions 在第 4.8.1 节Enum classes中给出。
空块:可能很简洁
空的块或类似块的构造在打开后可以立即关闭,中间没有任何字符或换行符( {}), 除非 它是多块语句的一部分(一个直接包含多个区块:if/else-if/else 或 try/catch/finally)。
Example:
void doNothing() {}
区块缩进:4 个空格
每次打开新的块或类似块的结构时,缩进量都会增加四个空格。当块结束时,缩进将返回到先前的缩进级别。缩进级别适用于整个块中的代码和 Comments。 (请参见第 4.1.2 节非空块:K&R 风格中的示例。)
每行一个声明
每个语句后都有一个换行符。
列数上限:120
Log4j 的列限制为 120 个字符。除非另有说明,否则任何超出此限制的行都必须进行换行,如Line-wrapping所述。
Exceptions:
-
不能遵守列限制的行(例如,Javadoc 中的长 URL 或长的 JSNI 方法参考)。
-
打包和导入语句(请参见Package statement和Import statements)。
-
Comments 中的命令行,可以将其剪切并粘贴到 Shell 中。
Line-wrapping
术语注: 将可能合法占用一行的代码分成多行时,通常是为了避免超出列限制,此活动称为换行。
没有全面的确定性公式可以准确显示在每种情况下的换行方式。通常,有几种有效的方法可以对同一段代码进行换行。
提示: 提取方法或局部变量可以解决问题,而无需换行。
在哪里 Rest
换行的主要指令是:更喜欢在 更高的语法级别 上断开。也:
-
如果在非分配运算符处折断了一条线,则该破折号会出现在符号之前。 (请注意,这与 Google 语言用于其他语言(例如 C 和 JavaScript)的做法不同。)
-
这也适用于以下“类似操作符”的符号:点分隔符(.),类型范围内的&符( <T extends Foo & Bar>)和 catch 块中的管道(catch(FooException | BarException e))。
-
当一行在赋值运算符处中断时,中断通常在符号之后,但是任一种方式都可以接受。
-
这也适用于增强的 for(“ foreach”)语句中的“类似赋值运算符”的冒号。
-
方法或构造函数名称保持附加到其后的括号(()中。
-
逗号(,)始终附加在其前面的令牌上。
缩进连续行至少 8 个空格
换行时,第一行(每个续行)之后的每一行都应从原始行缩进至少 8 位。
当有多条连续线时,缩进可以根据需要更改为超过 8.通常,两条连续线只有在以句法上 Parallel 的元素开头时才使用相同的缩进级别。
Horizontal alignment部分介绍了不鼓励使用可变数量的空格将某些标记与前几行对齐的做法。
Whitespace
Vertical Whitespace
出现一个空白行:
-
在类的连续成员(或初始化器)之间:字段,构造函数,方法,嵌套类,静态初始化器,实例初始化器。
-
exception: 两个连续字段之间的空白行(它们之间没有其他代码)是可选的。根据需要使用此类空行来创建字段的逻辑分组。
-
在方法主体内,根据需要创建语句的逻辑分组。
-
(可选)在类的第一个成员之前或之后(不鼓励也不劝阻)。
-
根据本文档其他部分的要求(例如Import statements)。
允许使用多个连续的空行,但从不要求(或鼓励)。
Horizontal whitespace
除了语言或其他样式规则所要求的位置之外,除了 Literals,Comments 和 Javadoc 外,单个 ASCII 空间还会“仅**”出现在以下位置。
-
将任何保留字(例如,if,for 或 catch)与该行后跟的开放括号(())分开
-
将任何保留字(例如 else 或 catch)与该行之前的右花括号(})分开
-
在任何大括号({)之前,但有两个 exception:
-
String [] [] x ={{"foo"}}; ({{,下面的第 8 项之间不需要空格)
-
在任何二元或三元运算符的两侧。这也适用于以下“类似于操作员”的符号:
-
连词类型的&符:\ <T extends Foo & Bar>
-
处理多个异常的 catch 块的管道:catch(FooException | BarException e)
-
增强的 for(“ foreach”)语句中的冒号(:)
-
之后,:;;或强制转换的右括号())
-
在双斜杠(//)的两边,开始行尾 Comments。在此,允许多个空格,但不是必需的。
-
在声明的类型和变量之间:List<String> list
-
数组初始化器的两个大括号内都可选
-
new int []{5, 6}和 new int []{ 5, 6 }均有效
注意: 此规则从不要求或禁止在行的开头或结尾增加额外的空间,而仅要求内部空间。
水平对齐:不需要
术语注: 水平对齐是在代码中添加可变数量的附加空格的一种做法,目的是使某些标记直接出现在前几行中其他标记的下面。
这种做法是允许的,但 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
提示: 对齐方式可以提高可读性,但会给以后的维护带来麻烦。考虑 Future 的变化,只需要触碰一条线。这项更改可能会使原本令人愉悦的格式受到干扰,这是“允许的”。通常,它会提示编码员(也许是您)也调整附近行的空白,可能触发一系列级联的重新格式化。单行更改现在具有“爆炸半径”。在最坏的情况下,这可能会导致毫无意义的繁忙工作,但在最佳情况下,它仍会破坏版本历史记录信息,减慢审阅者的速度并加剧合并冲突。
分组括号:推荐
仅当作者和审稿人同意没有合理的机会将代码误解为可选字符时,才省略可选的分组括号,也不会使代码更易于阅读。假设每个阅读器都存储了整个 Java 运算符优先级表,这是不合理的。
Specific constructs
Enum classes
在每个逗号后面跟随一个枚举常量,换行符是可选的。
没有方法且没有任何常量文档的枚举类可以有选择地格式化,就好像它是数组初始化程序一样(请参见array initializers)。
private enum Suit { CLUBS, HEARTS, SPADES, DIAMONDS }
由于枚举类是类,因此适用于格式化类的所有其他规则。
Variable declarations
每个声明一个变量
每个变量声明(字段或局部变量)仅声明一个变量:声明,例如 int a,b;不使用。
必要时声明,请尽快初始化
局部变量不是在其包含的块或类似块的结构的开头习惯性地声明的。取而代之的是,将局部变量声明为接近它们的首次使用(在合理范围内),以最小化它们的范围。局部变量声明通常具有初始化程序,或者在声明后立即初始化。
Arrays
数组初始值设定项:可以是“块状”的
任何数组初始化器都可以选择格式化,就好像它是“块状构造”一样。例如,以下全部有效( 不是 详尽列表):
new int[] { new int[] {
0, 1, 2, 3 0,
} 1,
2,
new int[] { 3,
0, 1, }
2, 3
} new int[]
{0, 1, 2, 3}
没有 C 样式的数组声明
方括号是类型的一部分,而不是变量的一部分:String [] args,而不是 String args []。
Switch statements
术语注: 开关块的花括号内是一个或多个语句组。每个语句组由一个或多个开关标签(大小写为 FOO:或默认值:)组成,后跟一个或多个语句。
Indentation
与其他任何块一样,switch 块的内容缩进 2.
在开关标签之后,出现换行符,并且压痕级别增加了 2,就像打开了一个块一样。下面的开关标签将返回到先前的缩进级别,就好像一个块已被关闭一样。
Fall-through: commented
在 switch 块内,每个语句组要么突然终止(带有 break,continue,return 或 thrown 异常),要么用 Comments 标记以指示执行将 continue 执行或可能 continue 执行下一个语句组。任何传达掉线想法的 Comment 都是足够的(通常//掉线)。在 switch 块的最后一个语句组中不需要此特殊 Comments。例:
switch (input) {
case 1:
case 2:
prepareOneOrTwo();
// fall through
case 3:
handleOneTwoOrThree();
break;
default:
handleLargeNumber(input);
}
存在默认情况
每个 switch 语句包括一个默认语句组,即使它不包含任何代码。
Annotations
应用于类,方法或构造函数的 Comments 将立即出现在 documentation 块之后,并且每个 Comments 都在其自己的一行上列出(即,每行一个 Comments)。这些换行符不构成换行符(第 4.5 节,Line-wrapping),因此缩进级别不会增加。例:
@Override
@Nullable
public String getNameIfPresent() { ... }
exception: 单个无参数 Comments 可能会与签名的第一行一起出现,例如:
@Override public int hashCode() { ... }
应用于字段的 Comments 也立即出现在文档块之后,但是在这种情况下,可能会在同一行上列出多个 Comments(可能已参数化)。例如:
@Partial @Mock DataLoader loader;
没有用于格式化参数和局部变量 Comments 的特定规则。
Comments
阻止 Comment 样式
块 Comments 的缩进程度与周围的代码相同。它们可以采用/ * ... * /样式或// ...样式。对于多行/ * ... * /Comments,后续行必须以开头,并与前一行的对齐。
/*
* This is // And so /* Or you can
* okay. // is this. * even do this. */
*/
Comments 未包含在标有星号或其他字符的框中。
提示:**在编写多行 Comments 时,如果希望自动代码格式化程序在必要时重新换行,请使用/ * ... * /样式(段落样式)。大多数格式化程序不会在// //样式 Comments 块中重新包装行。
Modifiers
如果存在类和成员修饰符,则它们按照 Java 语言规范的建议 Sequences 出现:
public protected private abstract static final transient volatile synchronized native strictfp
Numeric Literals
长值整数 Literals 使用大写的 L 后缀,切勿使用小写(以避免与数字 1 混淆)。例如,3000000000L 而不是 3000000000l。
Naming
所有标识符通用的规则
标识符仅使用 ASCII 字母和数字,并在下面提到的两种情况下使用下划线。因此,每个有效的标识符名称都由正则表达式\ w 匹配。
在 Google 样式中,不使用特殊的前缀或后缀(例如在示例 name_,mName,s_name 和 kName 中看到的前缀或后缀)。
按标识符类型划分的规则
Package names
软件包名称均为小写字母,连续的单词简单地串联在一起(没有下划线)。例如,com.example.deepspace,而不是 com.example.deepSpace 或 com.example.deep_space。
Class names
类名写在UpperCamelCase中。
类名通常是名词或名词短语。例如,Character 或 ImmutableList。接口名称也可以是名词或名词短语(例如 List),但有时也可以是形容词或形容词短语(例如 Readable)。
命名 Comments 类型没有特定的规则,甚至没有完善的约定。
测试类的名称以要测试的类的名称开头,并以 Test 结尾。例如,HashTest 或 HashIntegrationTest。
Method names
方法名称写在lowerCamelCase中。
方法名称通常是动词或动词短语。例如,sendMessage 或 stop。
下划线可能出现在 JUnit 测试方法名称中,以分隔名称的逻辑组成部分。一种典型的模式是 test<MethodUnderTest> _<state>,例如 testPop_emptyStack。没有一种命名测试方法的正确方法。
Constant names
常量名称使用 CONSTANT_CASE:所有大写字母,单词之间用下划线分隔。但是什么是常数呢?
每个常量都是一个静态最终字段,但并非所有静态最终字段都是常量。选择常量大小写之前,请考虑该字段是否真的像常量。例如,如果该实例的可观察状态中的任何一个可以改变,那么几乎可以肯定它不是常数。仅企图从不改变对象通常是不够的。例子:
// Constants
static final int NUMBER = 5;
static final ImmutableList<String> NAMES = ImmutableList.of("Ed", "Ann");
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 Logger logger = Logger.getLogger(MyClass.getName());
static final String[] nonEmptyArray = {"these", "can", "change"};
这些名称通常是名词或名词短语。
非恒定字段名称
非常量字段名(静态或其他)写在lowerCamelCase中。
这些名称通常是名词或名词短语。例如,计算值或索引。
Parameter names
参数名称写在lowerCamelCase中。
应避免使用一个字符的参数名称。
局部变量名称
局部变量名称用lowerCamelCase编写,并且可以比其他类型的名称更自由地缩写。
但是,除了临时变量和循环变量外,应避免使用一个字符的名称。
即使最终变量和不可变变量,局部变量也不会被视为常量,也不应将其设置为常量。
Importing 变量名称
每个类型变量以两种样式之一命名:
-
单个大写字母,可选后跟单个数字(例如 E,T,X,T2)
-
以类的形式使用的名称(请参见Class names),后跟大写字母 T(例如:RequestT,FooBarT)。
骆驼保护套:已定义
有时,有多种合理的方法可以将英语短语转换为驼峰式大小写,例如,当出现首字母缩写词或诸如“ IPv6”或“ iOS”之类的异常结构时。为了提高可预测性,Google 样式指定了以下(几乎)确定性方案。
从名字的散文形式开始:
-
将短语转换为纯 ASCII 并删除所有撇号。例如,“穆勒算法”可能会变成“穆勒算法”。
-
将此结果划分为单词,并在空格和所有剩余的标点符号(通常为连字符)上分割。
-
推荐:如果任何单词在常规用法中已经具有常规的驼峰式外观,请将其拆分为各个组成部分(例如,“ AdWords”变成“ ad words”)。请注意,诸如“ iOS”之类的词本身并不是驼峰式的。它违反任何约定,因此该建议不适用。
-
现在将所有内容(包括首字母缩写词)都小写,然后仅将以下内容的第一个字符大写:
-
...每个单词,以产生大写的驼峰大写字母,或
-
...除第一个单词外的每个单词,以产生小写的驼峰字母
-
最后,将所有单词合并为一个标识符。
请注意,几乎完全忽略了原始单词的大小写。例子:
Prose form | Correct | Incorrect |
---|---|---|
“ XML HTTP 请求” | XmlHttpRequest | XMLHTTPRequest |
“新 ClientID” | newCustomerId | newCustomerID |
"inner stopwatch" | innerStopwatch | innerStopWatch |
“在 iOS 上支持 IPv6 吗?” | supportsIpv6OnIos | supportsIPv6OnIOS |
"YouTube importer" | YouTubeImporter | |
YoutubeImporter* |
*可以接受,但不推荐。
注意: 某些单词在英语中带有歧义的连字符:例如,“ nonempty”和“ non-empty”都是正确的,因此方法名 checkNonempty 和 checkNonEmpty 都是正确的。
Programming Practices
@Override:始终使用
只要方法合法,就使用@Override 注解标记该方法。这包括覆盖超类方法的类方法,实现接口方法的类方法以及重新指定超接口方法的接口方法。
exception: 如果父方法为@Deprecated,则@Override 可以省略。
捕获的异常:不忽略
除以下所述外,对捕获到的异常不采取任何措施是非常正确的。 (通常的响应是将其记录下来,或者如果认为它是“不可能的”,则将其作为 AssertionError 重新抛出.)
当确实在 catch 块中不执行任何操作时,在 Comments 中说明了这样做有道理的原因。
try {
int i = Integer.parseInt(response);
return handleNumericResponse(i);
} catch (NumberFormatException ok) {
// it's not numeric; that's fine, just continue
}
return handleTextResponse(response);
异常: 在测试中,如果捕获的异常命名为期望的,则可以不加 Comments 地忽略。以下是确保测试中的方法确实抛出期望类型的异常的非常常见的习惯用法,因此此处无需添加 Comments。
try {
emptyStack.pop();
fail();
} catch (NoSuchElementException expected) {
}
静态成员:使用类合格
如果对静态类成员的引用必须是合格的,则使用该类的名称(而不是该类类型的引用或表达式)对其进行限定。
Foo aFoo = ...;
Foo.aStaticMethod(); // good
aFoo.aStaticMethod(); // bad
somethingThatYieldsAFoo().aStaticMethod(); // very bad
终结器:未使用
重写 Object.finalize 是“极少数”。
提示: 不要这样做。如果绝对必须,请非常仔细地阅读并理解Effective Java项目 7,“避免使用终结器”,然后再不这样做。
Javadoc
Formatting
General form
在此示例中可以看到 Javadoc 块的基本格式:
/**
* Multiple lines of Javadoc text are written here,
* wrapped normally...
*/
public int method(String p1) { ... }
...或在此单行示例中:
/** An especially short bit of Javadoc. */
基本形式始终可以接受。当不存在任何从句时,可以替换单行形式,并且整个 Javadoc 块(包括 Comments 标记)可以放在一行中。
Paragraphs
在段落之间,如果存在“从句”组之前,将出现一个空白行,即仅包含对齐的前导星号(*)的行。除第一段外,每个段的第一个单词前都有\ ,后面没有空格。
At-clauses
使用的任何标准“从句”都以@ param,@ return,@ throws,@ deprecated 的 Sequences 出现,并且这四种类型绝不会出现空的描述。如果一条从句不适合一行,则将续行从@的位置缩进四个(或更多)空格。
摘要片段
每个类和成员的 Javadoc 都以一个简短的“摘要片段”开头。该片段非常重要:它是文本在某些上下文(如类和方法索引)中出现的唯一部分。
这是一个片段-名词短语或动词短语,而不是完整的句子。它不是“不是”以 A{@code Foo}是...开头,或者此方法返回...,也不会形成一个完整的命令式句子,如“保存记录”。但是,该片段被大写并标点,就像这是一个完整的句子。
提示: 一个常见的错误是以/ 的形式编写简单的 Javadoc @@返回 ClientID * /.这是不正确的,应更改为/ 返回 ClientID。 * /。
使用 Javadoc 的地方
至少,每个公共类以及该类的每个公共或受保护成员都应使用 Javadoc,以下有一些 exception。
其他类和成员仍根据需要使用 Javadoc。每当将实现 Comments 用于定义类,方法或字段的总体目的或行为时,该 Comments 将改为用 Javadoc 编写。 (它更加统一,并且对工具更友好.)
exception:不言自明的方法
对于诸如 getFoo 之类的“简单,显而易见”的方法,Javadoc 是可选的,在这种情况下,除了“返回 foo”之外,别无他法。
重要提示: 引用此 exceptions 来说明省略典型 Reader 可能需要了解的相关信息是不合适的。例如,对于名为 getCanonicalName 的方法,如果典型的 Reader 可能不知道术语“规范名称”是什么,则不要省略其文档(其理由是仅说/ *返回规范名称. /)。手段!
Exception: overrides
在覆盖超类型方法的方法上并不总是存在 Javadoc。