企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# :-: elsfs 开源项目java代码规范 [TOC] ## 1. 介绍 本文档是elsfs Java源代码编码标准的完整定义,编程语言Java源文件被描述为elsfs样式,前提是必须遵守此处的规则。 与其他编程风格指南一样,所涵盖的问题不仅涉及格式的美学问题,还涉及其他类型的约定或编码标准。然而,本文件主要关注我们普遍遵循的硬性规则,并避免提供无法明确执行的建议(无论是通过人工还是工具)。 ### 1.1. 术语解释 在本文件中,除非另有说明: 1. 类一词包括 普通类(ordinary class)、枚举类(enum class)、接口(interface)或注释类型(@interface)。 2. 成员(类)这一术语被广泛地用于表示嵌套类、字段、方法或构造函数;也就是说,除了初始化器和注释之外的所有类顶层内容。 3. 术语“注释”始终指的是实现注释。我们不使用“文档注释”这个短语,而是使用通用术语“Javadoc”。 **其他“术语注解”将会在文档中不定时出现。** ### 1.2. 指南解释 本文档中的示例代码是非规范性的。也就是说,虽然示例采用elsfsf样式,但它们可能没有唯一的方式来表达代码。示例中做出的可选格式选择不应被强制作为规则。 ## 2. 源文件的基本条件 ### 2.1. 文件名称 原文件名必须和顶级类(只有一个 java特性)的名字一致且区分大小写,扩展名“.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)转义。 参考The Java™ Tutorials [Characters (The Java™ Tutorials > Learning the Java Language > Numbers and Strings) (oracle.com)](https://docs.oracle.com/javase/tutorial/java/data/characters.html) #### 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 源文件结构 ### 3.1 许可证或版权信息(如果有) 如果许可证或版权信息属于文件的一部分,那么就把许可放在文件里。 ### 3.2 包声明 package语句声明不能换行,列限制 [4.4 列限制:100]()不适用于包声明 ### 3.3 导入 #### 3.3.1 不允许使用通配符导入 不允许静态或其他形式的通配符导入 #### 3.3.2 不允许导入声明与导入声明之间存在空格 导入语句不允许换行 [4.4 列限制:100]()不适用导入语句 #### 3.3.3 导入声明排序和间距 导入语句的排序方式如下: 1. 将所有静态导入放在一个块中。 2. 将所有非静态导入放在一个块中。 如果同时存在静态和非静态导入,则用一条空行分隔这两个块。导入语句之间没有其他空行。 在每个块中,导入的名称以ASCII排序顺序显示。(**注意**:这与导入语句的ASCII排序顺序不同,因为“.”在“;”之前排序。) #### 3.3.4 禁止静态导入类 静态导入不能用于静态嵌套类,它们是通过普通导入来导入的。 ### 3.4 类的声明 #### 3.4.1 只有一个顶级类声明 每一个顶级类都一个在自己独立的原文件中 #### 3.4.2 类的内容顺序 对于类的成员和初始化器的选择顺序,可能会对可学习性产生很大的影响。但是,并没有一个正确的做法;不同的类可能会以不同的方式来排序它们的内容。 重要的是每个类都使用某种逻辑顺序,如果需要的话,其维护者可以解释这个顺序。例如,新方法不应该习惯性地添加到类的末尾,因为那样会导致按照添加日期排序的顺序,这不是一个逻辑顺序。 ##### 3.4.2.1 重载:分不割 具有相同名称的方法出现在一个单独的连续组中,组中没有其他成员。对于多个构造函数(它们总是具有相同的名称),也适用相同的规则。即使在不同的方法之间存在修饰符(如static或private),也适用此规则。 ## 4 格式化 术语注记:块状结构体是指类体、方法体或构造函数体。注意,在4.8.1 数组初始化中,任何数组初始化都可以可选择地被视为块状结构体。 ### 4.1 花括号 #### 4.1.1 可选使用大括号 花括号与if、else、for、do和while语句一起使用,即使主体为空或仅包含一个语句。其他可选的花括号(例如lambda表达式中的花括号)仍然可选。 #### 4.1.2 非空快结构:K&R样式 花括号(braces)按照Kernighan和Ritchie风格(“埃及花括号”)来配对非空块和块状结构: * 除以下详细说明的情况外,在开括号前没有换行。 * 将开括号后的内容进行换行。 * 在闭合的大括号前添加换行符。 * 在关闭的大括号后面换行,只有当该大括号结束了一个语句或者结束了一个方法、构造函数或者命名类的主体时。例如,如果大括号后面是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 空快结构 一个空的代码块或类似代码块的构造可以使用K&R风格(如4.1.2节所述)。或者,可以在打开后立即关闭,中间没有字符或行分隔符({}),除非它是多代码块语句(直接包含多个代码块的语句:if/else或try/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个字符,即使它的显示宽度更大或更小。例如,如果使用全角字符,则可以选择在比此规则严格要求的位置更早的地方换行。 **异常(xceptions):** 1. 无法遵守列限制的行(例如Javadoc中的长URL,或长JSNI方法引用)。 2. 包声明和导包声明(见第3.2节包声明和第3.3节导包声明)。 3. 注释中的命令行,可以被复制并粘贴到shell中。 4. 在极少数情况下需要非常长的标识符,允许其超过列限制。在这种情况下,周围代码的有效换行方式应由java-format生成。 ### 4.5 换行 **术语注解:**当一段代码在单行中无法合法地占用一行时,将该代码拆分成多行,这个活动就叫做换行。 没有一种全面、确定的公式可以精确地告诉你在任何情况下如何进行折行。很多时候,对于同一段代码,存在多种有效的折行方式。 ***注意:**尽管换行的常见原因是避免超过列限制,但即使是实际上能够适应列限制的代码也可能由作者自行决定进行换行。* **提示:**提取方法或局部变量可以在不需要换行的情况下解决问题。 #### 4.5.1 折行 折行的首要原则是:倾向于在更高的句法层面进行折行。同时: 1. 当一行在非赋值运算符处断开时,断点出现在该符号之前。 这也适用于以下“类似操作符”的符号: * 点分隔符(.) * 方法引用的两个冒号(:) * 在类型约束中()使用ampersand * 在catch块中的管道(catch(FooException|BarException e))。 2. 当一行在赋值运算符处断开时,通常在该符号之后断开,但是两种方式都是可以接受的。 * 这也适用于增强for循环(“foreach”)语句中的“赋值运算符-样”冒号。 3. 方法或构造器的名称保留附着在其后跟随的开放圆括号(**(**)上。 4. 逗号(,)保持与其前面的标记相连。 5. lambda表达式中箭头两侧的代码行不会被分割,除非lambda的主体只包含一个不带花括号的表达式,此时箭头后可紧随一个代码行。例如: ~~~ MyLambda<String, Long, Object> lambda = (String label, Long value, Object obj) -> { ... }; Predicate<String> predicate = str -> longExpressionInvolving(str); ~~~ **注意**:换行的主要目标是拥有清晰的代码,而不一定是适合最小行数的代码。 #### 4.5.2 换行空4个格的情况(java 17)推荐 ### 4.6 空白 #### 4.6.1 垂直空白 一个单独的空白行总是会出现: 1. 在类的连续成员或初构造器之间:字段、构造函数、方法、嵌套类、静态初始化程序和实例初始化程序。 * **例外**:两个连续字段(它们之间没有其他代码)之间的空行是可选的。这些空行根据需要用于创建字段的逻辑分组。 * Exception:枚举常量之间的空行在第4.8.1节中有所介绍。 2. 按照本文件其他部分的要求(例如第3节“源文件结构”和第3.3节“导入语句”)。 在任何可以改善可读性的地方,也可以出现一个空行,例如在语句之间将代码组织成逻辑子部分。类中第一个成员或初始化器之前或最后一个成员或初始化器之后,空行既不鼓励也不阻止。 多行连续空白行是允许的,但从来不是必需的。 #### 4.6.2 空格 除了语言或其他样式规则要求的地方,以及除了文字、注释和Javadoc之外,单个ASCII空格也仅出现在以下地方。 1. 将任何保留字(如if、for或catch)与紧随其后的开圆括号(())分离,位于同一行内 2. 将任何保留字(例如 else 或 catch)与在该行的开头跟在它前面的结束花括号(})分离 3. 在任何开大括号({})之前,除两种情况外: * `@SomeAnnotation({a, b})`(没有空格) * String\[\]\[\] x = {{"foo"}}; 不需要在两者{{之间留有空格 4. 在任何二元或三元运算符的两边。这也适用于以下“类似运算符”的符号: * 在泛型中:<T extends Foo> * 处理多个异常的用于捕获块:catch (FooException | BarException e) * 增强for循环(“foreach”)语句中的冒号(:) * lambda表达式中的箭头:(String str) -> str.length() * 方法引用的双冒号:Object::toString * 点号分隔符(.):object.toString 5. 在任何内容与双斜杠(//)之间,该双斜杠开始一个注释。允许使用多个空格。 6. 在开始注释的双斜杠(//)和注释的文本之间。允许使用多个空格。 7. 在声明的类型和变量之间:*List<String> list* 8. 仅在数组初始值设定项的两个大括号内可选 * * `new int[] {5, 6}`and`new int[] { 5, 6 }`are both valid 9. 1. 在类型批注和 或 之间。`[]``...` 这条规则从来没有被解释为要求或禁止在一行的开始或结束处有额外的空间;它只涉及内部空间。 #### 4.6.3 水平对齐 术语注释:水平对齐是在代码中添加可变数量的额外空格的做法,目的是使某些标记直接出现在前几行的某些其他标记下方。 这种做法是允许的,但代码风格从来没有要求。它甚至不需要在已经使用过的地方保持水平对齐。 下面是一个没有对齐,然后使用对齐的示例: ~~~ 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 声明一个变量 每个变量声明(字段或局部变量)只声明一个变量:声明如int a、b;不使用。 异常:在for循环的头中可以接受多个变量声明。 #### 4.8.2 在需要的时候才申请变量 局部变量不习惯在包含它们的作用域或块状构造的开头进行声明,而是在首次使用时(在合理范围内)附近进行声明,以最小化它们的作用域。局部变量声明通常有初始化器,或者在声明后立即进行初始化。 ### 4.8.3 Arrays #### 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-Style数组声明 使用这个形式`String[] args`, 而不是`String args[]`. #### 4.8.4 Switch语句 术语注释:switch块的大括号内是一个或多个语句组。每个语句组包含一个或多个switch标签(要么是case FOO:,要么是default:),后面跟着一个或多个语句(或者对于最后一个语句组,后面跟着零个或多个语句)。 ##### 4.8.4.1 缩进 与其他任何块一样,switch块的内容缩进+2。 在switch标签之后,有一个换行符,并且缩进级别增加+2,就仿佛一个块被打开了一样。以下的switch标签返回先前的缩进级别,就像一个块被关闭一样。 ##### 4.8.4.2 解释 在一个switch块内,每个语句组要么突然终止(使用break、continue、return或抛出异常),要么使用注释标记以表示执行会或可能会继续到下一个语句组。任何传达“fall through”思想的注释都是足够的(通常为// fall through)。在switch块的最后一个语句组中不需要这种特殊的注释。例如: ~~~ 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语句组,即使它不包含任何代码。 例外:如果为枚举类型添加了显式案例并且涵盖该类型的所有可能值,则可以省略default语句组。这使得IDE或其他静态分析工具能够发出警告,如果漏掉了任何情况。 #### 4.8.5 注释 ##### 4.8.5.1 注解类型使用 ## 5 命名