文章目录
Google Java 编程风格及编码标准指南
前言
本文为Google Java 编程风格及编码标准指南,笔者做了示例补充~
1 简介
本文档完整定义了 Google Java
编程语言源代码的编码标准。当且仅当 Java
源文件严格遵循本文所阐述的规则时,我们才认定该文件符合 Google
风格。
与其他编程风格指南类似,本文不仅涵盖了代码格式的美学规范,还涉及各类约定和编码标准。不过,本文的重点在于明确那些我们普遍遵循的硬性规则,避免提供那些难以通过人工或工具精确执行的建议。
1.1 术语说明
在本文档中,相关术语的定义如下:
- 类:此概念具有宽泛的涵盖范围,包括普通类、枚举类、接口以及注解类型(
@interface
)。 - 成员:指的是类内部的嵌套类、字段、方法或者构造函数,即类的顶层内容,但不包含初始化器和注释。
- 注释:专门指实现注释,这里不使用 “文档注释” 这一术语,通用术语为 “
Javadoc
”。
1.2 指南说明
本文所给出的示例代码均为非规范性示例。这些示例虽然符合 Google
风格,但并非是唯一正确的形式。示例中呈现的可选格式不应被视作强制规则。
2 源文件基础
2.1 文件名
源文件的命名规则是:由顶级类名(严格区分大小写)加上 .java
扩展名构成。
正例 ✅: 类名与文件名保持一致
src/com/example/MyClass.java
public class MyClass { ... }
反例 ❌: 类名与文件名不匹配
src/com/example/MyClassTest.java
public class MyClass { ... } // 文件名与类名不匹配
2.2 文件编码:UTF-8
源文件统一采用 UTF-8
编码格式,以确保在不同系统和环境下的兼容性和一致性。
2.3 特殊字符
2.3.1 空白字符
除了行终止符之外,ASCII
水平空格字符(0x20
)是源文件中唯一被允许使用的空白字符。这一规则包含以下两层含义:
- 字符串和字符字面量中的其他空白字符必须进行转义处理。
- 禁止使用制表符(
Tab
)进行代码缩进。
正例 ✅: 使用空格进行缩进
public void method() {
∙∙if (condition) {
∙∙∙∙doSomething();
∙∙}
}
反例 ❌: 使用制表符进行缩进
public void method() {
→if (condition) {
→→doSomething();
→}
}
为什么推荐使用空格而不是制表符?
跨平台兼容性:不同的操作系统对制表符的宽度处理方式存在差异。例如,Windows
系统默认的制表符宽度为 4
个空格,而 Linux
和 macOS
系统通常将其设置为 8
个空格。使用空格可以有效避免这种因操作系统不同而导致的显示不一致问题。
2.3.2 特殊转义序列
对于具有特殊转义序列的字符,如 \b
(退格符)、\t
(制表符)、\n
(换行符)、\f
(换页符)、\r
(回车符)、\"
(双引号)、\'
(单引号)、\\
(反斜杠),必须使用标准的转义序列,而不能采用八进制(如 \012
)或 Unicode
(如 \u000a
)转义。
正例 ✅: 使用标准转义序列
String text = "Line1\nLine2"; // 换行符
String path = "C:\\Users\\file"; // 反斜杠
String quote = "\"Hello\""; // 双引号
char backspace = '\b'; // 退格符
反例 ❌: 使用八进制Unicode
转义替代标准转义
String text = "Line1\012Line2"; // 用八进制\012表示换行
String path = "C:\u005CUsers\u005Cfile"; // 用Unicode转义反斜杠
String quote = "\u0022Hello\u0022"; // 用Unicode转义双引号
char backspace = '\u0008'; // 用Unicode转义退格符
2.3.3 非ASCII字符
对于非 ASCII
字符,既可以使用实际的 Unicode
字符(如 ∞
),也可以使用等效的 Unicode
转义(如 \u221e
)。具体的选择应根据代码的可读性来决定,但强烈建议避免在字符串和注释之外使用 Unicode
转义。
正例 ✅: 直接使用Unicode
字符(可打印字符)
String mathSymbol = "∞"; // 无限符号
String greeting = "你好,世界!"; // 中文字符
String unit = "Ω"; // 欧姆符号
// 对不可打印字符使用转义)
String bom = "\ufeff" + content; // 字节顺序标记(不可打印)
反例 ❌: 不必要的Unicode转义(可打印字符)
// 不必要的Unicode转义(可打印字符)
String mathSymbol = "\u221e"; // 应直接使用 ∞
String greeting = "\u4f60\u597d\uff0c\u4e16\u754c\uff01"; // 硬编码中文转义
String unit = "\u03a9"; // 应直接使用 Ω
// 在代码逻辑中使用Unicode转义
if (str.startsWith("\uFEFF")) { // 应改用常量定义
// 非字符串/注释区域使用转义
}
// 混合使用字符和转义(不一致)
String hybrid = "μs\u03bcs"; // 混用 μ 和 \u03bc
3 源文件结构
源文件应当按照以下顺序依次包含相应内容:
- 许可证 / 版权信息(若存在)
package
语句import
语句- 唯一的顶级类
各部分内容之间需用空行进行分隔,以增强代码的可读性和结构的清晰性。
正例 ✅:
// 1. 许可证信息(使用块注释)
/*
* Copyright 2025 yaoxin LLC
* Licensed under the Apache License, Version 2.0
*/
// 2. package语句(单行不换行)
package com.example.project.module.submodule;
// 3. import语句(空行分隔)
import java.util.List;
import javax.servlet.http.HttpServletRequest;
// 4. 顶级类(空行分隔)
public class MainController {
// class body
}
3.1 许可证/版权信息
若源文件需要包含许可证或版权信息,那么这些信息应放置在文件的开头位置,以明确文件的版权归属和使用许可。
反例 ❌:
package com.example.project; // 错误:许可证与package之间无空行
import java.util.List; // 错误:package与import之间无空行
/*
* Copyright信息
*/
public class MainController { ... }
3.2 package
语句
package
语句必须写在同一行,不允许换行,并且不受 100
字符列宽的限制。
反例 ❌:
// 错误:package语句换行(即使超长)
package com.example.project.
submodule; // 违反:package必须单行
3.3 import
语句
3.3.1 禁止通配符导入
无论是静态还是非静态的通配符导入,都不允许在代码中使用。这种做法有助于明确代码依赖,避免潜在的命名冲突。
正例 ✅: 显式导入
import java.util.ArrayList;
import java.util.List;
反例 ❌: 通配符导入
import java.util.*;
3.3.2 禁止换行
import
语句同样需要写在同一行,不允许换行,也不受列宽的限制。
正例 ✅:
import java.util.ArrayList;
import java.util.List;
反例 ❌:
import java.util.
ArrayList;
import java.util.
List;
3.3.3 排序与间距
import
语句的排序规则如下:
- 所有的静态导入需集中在一个单独的块中。
- 所有的非静态导入也需集中在一个单独的块中。
若同时存在静态和非静态导入,这两个块之间需用一个空行进行分隔,而在每个块内部的 import
语句之间则不允许有其他空行。
在每个块中,导入的名称应按照 ASCII
排序顺序排列。需要注意的是,这里是指导入名称的 ASCII
排序,而非 import
语句的 ASCII
排序,因为在排序规则中,'.'
的优先级高于 ';'
。
正例 ✅: 静态导入与非静态导入分组
import static com.example.Utils.MAX_SIZE;
import static com.example.Utils.MIN_SIZE;
import com.example.model.User;
import com.example.service.AuthService;
反例 ❌: 混合排序
import com.example.model.User;
import static com.example.Utils.MAX_SIZE;
import com.example.service.AuthService;
3.3.4 静态嵌套类导入
对于静态嵌套类,应使用普通导入方式,而不使用静态导入。
正例 ✅: 普通导入静态嵌套类
import com.example.OuterClass.NestedStaticClass;
public class Demo {
void method() {
// 正确:通过类名直接访问
NestedStaticClass obj = new NestedStaticClass();
// 或明确包含外部类名(两者均可)
OuterClass.NestedStaticClass obj2 = new OuterClass.NestedStaticClass();
}
}
反例 ❌: 静态导入嵌套类
import static com.example.OuterClass.NestedStaticClass;
public class Demo {
void method() {
NestedStaticClass obj = new NestedStaticClass(); // 编译通过但违反规范
}
}
3.4 类声明
3.4.1 单顶级类声明
每个顶级类都必须位于一个独立的源文件中,以保证代码结构的清晰性和可维护性。
正例 ✅:
// 文件:User.java
public class User { // 类名与文件名一致
// class body
}
反例1 ❌: 多个顶级类
// 文件:User.java
public class User { ... }
class Address { // 违反:一个文件只能有一个public类
// 其他类必须是非public的(且不鼓励)
}
反例2 ❌: 文件名与类名不匹配
// 文件:UserService.java
public class UserController { ... } // 类名与文件名不一致
3.4.2 类内容顺序
类的成员和初始化器的排列顺序应具有逻辑性,新增方法时不应简单地将其追加到类的末尾。
正例 ✅: 逻辑分组
public class OrderProcessor {
// 字段分组
private static final Logger LOG = ...;
private String config;
// 构造器
public OrderProcessor() { ... }
public OrderProcessor(String config) { ... }
// 公共方法
public void process() { ... }
public void validate() { ... }
// 重载方法集中
public void process(Order order) { ... } // 重载方法1
public void process(Order order, boolean log) { ... } // 重载方法2
// 私有方法
private void logProcess() { ... }
}
反例 ❌: 无序结构
public class OrderProcessor {
private String config;
public void validate() { ... }
// ❌ 错误:重载方法被其他方法隔开
public void process(Order order) { ... }
private void logProcess() { ... }
public OrderProcessor() { ... } // ❌ 构造器出现在方法之后
public void process(Order order, boolean log) { ... } // 重载方法分散
}
3.4.2.1 重载方法集中
同名的方法必须连续排列,即使它们的修饰符不同(如 static
或 private
),这样可以方便开发者快速查找和理解相关方法。
正例 ✅: 连续重载
public class StringUtils {
// 重载方法组1:静态方法
public static String trim(String str) { ... }
public static String trim(String str, char ch) { ... }
// 重载方法组2:实例方法
public String reverse() { ... }
public String reverse(boolean keepCase) { ... }
}
反例1 ❌: 重载方法分散
public class StringUtils {
public static String trim(String str) { ... }
public String reverse() { ... } // ❌ 其他方法插入重载组中间
public static String trim(String str, char ch) { ... } // 与同组方法分离
}
反例2 ❌: 不同修饰符分离
public class Cache {
public void clear() { ... }
private void logClear() { ... }
// ❌ 同方法名但修饰符不同导致分离
public static void clear(String key) { ... }
}
4 格式规范
4.1 大括号
4.1.1 大括号使用
在编写 if
、else
、for
、do
、while
语句时,无论代码体为空或者仅包含单条语句,都必须使用大括号。不过,lambda
表达式中的可选大括号仍遵循其原有的可选规则。
正例 ✅: 单语句也使用大括号
if (condition) {
doSomething();
}
反例 ❌: 省略大括号
if (condition)
doSomething();
4.1.2 非空代码块:K&R风格
采用 Kernighan
和 Ritchie
风格(即 “埃及括号” 风格),具体规则如下:
- 左大括号前不换行,保证代码的紧凑性。
- 左大括号后换行,清晰划分代码块。
- 右大括号前换行,增强代码的可读性。
- 右大括号后换行(当结束语句或方法体时),使代码结构更加清晰。
例外情况:switch
语句中用于限制局部变量作用域的代码块可以换行。
正例 ✅:
public class KRStyleExample {
public static void main(String[] args) {
if (true) {
System.out.println("Condition is true");
} else {
System.out.println("Condition is false");
}
// switch语句中限制局部变量作用域的代码块换行示例
int num = 1;
switch (num) {
case 1: {
int localVar = 10;
System.out.println("Local variable: " + localVar);
break;
}
default:
System.out.println("Default case");
}
}
}
反例 ❌:
public class BadKRStyleExample {
public static void main(String[] args) {
if (true)
{
System.out.println("Condition is true");
} // 左大括号前换行,不符合规则
if (false) { System.out.println("This won't print"); } // 左大括号后未换行,不符合规则
if (false) {
System.out.println("Condition is false");} // 右大括号前未换行,不符合规则
if (true) {
System.out.println("Condition is true");
} else System.out.println("This is wrong"); // 右大括号后未换行且未使用大括号包裹else块,不符合规则
}
}
4.1.3 空代码块
空代码块可以使用 {}
这种简洁形式,但多块语句(如 if/else
)中的空块除外。
正例 ✅:
public void multiBlockExample() {
try {
// 多块语句中的空块,不能使用简洁形式
doSomething();
} catch (Exception e) {
// 这里不能写成 {}
}
}
反例 ❌:
public void multiBlockExample() {
try {
doSomething();
} catch (Exception e) {} // 多块语句中的空块使用了简洁形式,不符合规则
}
4.2 代码块缩进:+2
空格
每开启一个新的代码块,都需要增加 2
个空格的缩进;当代码块结束时,恢复到前一个缩进级别。这样可以使代码的层次结构更加清晰,便于阅读和维护。
正例 ✅: 正确缩进
public void method() {
∙∙if (condition) {
∙∙∙∙return result;
∙∙}
}
反例 ❌: 缩进不足
public void method() {
∙if (condition) {
∙∙return result;
∙}
}
4.3 每行单语句
每个语句结束后都要进行换行,确保每行代码只包含一个语句。这样可以提高代码的可读性,避免出现复杂难读的长行代码。
正例 ✅:
public class SingleStatementPerLineExample {
public static void main(String[] args) {
// 每个语句单独一行
int num = 10;
String str = "Hello";
if (num > 5) {
System.out.println("Number is greater than 5");
}
for (int i = 0; i < 3; i++) {
System.out.println("Iteration: " + i);
}
}
}
反例 ❌:
public class BadSingleStatementPerLineExample {
public static void main(String[] args) {
// 多个语句写在一行,不符合规范
int num = 10; String str = "Hello";
if (num > 5) { System.out.println("Number is greater than 5"); }
for (int i = 0; i < 3; i++) System.out.println("Iteration: " + i);
}
}
4.4 列宽限制:100
字符
代码行的宽度一般不应超过 100
个字符(Unicode
码点)。不过,存在以下例外情况:
- 无法换行的长
URL/JSNI
引用。 package/import
语句。- 可复制到
shell
的命令行注释。 - 超长标识符(这种情况较为罕见)。
正例 ✅:shortMessage
变量的赋值语句长度在 100 字符以内,符合列宽限制。longMessage
变量的赋值语句超过了 100 字符,通过换行的方式将其拆分成多行,满足列宽要求。
public class ColumnWidthPositiveExample {
public static void main(String[] args) {
// 正常代码行不超过 100 字符
String shortMessage = "This is a short message within the 100-character limit.";
System.out.println(shortMessage);
// 当代码可能超过 100 字符时进行换行
String longMessage = "This is a long message that needs to be "
+ "split into multiple lines to fit within the 100-character limit.";
System.out.println(longMessage);
}
}
例外情况处理
// 无法换行的长 URL 引用,不受 100 字符限制
package com.example.longurlpackage;
import java.net.URL;
public class ColumnWidthExceptionsPositive {
// 超长 URL 引用
public static final String LONG_URL = "https://www.example.com/this-is-a-very-long-url-that-cannot-be-easily-wrapped-and-is-an-exception-to-the-100-character-limit";
// 可复制到 shell 的命令行注释
// java -cp /path/to/your/classes com.example.longurlpackage.ColumnWidthExceptionsPositive --option1 value1 --option2 value2
// 超长标识符(罕见情况)
public static final String VERY_LONG_IDENTIFIER_THAT_IS_ALLOWED_TO_EXCEED_100_CHARACTERS = "This is a very long identifier for demonstration purposes";
public static void main(String[] args) throws Exception {
URL url = new URL(LONG_URL);
System.out.println(url);
}
}
反例 ❌:longMessage
变量的赋值语句超过了 100 字符,且没有进行换行处理,违反了代码行宽不超过 100 字符的规则。
public class ColumnWidthNegativeExample {
public static void main(String[] args) {
// 代码行超过 100 字符且未换行
String longMessage = "This is a very long message that exceeds the 100-character limit and should be split into multiple lines but is not split properly. This makes the code hard to read and violates the column width rule.";
System.out.println(longMessage);
}
}
4.5 换行
4.5.1 换行位置
在进行代码换行时,应优先在高语法层级的位置进行换行,具体规则如下:
- 在非赋值运算符前换行,这样可以使表达式的逻辑更加清晰。
- 在赋值运算符后换行,便于区分赋值操作和后续的表达式。
- 方法名与左括号保持在同一行,确保方法调用的完整性。
- 逗号前保持在同一行,使参数列表的结构更加清晰。
Lambda
箭头后允许换行(当表达式为单行非大括号时),增加代码的灵活性。
正例 ✅:
public static void main(String[] args) {
// 在非赋值运算符前换行
int result = 10 + 20
+ 30 + 40;
// 赋值运算符后换行
String longString = "This is a very long string that "
+ "needs to be wrapped for better readability.";
// 方法名与左括号保持同行
List<String> list = new ArrayList<>(
List.of("element1", "element2", "element3")
);
// 逗号前保持同行
someMethod(
"arg1",
"arg2",
"arg3"
);
// Lambda箭头后允许换行(当表达式为单行非大括号时)
Predicate<String> predicate = str ->
str.length() > 5;
}
反例 ❌:
public static void main(String[] args) {
// 错误:在非赋值运算符后换行
int result = 10 +
20 + 30 + 40;
// 错误:赋值运算符前换行
String longString =
"This is a very long string that " + "needs to be wrapped for better readability.";
// 错误:方法名与左括号换行
List<String> list = new ArrayList<>
(List.of("element1", "element2", "element3"));
// 错误:逗号后保持同行,未在合适位置换行
someMethod("arg1", "arg2",
"arg3");
// 错误:Lambda 箭头前换行
Predicate<String> predicate = str
-> str.length() > 5;
}
4.5.2 续行缩进:至少+4
空格
当代码需要换行继续编写时,续行的缩进至少为 4
个空格。同时,也可以根据实际情况灵活调整,以对齐相似的代码结构。
正例 ✅: 续行缩进+4
String longString = "This is a very long string that needs to be "
+ "split into multiple lines.";
反例 ❌: 缩进错误
String longString = "This is a very long string that needs to be "
+ "split into multiple lines.";
4.6 空白
4.6.1 垂直空白
在以下两种情况下,必须使用单空行进行分隔:
- 类的成员之间,例如字段、构造器、方法等。
- 文件结构的各个部分之间,像版权信息、包声明、导入声明和顶级类之间。
为了提升代码的可读性,允许适当使用单空行。但要注意,禁止使用多空行,以免造成代码结构的松散和混乱。
正例 ✅:
// 版权信息
/*
* Copyright (c) 2025, Example Company. All rights reserved.
*/
// 包声明
package com.example.demo;
// 导入声明
import java.util.ArrayList;
import java.util.List;
// 顶级类
public class VerticalWhitespaceExample {
// 字段
private int field1;
private String field2;
// 构造器
public VerticalWhitespaceExample() {
field1 = 0;
field2 = "";
}
// 方法
public void method1() {
System.out.println("Method 1");
}
public void method2() {
System.out.println("Method 2");
}
}
反例 ❌:
// 版权信息
/*
* Copyright (c) 2025, Example Company. All rights reserved.
*/
package com.example.demo;
import java.util.ArrayList;
import java.util.List;
public class BadVerticalWhitespaceExample {
private int field1;private String field2;
public BadVerticalWhitespaceExample() {
field1 = 0;
field2 = "";
}
public void method1() {
System.out.println("Method 1");
}
public void method2() {
System.out.println("Method 2");
}
}
4.6.2 水平空白
在以下这些位置,需要使用单空格进行分隔:
- 关键字与左括号之间,增强代码的逻辑性和可读性。
- 右大括号与关键字(如
else
)之间,使代码结构更加清晰。 - 二元 / 三元运算符的两侧,明确运算符的作用范围。
- 逗号、分号、类型转换之后,规范代码的书写格式。
- 双斜线注释之前,保持注释与代码的清晰分隔。
- 类型与变量之间,清晰区分类型和变量名。
- 数组初始化大括号内(此为可选操作)。
正例 ✅:
public class HorizontalWhitespaceExample {
// 关键字与左括号之间
public void ifExample() {
if (true) {
System.out.println("Condition is true");
}
}
// 右大括号与关键字(如else)之间
public void ifElseExample() {
if (false) {
System.out.println("This won't print");
} else {
System.out.println("This will print");
}
}
// 二元/三元运算符两侧
public int binaryOperatorExample() {
int a = 5;
int b = 3;
int result = a + b;
int max = (a > b) ? a : b;
return result;
}
// 逗号/分号/类型转换后
public void commaSemicolonCastExample() {
int[] array = {1, 2, 3};
Object obj = array;
int[] newArray = (int[]) obj;
System.out.println(newArray[0]);
}
// 双斜线注释前
public void commentExample() {
int num = 10; // This is a comment
}
// 类型与变量之间
public void typeVariableExample() {
List<String> list = new ArrayList<>();
}
// 数组初始化大括号内(可选)
public void arrayInitializerExample() {
int[] arr = { 1, 2, 3 };
}
}
反例 ❌:
public class BadHorizontalWhitespaceExample {
// 关键字与左括号之间无空格
public void ifExample() {
if(true) {
System.out.println("Condition is true");
}
}
// 右大括号与关键字(如else)之间无空格
public void ifElseExample() {
if (false) {
System.out.println("This won't print");
}else {
System.out.println("This will print");
}
}
// 二元/三元运算符两侧无空格
public int binaryOperatorExample() {
int a = 5;
int b = 3;
int result = a+b;
int max = (a>b)? a: b;
return result;
}
// 逗号/分号/类型转换后无空格
public void commaSemicolonCastExample() {
int[] array = {1,2,3};
Object obj = array;
int[] newArray = (int[])obj;
System.out.println(newArray[0]);
}
// 双斜线注释前无空格
public void commentExample() {
int num = 10;// This is a comment
}
// 类型与变量之间无空格
public void typeVariableExample() {
List<String>list = new ArrayList<>();
}
}
4.6.3 禁止水平对齐
在代码编写过程中,不应该刻意通过添加额外的空格来实现水平对齐。各变量声明和语句应按照正常的格式进行编写,这样可以避免代码的过度格式化,使代码更加简洁易读。
正例 ✅:
public class NoHorizontalAlignmentPositive {
private int x;
private String name;
public void method1() {
int a = 1;
String str = "Hello";
}
}
反例 ❌:
public class NoHorizontalAlignmentNegative {
private int x;
private String name;
public void method1() {
int a = 1;
String str = "Hello";
}
}
4.7 括号分组
推荐在代码中使用分组括号,以此明确运算顺序,避免产生歧义。除非代码的作者和审阅者都一致认为可以安全地省略括号,否则应尽量使用括号来清晰表达代码的逻辑。
正例 ✅: 明确运算顺序
public static void main(String[] args) {
// 使用括号明确运算顺序,避免歧义
int result = (2 + 3) * 4;
boolean condition = (x > 5) && (y < 10);
}
反例 ❌: 省略括号导致歧义
public class ParenthesesGroupingNegative {
public static void main(String[] args) {
// 省略括号可能导致误解
int result = 2 + 3 * 4;
boolean condition = x > 5 && y < 10;
}
}
4.8 特定结构
4.8.1 枚举类
对于枚举类,枚举常量后的逗号可以选择换行,并且允许添加额外的空行来提升代码的可读性。对于简单的枚举,可以将其格式化为类似数组初始化的形式。
正例 ✅: 在 Answer
枚举中,枚举常量 YES
后逗号换行,并且添加了额外空行,使代码结构更清晰;Suit
枚举是简单枚举,格式化为类似数组初始化的形式,符合规范。
// 枚举常量后逗号换行并添加额外空行
private enum Answer {
YES {
@Override
public String toString() {
return "yes";
}
},
NO,
MAYBE
}
// 简单枚举格式化类似数组初始化
private enum Suit { CLUBS, HEARTS, SPADES, DIAMONDS }
反例 ❌:Answer
枚举没有在逗号后换行和添加额外空行,代码显得拥挤,可读性较差;Suit
枚举是简单枚举,不需要如此复杂的换行格式,没有采用类似数组初始化的简洁形式。
// 没有在逗号后换行且没有额外空行,代码可读性差
private enum Answer { YES { @Override public String toString() { return "yes"; } }, NO, MAYBE }
// 简单枚举没有采用合适的格式化
private enum Suit {
CLUBS,
HEARTS,
SPADES,
DIAMONDS
}
4.8.2 变量声明
4.8.2.1 单变量声明
每个声明语句只应该声明一个变量,不过 for
循环头是个例外。
正例 ✅:
// 每个声明只声明一个变量
int a;
int b;
反例 ❌:
int a, b;
4.8.2.2 按需声明
局部变量应该在首次使用它的位置附近进行声明。这样可以使代码的逻辑更加清晰,减少不必要的变量作用域。
正例 ✅:result
变量在 if
语句块中首次使用前进行声明,符合局部变量应在首次使用处附近声明的规则。
public class DeclareWhenNeededPositive {
public static void main(String[] args) {
// 在首次使用处附近声明局部变量
boolean condition = true;
if (condition) {
int result = calculate();
System.out.println("Result: " + result);
}
}
private static int calculate() {
return 10;
}
}
反例 ❌:result
变量在 main
方法开始处就声明了,但直到 if
语句块中才使用,没有在首次使用处附近声明,不符合规范。
public class DeclareWhenNeededNegative {
public static void main(String[] args) {
// 没有在首次使用处附近声明
int result;
boolean condition = true;
if (condition) {
result = calculate();
System.out.println("Result: " + result);
}
}
private static int calculate() {
return 10;
}
}
4.8.3 数组
4.8.3.1 数组初始化器
数组初始化器可以格式化为类似代码块的形式,这样可以使数组元素的结构更加清晰,提高代码的可读性。
示例:array1
和 array2
的初始化器都采用了类似代码块的格式化方式,每个元素可以单独成行,array3
中也可以多个元素在一行,提高了代码的可读性。
public class ArrayInitializerPositive {
public static void main(String[] args) {
// 数组初始化器格式化类似代码块
int[] array1 = {
1,
2,
3
};
int[] array2 = {
4, 5,
6
};
int[] array3 = {1, 2, 3};
}
}
4.8.3.2 禁止C风格数组声明
在 Java 中,应该使用 String[] args
这种形式来声明数组,而不是 String args[]
,以遵循 Java 的编程规范。
正例 ✅:
String[] args;
反例 ❌:
String args[]; // C风格声明
4.8.4 switch语句
4.8.4.1 缩进
switch
代码块需要进行 +2
的缩进,而 case
标签后的代码同样需要缩进 +2
。这样可以清晰地展示 switch
语句的层次结构。
正例 ✅:switch
代码块整体缩进了 +2
,case
标签后的代码也缩进了 +2
,符合缩进规则。
public class SwitchIndentationPositive {
public static void main(String[] args) {
int num = 2;
switch (num) {
case 1:
System.out.println("Number is 1");
break;
case 2:
System.out.println("Number is 2");
break;
default:
System.out.println("Number is neither 1 nor 2");
}
}
}
反例 ❌:switch
代码块和 case
标签后的代码都没有进行正确的缩进,不符合规则。
public class SwitchIndentationNegative {
public static void main(String[] args) {
int num = 2;
switch (num) {
case 1:
System.out.println("Number is 1");
break;
case 2:
System.out.println("Number is 2");
break;
default:
System.out.println("Number is neither 1 nor 2");
}
}
}
4.8.4.2 贯穿注释
如果 case
语句不是最后一个,并且需要执行贯穿操作(即不使用 break
语句跳出 switch
语句),则需要使用 // fall through
注释来明确说明这种贯穿意图,避免代码的逻辑混淆。
正例 ✅: 在 case 1
语句块末尾添加了 // fall through
注释,表明执行会继续到下一个 case
,符合贯穿注释的规则。
public class SwitchFallThroughPositive {
public static void main(String[] args) {
int num = 1;
switch (num) {
case 1:
System.out.println("Number is 1");
// fall through
case 2:
System.out.println("Number is 2 or 1");
break;
default:
System.out.println("Number is neither 1 nor 2");
}
}
}
反例 ❌:case 1
语句块没有添加 // fall through
注释,无法清晰表明代码的贯穿意图,不符合规则。
public class SwitchFallThroughNegative {
public static void main(String[] args) {
int num = 1;
switch (num) {
case 1:
System.out.println("Number is 1");
case 2:
System.out.println("Number is 2 or 1");
break;
default:
System.out.println("Number is neither 1 nor 2");
}
}
}
4.8.4.3 default标签
switch
语句必须包含 default
标签,不过当 switch
语句针对枚举类型,并且已经覆盖了该枚举类型的所有可能值时,可以省略 default
标签。
正例 ✅: 第一个示例中 switch
语句包含了 default
标签;第二个示例中针对枚举类型 Color
的 switch
语句覆盖了所有可能的值,因此省略 default
标签,都符合规则。
public class SwitchDefaultPositive {
public static void main(String[] args) {
int num = 3;
switch (num) {
case 1:
System.out.println("Number is 1");
break;
case 2:
System.out.println("Number is 2");
break;
default:
System.out.println("Number is neither 1 nor 2");
}
}
}
// 枚举类型全覆盖时省略 default
enum Color { RED, GREEN, BLUE }
public class SwitchEnumPositive {
public static void main(String[] args) {
Color color = Color.RED;
switch (color) {
case RED:
System.out.println("Color is red");
break;
case GREEN:
System.out.println("Color is green");
break;
case BLUE:
System.out.println("Color is blue");
break;
}
}
}
反例 ❌:switch
语句没有包含 default
标签,且不是枚举类型全覆盖的情况,不符合规则。
public class SwitchDefaultNegative {
public static void main(String[] args) {
int num = 3;
switch (num) {
case 1:
System.out.println("Number is 1");
break;
case 2:
System.out.println("Number is 2");
break;
}
}
}
4.8.5 注解
4.8.5.1 类型注解
类型注解应该紧挨着被注释的类型。如果注释是一种类型使用注解(即使用 @Target(ElementType.TYPE_USE)
),则更要严格遵循此规则。
正例 ✅: 类型注解 @Optional
紧接在被注解的类型 String
前,符合规则。
import java.util.Optional;
public class TypeAnnotationPositive {
private @Optional String nullableString;
public @Optional String getNullableString() {
return nullableString;
}
}
反例 ❌: 类型注解 @Optional
没有紧接在被注解的类型 String
前,不符合规则。
import java.util.Optional;
public class TypeAnnotationNegative {
private String @Optional nullableString;
public String @Optional getNullableString() {
return nullableString;
}
}
4.8.5.2 类注解
类注解应当在文档块之后,每行只放置一个注解。这样的排版方式能够让代码更加清晰易读,便于开发者快速识别和理解类所使用的注解。
正例 ✅: 类注解 @Deprecated
和 @SuppressWarnings("unchecked")
在文档块后每行一个,符合规则。
/**
* This is a sample class.
*/
@Deprecated
@SuppressWarnings("unchecked")
public class ClassAnnotationPositive {
// Class body
}
反例 ❌: 类注解 @Deprecated
和 @SuppressWarnings("unchecked")
没有每行一个,不符合规则。
/**
* This is a sample class.
*/
@Deprecated @SuppressWarnings("unchecked")
public class ClassAnnotationNegative {
// Class body
}
4.8.5.3 方法和构造函数注释
方法注解的规则与类注解基本一致,不过对于无参注解,可以选择与方法签名写在同一行。这样的规则既保证了代码的规范性,又在一定程度上提供了灵活性。
正例 ✅: 第一个方法 toString
的注解 @Deprecated
和 @Override
每行一个;第二个方法 hashCode
的单无参注解 @Override
与签名同行,都符合规则。
public class MethodAnnotationPositive {
@Deprecated
@Override
public String toString() {
return super.toString();
}
@Override public int hashCode() {
return super.hashCode();
}
}
反例 ❌: 方法 toString
的注解 @Deprecated
和 @Override
没有每行一个,不符合规则。
public class MethodAnnotationNegative {
@Deprecated @Override
public String toString() {
return super.toString();
}
}
4.8.5.4 字段注解
字段注解可以在同一行放置多个。这种方式能够在不影响代码可读性的前提下,简洁地为字段添加多个注解。
正例 ✅: 字段注解 @NonNull
和 @ReadOnly
在字段声明处同行多个,符合规则。
public class FieldAnnotationPositive {
@NonNull @ReadOnly private String field;
}
反例 ❌: 虽然这种写法本身语法正确,但没有按照规则将字段注解写在同一行,不符合规则。
public class FieldAnnotationNegative {
@NonNull
@ReadOnly
private String field;
}
4.8.6 注释
4.8.6.1 块注释样式
实现注释可以采用 /*...*/
或 //
这两种风格。当使用多行 /*...*/
注释时,后续行需要以 *
进行对齐,这样可以使注释看起来更加整齐美观,增强代码的可读性。
正例 ✅: 多行 /*...*/
注释后续行以 *
对齐,单行注释使用 //
风格,符合规则。
/*
* This is a multi - line comment.
* Each subsequent line starts with an asterisk aligned.
*/
public class CommentPositive {
// This is a single - line comment.
public void method() {
/*
* Another multi - line comment
* in the method body.
*/
}
}
反例1 ❌: 多行 /*...*/
注释后续行没有以 *
对齐,不符合规则。
/*
This is a wrong multi - line comment.
The subsequent lines don't start with an aligned asterisk.
*/
public class CommentNegative {
// This is a single - line comment.
public void method() {
/*
Another multi - line comment
in the method body without proper alignment.
*/
}
}
反例2 ❌:
/**********************************
* 带边框的注释(不推荐)
**********************************/
4.8.7 修饰符
修饰符的顺序应严格按照 JLS
(Java Language Specification
,Java
语言规范)的要求,即 public protected private abstract default static final transient volatile synchronized native strictfp
。遵循这一规范可以使代码的结构更加清晰,便于开发者理解和维护。
正例 ✅: 修饰符顺序按照 JLS
规范,如 public static final
、private transient volatile
、public synchronized
,符合规则。
public class ModifierPositive {
public static final int CONSTANT = 10;
private transient volatile int field;
public synchronized void method() {
// Method body
}
}
反例 ❌: 修饰符顺序没有按照 JLS
规范,如 static public final
、transient private volatile
、synchronized public
,不符合规则。
public class ModifierNegative {
static public final int CONSTANT = 10;
transient private volatile int field;
synchronized public void method() {
// Method body
}
}
4.8.8 数字字面量
对于 long
类型的数字字面量,必须使用大写的 L
作为后缀,例如 3000000000L
。使用大写 L
可以避免与数字 1
产生混淆,提高代码的准确性和可读性。
正例 ✅:long
类型的数字字面量使用了大写 L
后缀,符合规则。
public static final long LONG_VALUE = 3000000000L;
反例 ❌:long
类型的数字字面量使用了小写 l
后缀,容易与数字 1
混淆,不符合规则。
public static final long LONG_VALUE = 3000000000l;
5 命名规范
5.1 通用规则
标识符的命名应仅使用 ASCII
字母、数字以及下划线(在少数情况下使用),同时要禁止使用特殊前缀或后缀。这样做的目的是确保代码在不同的环境和工具中都能被正确识别和处理,提高代码的通用性和可移植性。
正例 ✅: 类名、变量名和方法名仅使用了 ASCII 字母、数字和下划线,没有特殊前缀或后缀,符合通用规则。
// 仅使用 ASCII 字母、数字和下划线
public class ValidNaming {
private int validVariable1;
public void validMethod() {
// 方法体
}
}
反例 ❌: 类名使用了非 ASCII 字符,变量名和方法名使用了特殊前缀,不符合通用规则。
// 使用了非 ASCII 字符和特殊前缀
public class 无效命名 {
private int $invalidVariable;
private String #invalid_variable;
public void @invalidMethod() {
// 方法体
}
}
5.2 分类规则
5.2.1 包名
包名应全部使用小写字母,当包名由多个连续单词组成时,直接将这些单词拼接在一起,例如 com.example.deepspace
。这种命名方式有助于保持包名的简洁性和一致性,方便开发者进行管理和查找。
正例 ✅:
package com.example.deepspace;
public class PackageExample {
// 类体
}
反例 ❌: 包名中包含大写字母,不符合全小写的规则。
package com.Example.DeepSpace;
public class BadPackageExample {
// 类体
}
5.2.2 类名
类名应采用大驼峰式(UpperCamelCase
)命名,通常使用名词来描述类的功能或用途。对于测试类,应以 Test
结尾,
正例 ✅: 大驼峰
public class UserService { ... }
反例 ❌: 小驼峰或蛇形
public class userService { ... }
public class User_Service { ... }
5.2.3 方法名
方法名应使用小驼峰式(lowerCamelCase
)命名,通常使用动词来描述方法的操作。对于测试方法,可以使用下划线分隔逻辑组件,以便更清晰地表达测试的逻辑和目的。
正例 ✅:
public void calculateTotal() { ... }
反例 ❌: 首字母大写
public void CalculateTotal() { ... } //
5.2.4 常量名
常量名应采用全大写蛇形(UPPER_SNAKE_CASE
)命名,这里的常量指的是不可变的静态 final
字段。这种命名方式能够清晰地表明该字段是一个常量,避免被意外修改。
正例 ✅: 全大写蛇形
public static final int MAX_RETRIES = 3;
反例 ❌: 非全大写
public static final int maxRetries = 3;
5.2.5 非常量字段名
非常量字段名应使用小驼峰式命名,通常使用名词来描述字段所代表的对象或属性。
正例 ✅: 小驼峰
private int studentCount;
反例 ❌: 下划线或大写开头
private int StudentCount;
private int student_count;
5.2.6 参数名
参数名应采用小驼峰式命名,在公开方法中要避免使用单字符作为参数名,因为单字符参数名往往不能清晰地表达参数的含义,会降低代码的可读性。而在私有方法中,由于其作用范围相对较小,可以使用单字符参数名。
正例 ✅: 开方法 publicMethod
的参数名 customerId
和 customerName
采用小驼峰式,避免了单字符;私有方法 privateMethod
使用单字符参数是允许的,符合规则。
public class ParameterNamingPositive {
public void publicMethod(int customerId, String customerName) {
// 方法体
}
private void privateMethod(int x) {
// 私有方法可使用单字符参数
}
}
反例 ❌: 公开方法 publicMethod
使用了单字符参数 i
和 n
,不符合规则。
public class ParameterNamingNegative {
public void publicMethod(int i, String n) {
// 公开方法使用单字符参数,不符合规则
}
}
5.2.7 局部变量名
局部变量名应采用小驼峰式命名,并且不将其视为常量。这样可以与常量名进行区分,避免混淆。
正例 ✅: 局部变量 currentCount
和 userName
采用小驼峰式,不视为常量,符合规则。
public class LocalVariableNamingPositive {
public void method() {
int currentCount = 0;
String userName = "John";
// 局部变量使用小驼峰式
}
}
反例 ❌: 局部变量使用了常量命名风格(全大写),不符合规则。
public class LocalVariableNamingNegative {
public void method() {
int CURRENT_COUNT = 0;
String USER_NAME = "John";
// 局部变量使用常量命名风格,不符合规则
}
}
5.2.8 类型变量名
每个类型变量都应遵循以下两种命名样式之一:
- 单个大写字母,后跟一个数字(可选),例如
E
、T
、X
、T2
。 - 用于类的形式名称,后跟大写字母
T
,例如RequestT
、FooBarT
。
正例 ✅: 类的类型变量 T
是单大写字母,方法的类型变量 RequestT
是类名 Request
加 T
,符合规则。
import java.util.List;
public class TypeVariableNamingPositive<T> {
public <RequestT> List<RequestT> processRequests(List<RequestT> requests) {
return requests;
}
}
反例 ❌: 类的类型变量 t
是小写字母,方法的类型变量 requestT
首字母未大写,不符合规则。
import java.util.List;
public class TypeVariableNamingNegative<t> {
public <requestT> List<requestT> processRequests(List<requestT> requests) {
return requests;
}
}
5.3 驼峰式定义
将短语转换为 ASCII
编码后,拆分其中的单词,将每个单词全部转换为小写,然后将每个单词的首字母大写并拼接起来。
正例 ✅:UserRegistrationSystem
是将短语 “user registration system” 转换为 ASCII 后拆分单词,全小写后首字母大写拼接。
// 处理普通短语
public class UserRegistrationSystem {
// 类名采用驼峰式
}
// 处理特殊缩写
public class IosDeviceManager {
}
反例 ❌:userregistrationsystem
未采用驼峰式命名。
// 未正确处理驼峰式
public class userregistrationsystem {
// 类名未采用驼峰式
}
// 错误处理特殊缩写
public class IOSDeviceManager {
}
示例 | 正例 | 错误 |
---|---|---|
"XML HTTP request" | XmlHttpRequest | XMLHTTPRequest |
"new customer ID" | newCustomerId | newCustomerID |
"inner stopwatch" | innerStopwatch | innerStopWatch |
"supports IPv6 on iOS?" | supportsIpv6OnIos | supportsIPv6OnIOS |
"YouTube importer" | YouTubeImporter YoutubeImporter | |
"Turn on 2SV" | turnOn2sv | turnOn2Sv |
注意:有些单词在英语中带有歧义的连字符,例如 “Non-Empty
” 和 “NonEmpty
” 都是正确的,因此方法名称 checkNonempty
和 checkNonEmpty
同样都是正确的。这种情况在命名时需要根据团队的约定或项目的整体风格来选择合适的命名方式。
6 编程实践
6.1 @Override
必须使用
在合法的情况下,必须使用 @Override
注解(当父方法为 @Deprecated
方法时除外)。使用该注解能够明确地表明一个方法是对父类方法的覆盖,增强代码的可读性和可维护性,同时编译器也能借此进行检查,避免因拼写错误等原因导致意外的方法重载。
正例 ✅: 明确标记覆盖
@Override
public String toString() { ... }
反例 ❌: 省略注解
public String toString() { ... }
6.2 不忽略捕获异常
当捕获到异常时,不能简单地忽略它。如果在 catch
块中确实不需要执行任何操作,那么必须在注释中详细解释这样做的原因,以保证代码的逻辑清晰,便于其他开发者理解。
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
或以 expected
开头,那么可以忽略该异常而无需添加注释。这是一种常见的测试惯用法,用于确保被测试的代码确实会引发预期类型的异常,所以在此情况下不需要额外注释。
try {
emptyStack.pop();
fail();
} catch (NoSuchElementException expected) {
}
除上述例外情况外,捕获到的异常必须进行处理。
正例 ✅: 记录或处理异常
try {
readFile();
} catch (IOException e) {
log.error("File read failed", e);
}
反例 ❌: 忽略异常
try {
readFile();
} catch (IOException e) {}
6.3 静态成员类限定
对于静态成员,应该通过类名来引用,而不是通过类的实例来引用。这样做可以使代码的意图更加清晰,符合静态成员属于类本身而非类的某个实例的特性。
正例 ✅: 在 main
方法中,通过 StaticNestedClass
类名直接引用了其静态成员 staticValue
和 staticMethod
,符合通过类名而非实例引用静态成员的规则。
public class StaticMemberAccessPositive {
// 定义一个包含静态成员的类
static class StaticNestedClass {
public static int staticValue = 10;
public static void staticMethod() {
System.out.println("This is a static method.");
}
}
public static void main(String[] args) {
// 通过类名引用静态成员
int value = StaticNestedClass.staticValue;
System.out.println("Static value: " + value);
StaticNestedClass.staticMethod();
}
}
反例 ❌: 在 main
方法中,创建了 StaticNestedClass
的一个实例 instance
,并通过该实例引用了静态成员 staticValue
和 staticMethod
,违反了通过类名而非实例引用静态成员的规则。
public class StaticMemberAccessNegative {
// 定义一个包含静态成员的类
static class StaticNestedClass {
public static int staticValue = 10;
public static void staticMethod() {
System.out.println("This is a static method.");
}
}
public static void main(String[] args) {
// 通过实例引用静态成员,不符合规则
StaticNestedClass instance = new StaticNestedClass();
int value = instance.staticValue;
System.out.println("Static value: " + value);
instance.staticMethod();
}
}
6.4 禁用finalize
在 Java 编程里,应避免覆盖 Object
类的 finalize
方法。finalize
方法是 Java
垃圾回收机制中的一部分,原设计目的是在对象被垃圾回收之前执行一些资源清理操作,但它的执行时机和行为具有不确定性,可能会引发性能问题,还会让代码的逻辑变得复杂,因此不推荐使用。
正例 ✅: NoFinalizePositive
类没有覆盖 Object.finalize
方法,符合不要覆盖 Object.finalize
的规则。
public class NoFinalizePositive {
public void doSomething() {
System.out.println("Doing something...");
}
}
反例 ❌:NoFinalizeNegative
类覆盖了 Object.finalize
方法,违反了不要覆盖 Object.finalize
的规则。在实际开发中,finalize
方法的行为难以预测,并且可能会导致性能问题,因此不建议使用。
public class NoFinalizeNegative {
@Override
protected void finalize() throws Throwable {
try {
System.out.println("Finalize method is called.");
} finally {
super.finalize();
}
}
public void doSomething() {
System.out.println("Doing something...");
}
}
7 Javadoc
规范
7.1 格式
7.1.1 通用形式
Javadoc
块的基本格式如下例所示:
/**
* 多行Javadoc文本...
*/
public int method(String p1) { ... }
单行的示例如下:
/** 简短描述 */
当整个 Javadoc
块(包含注释标记)能够完整地放在一行上,并且没有 @return
等块标签时,就可以采用单行格式。
正例 ✅: 类 MultiLineJavadocExample
使用了多行 Javadoc
格式,方法 singleLineMethod
因为整个 Javadoc
块可以放在一行且没有块标签,使用了单行格式,符合通用形式的规则。
/**
* 这是一个多行的 Javadoc 示例,用于描述类的功能。
* 这里可以详细说明类的用途和一些注意事项。
*/
public class MultiLineJavadocExample {
/** 简短描述该方法的作用 */
public int singleLineMethod(String p1) {
return 0;
}
}
反例 ❌: 类注释和方法注释都没有使用正确的 Javadoc
格式,类注释缺少开头的第二个星号,方法注释没有使用 /** */
包裹,不符合通用形式的要求。
/*
* 这不是标准的 Javadoc 格式,缺少开头的第二个星号。
*/
public class BadJavadocFormatExample {
/* 简短描述,不是正确的单行 Javadoc 格式 */
public int badSingleLineMethod(String p1) {
return 0;
}
}
7.1.2 段落
段落之间需有一个空行(即仅包含对齐的前导星号 *
的行),并且在块标签组(若存在)之前也应有这样的空行。除第一个段落外,后续每个段落的首个单词前都要添加 <p>
标签,且该标签后不添加空格。对于其他块级元素的 HTML 标记,像 <ul>
或 <table>
,其前面无需添加 <p>
标签。
正例 ✅: 段落之间用仅包含对齐前导星号 *
的空行分隔,除首段外,后续段落的第一个单词之前添加了 <p>
标签,且块标签组 @param
前有分隔空行,符合段落格式规则。
/**
* 这是 Javadoc 的首段,描述类的基本功能。
*
* * <p>这是第二段,进一步详细说明类的其他特性和使用场景。
*
* * <p>这是第三段,可能会提及一些注意事项或相关的额外信息。
*
* @param p1 方法的参数说明
*/
public class ParagraphExample {
public void methodWithParams(String p1) {
// 方法体
}
}
反例 ❌: 段落之间没有用仅包含对齐前导星号 *
的空行分隔,且除首段外的段落没有添加 <p>
标签,不符合段落格式要求。
/**
* 这是首段
* 这是第二段,没有用空行分隔且未加 <p> 标签
* 这是第三段,同样不符合要求
* @param p1 方法的参数说明
*/
public class BadParagraphExample {
public void methodWithParams(String p1) {
// 方法体
}
}
7.1.3 块标签
标准块标签应按照 @param
、@return
、@throws
、@deprecated
的顺序排列,若标签内容需要续行,则缩进 4
个及以上空格。
正例 ✅: 块标签按照 @param
, @return
, @throws
的顺序排列,续行缩进超过 4 个空格(这里实际是正常对齐),符合块标签的格式要求。
/**
* 该方法用于计算两个整数的和。
*
* * <p>详细说明该方法的实现逻辑和使用场景。
*
* @param a 第一个整数
* @param b 第二个整数
* @return 两个整数的和
* @throws IllegalArgumentException 如果输入的参数为负数
*/
public int add(int a, int b) throws IllegalArgumentException {
if (a < 0 || b < 0) {
throw new IllegalArgumentException("参数不能为负数");
}
return a + b;
}
反例 ❌: 块标签的顺序混乱,没有按照 @param
, @return
, @throws
的顺序排列,不符合规则。
/**
* 该方法用于计算两个整数的和。
* @throws IllegalArgumentException 如果输入的参数为负数
* @param a 第一个整数
* @return 两个整数的和
* @param b 第二个整数
*/
public int badAdd(int a, int b) throws IllegalArgumentException {
if (a < 0 || b < 0) {
throw new IllegalArgumentException("参数不能为负数");
}
return a + b;
}
7.2 摘要片段
每个 Javadoc
块都要以一个简短的摘要片段作为开头,此注释应简洁明了。
提示:常见的错误写法是 /** @return the customer ID */
,正确的写法应为 /** Returns the customer ID. */
。
正例 ✅:
/**
* 计算两个数的乘积。
*
* * <p>该方法接收两个双精度浮点数作为参数,返回它们的乘积。
*/
反例 ❌: 可省略此方法3个字。
/**
* 此方法返回两个数的乘积。
*
* * <p>该方法接收两个双精度浮点数作为参数,返回它们的乘积。
*/
7.3 使用场景
必须为所有的 public
类以及 protected
公共成员添加 Javadoc
。
7.3.1 例外:自解释成员
(如简单的 getter
方法)
正例 ✅: 简单的 getter
方法 getName
没有添加 Javadoc
,符合自解释成员的例外规则;非自解释的 setter
方法 setName
有 Javadoc
注释。
public class BadGetterExample {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
反例 ❌: 简单的 getter
方法 getName
添加了不必要的 Javadoc
注释,虽然本身没有错误,但不符合自解释成员可不添加 Javadoc
的规则;setter
方法 setName
没有添加 Javadoc
注释,不符合必须为公共成员添加 Javadoc
的规则。
public class BadGetterExample {
private String name;
/**
* 获取用户的名称。
*
* @return 用户的名称
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
7.3.2 例外:覆盖方法
正例 ✅: 子类 ChildClass
中的 doSomething
方法覆盖了父类的方法,没有添加 Javadoc
注释,符合覆盖方法的例外规则。
public class ParentClass {
public void doSomething() {
// 父类方法
}
}
public class ChildClass extends ParentClass {
@Override
public void doSomething() {
// 覆盖方法,可不添加 Javadoc
super.doSomething();
}
}
反例 ❌: 子类 BadChildClass
中的 doSomething
方法覆盖了父类的方法,添加了不必要的 Javadoc
注释,不符合覆盖方法可不添加 Javadoc
的规则。
public class ParentClass {
public void doSomething() {
// 父类方法
}
}
public class BadChildClass extends ParentClass {
/**
* 这是一个不必要的覆盖方法注释。
*/
@Override
public void doSomething() {
super.doSomething();
}
}