Java编程风格

import语句

  • 尽量不要使用通配符,如import java.util.*
  • 按照是否静态、顶级包等性质对import进行分组。

声明

类声明

  • 每个顶级类都在一个与它同名的源文件中。
  • 累成员顺序需要按照一定的逻辑关系组织在一起,而不是新的方法习惯性添加到类的结尾。
  • 当有多个同名重载函数时,这些函数应该按照一定的逻辑顺序连续放在一起。

变量声明

  • 每次只声明一个变量,不要使用组合声明。
  • 需要使用变量时才声明(在第一次使用时才声明),并尽快初始化。不要在一个代码快把局部变量都一次性声明。
  • 数组初始化:写成块状结构。
1
2
3
4
5
6
new int[] {0, 1, 2, 3};

new String[][] {
{"a", "b"},
{"c", "d"}
};

命名约定

约定

  • 包名全部小写,连续的单词只是简单连接起来,不使用下划线。
  • 类名以UpperCamelCase风格命名,通常是名词或者名词短语,接口名称有时可能是形容词或者形容词短语。
  • 测试类的命名以要测试的类的名称开始,以Test结束。
  • 方法名以lowerCamelCase风格命名。方法名通常是动词或者动词短语。下划线可能出现在JUnit测试方法名称中以分割名称的逻辑组件,一个典型的模式是:test<MethodUnderTest>_<state>
  • 常量命名模式为CONSTANT_CASE,全部字母大写,用下划线分割单词。
  • 非常量字段名,按照lowerCamelCase
  • 参数名,按照lowerCamelCase
  • 局部变量名,按照lowerCamelCase,但是可以有更为宽松的缩写。

CamelCase

驼峰式命名法分大驼峰式命名法(UpperCamelCase)和小驼峰式命名法(lowerCamelCase)。 名字从散文形式(prose form)开始:

  • 把短语转换为纯ASCII码,并且移除任何单引号。例如:”Müller’s algorithm”将变成”Muellers algorithm”。
  • 把这个结果切分成单词,在空格或其它标点符号(通常是连字符)处分割开。
    推荐:如果某个单词已经有了常用的驼峰表示形式,按它的组成将它分割开(如”AdWords”将分割成”ad words”)。 需要注意的是”iOS”并不是一个真正的驼峰表示形式,因此该推荐对它并不适用。
  • 现在将所有字母都小写(包括缩写),然后将单词的第一个字母大写:
    • 每个单词的第一个字母都大写,来得到大驼峰式命名。
    • 除了第一个单词,每个单词的第一个字母都大写,来得到小驼峰式命名。
  • 最后将所有的单词连接起来得到一个标识符。

示例:

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

格式

大括号

  • if, else, for, do, while一起使用,即使只有一条语句,也需要加上大括号。
  • 非空块时遵循Kernighan风格:
    • 左大括号前不换行
    • 左大括号后换行
    • 右大括号前换行
    • 如果右大括号是一个语句、函数体或类的终止,则右大括号后换行; 否则不换行。例如,如果右大括号后面是else或逗号,则不换行。
1
2
3
4
5
6
7
8
9
10
11
return new MyClass() {
@Override public void method() {
if (condition()) {
try {
something();
} catch (ProblemException e) {
recover();
}
}
}
};
  • 空块,可以直接使用单行void doNothing() {}

空白

垂直空白

  • 类内连续成员之间(可选,主要是用于逻辑分组)
  • 函数体内,语句的逻辑分组之间。

水平空白

  • 任何保留字与紧随其后的左括号(时:
  • 任何保留字与其前面的右大括号}
  • 任何左大括号前{
  • 任何二元或三元运算符的两侧
  • , : ;及右括号)
  • 括号内的最左最右元素的空格不是必须的。
1
2
3
4
5
6
7
8
9
@Override
public String playShiritori(Set<String> candidates, String lastResponse) {
Set<String> choices = getNextChoices(candidates, lastResponse);
if (choices.isEmpty()) {
return null;
} else {
...
}
}

用小括号来指定逻辑组:推荐

自动换行

  • 如果在非赋值运算符处断开,则应该在该符号之前断开(比如+,应该和后面的语句一起放到下一行)
  • 如果在赋值运算符出断开,通常在该符合之后断开。
  • 方法名或够照函数名与左括号留在同一行。
  • 逗号(,)与前面的内容留在同一行。

注释

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

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

JavaDoc

参考如何写Java文档注释(Java Doc Comments)

“文档注释”(Java Doc Comments)是专门为了用javadoc工具自动生成文档而写的注释,它是一种带有特殊功能的注释。

文档注释只负责描述类(class)、接口(interface)、方法(method)、构造器(constructor)、成员字段(field)。相应地,文档注释必须写在类、接口、方法、构造器、成员字段前面,而写在其他位置,比如函数内部,是无效的文档注释。

文档注释采用HTML语法规则书写,支持HTML标记(tag),同时也有一些额外的辅助标记。需要注意的是,这些标记不是给人看的(通常他们的可读性也不好),他们的作用是为了javadoc工具更好地生成最终文档。

文档注释与一般注释的最大区别在于起始符号是`/而不是///`。*

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

1
2
3
4
5
6
7
一个文档注释由两部分组成:

/**
* 描述部分(description)
*
* 标记部分(block tags)
*/

描述部分自然不用多说,所谓的标记符号指的是@param, @return, @see之类的。
或者是以下单行形式:

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

需要注意的几点:

  1. 第一行以特殊的文档定界符/** 开头
  2. 在描述段落和标记段落之间空一行,描述段落和标记段落必须分开,不能揉在一起,描述段落必须在标记段落之前
  3. 每一行注释都应该跟后面描述的类、方法等保持同样距离的缩进

描述部分(Description)

描述部分的第一行应该是一句对类、接口、方法等的简单描述,这句话最后会被javadoc工具提取并放在索引目录中。

怎么界定第一句话到哪结束了呢?答案是跟在第一个句号(英文标点)之后的tab、空行或行终结符规定了第一句的结尾。

例如下面这句注释,第一句的结尾是Prof.:

1
2
3
/**
* This is a simulation of Prof. Knuth's MIX computer.
*/

除了普通的文本之外,描述部分可以使用:

  1. HTML语法标签,例如 <b>xxx</b>
  2. javadoc规定的特殊标签,例如 {@link xxx} 。标签的语法规则是:{@标签名 标签内容}

需要注意的地方:

  1. 标签在有javadoc工具生成文档时会转化成特殊的内容,比如 {@link URL} 标签会被转化成指向URL类的超链接
  2. 如果注释包含多段内容,段与段之间需要用 <p> 分隔,空行是没用的
  3. 最后结尾行 */ 和起始行不同,这里只有一个星号
  4. 为了避免一行过长影响阅读效果,务必将每行的长度限制在80个字符以内
  5. 善用javadoc工具的复制机制避免不必要的注释:

如果一个方法覆盖了父类的方法或实现了接口种的方法,那么javadoc工具会在该注释里添加指向原始方法的链接,此外如果新方法没有注释,那么javadoc会把原始方法的注释复制一份作为其注释,但是如果新方法有注释了,就不会复制了。

注释风格:

  1. 使用 <code>关键字</code> 来强调关键字,建议强调的内容有:java关键字、包名、类名、方法名、接口名、字段名、参数名等
  2. 控制 {@link xxx} 的数量,太多的链接会使文档的可读性很差,因为读者总是跳来跳去。不要出现相同的链接,同样的链接只保留第一个;不要为java自带的内容或是常识性的内容提供链接
  3. 描述一个方法时,应当只保留方法名字,不要附带方法的参数。比如有个方法是add(Object obj),那么用add指代该方法即可,而不是add(Object obj)
  4. 英文注释可以是短语也可以是句子。如果是句子,首字母要大写,如果是短语,首字母小写。
  5. 英文注释使用第三人称,而不是第二人称。

标记部分(Tag)

1
2
3
4
5
6
7
8
9
@author(只出现在类和接口的文档中)
@version(只出现在类和接口的文档中)
@param(只出现在方法或构造器的文档中)
@return(只出现在方法中)
@exception(从java1.2之后也可以使用@thrown替代)
@see
@since
@serial(也可以使用@serialField或@serialData替代)
@deprecated

此外,如果有多个相同标记,也要注意顺序:

1
2
3
4
多个@author标记,应该按照时间顺序排列,即原作者应该排在第一个位置
多个@param标记,应该按照参数定义的顺序排列
多个@exception(或是@thrown)应该按照异常的字母顺序排列
多个@see标记,应该按照注释的逻辑顺序排列,即从最近的到最远的,从最具体的到最一般的

如果方法有参数,@param标记必须包含,而且每个对应一个参数
如果方法有返回值,@return标记必须包含

空行(即,只包含最左侧星号的行)会出现在段落之间和Javadoc标记(@XXX)之前(如果有的话)。 除了第一个段落,每个段落第一个单词前都有标签<p>,并且它和第一个单词间没有空格。

至少在每个public类及它的每个public和protected成员处使用Javadoc,除了测试类和方法。

Examples:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* Returns an Image object that can then be painted on the screen.
* The url argument must specify an absolute {@link URL}. The name
* argument is a specifier that is relative to the url argument.
* <p>
* This method always returns immediately, whether or not the
* image exists. When this applet attempts to draw the image on
* the screen, the data will be loaded. The graphics primitives
* that draw the image will incrementally paint on the screen.
*
* @param url an absolute URL giving the base location of the image
* @param name the location of the image, relative to the url argument
* @return the image at the specified URL
* @see Image
*/
public Image getImage(URL url, String name) {
try {
return getImage(new URL(url, name));
} catch (MalformedURLException e) {
return null;
}
}
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/**
* Graphics is the abstract base class for all graphics contexts
* which allow an application to draw onto components realized on
* various devices or onto off-screen images.
* A Graphics object encapsulates the state information needed
* for the various rendering operations that Java supports. This
* state information includes:
* <ul>
* <li>The Component to draw on
* <li>A translation origin for rendering and clipping coordinates
* <li>The current clip
* <li>The current color
* <li>The current font
* <li>The current logical pixel operation function (XOR or Paint)
* <li>The current XOR alternation color
* (see <a href="#setXORMode">setXORMode</a>)
* </ul>
* <p>
* Coordinates are infinitely thin and lie between the pixels of the
* output device.
* Operations which draw the outline of a figure operate by traversing
* along the infinitely thin path with a pixel-sized pen that hangs
* down and to the right of the anchor point on the path.
* Operations which fill a figure operate by filling the interior
* of the infinitely thin path.
* Operations which render horizontal text render the ascending
* portion of the characters entirely above the baseline coordinate.
* <p>
* Some important points to consider are that drawing a figure that
* covers a given rectangle will occupy one extra row of pixels on
* the right and bottom edges compared to filling a figure that is
* bounded by that same rectangle.
* Also, drawing a horizontal line along the same y coordinate as
* the baseline of a line of text will draw the line entirely below
* the text except for any descenders.
* Both of these properties are due to the pen hanging down and to
* the right from the path that it traverses.
* <p>
* All coordinates which appear as arguments to the methods of this
* Graphics object are considered relative to the translation origin
* of this Graphics object prior to the invocation of the method.
* All rendering operations modify only pixels which lie within the
* area bounded by both the current clip of the graphics context
* and the extents of the Component used to create the Graphics object.
*
* @author Sami Shaio
* @author Arthur van Hoff
* @version %I%, %G%
* @since 1.0
*/
public abstract class Graphics {

/**
* Draws as much of the specified image as is currently available
* with its northwest corner at the specified coordinate (x, y).
* This method will return immediately in all cases, even if the
* entire image has not yet been scaled, dithered and converted
* for the current output device.
* <p>
* If the current output representation is not yet complete then
* the method will return false and the indicated
* {@link ImageObserver} object will be notified as the
* conversion process progresses.
*
* @param img the image to be drawn
* @param x the x-coordinate of the northwest corner
* of the destination rectangle in pixels
* @param y the y-coordinate of the northwest corner
* of the destination rectangle in pixels
* @param observer the image observer to be notified as more
* of the image is converted. May be
* <code>null</code>
* @return <code>true</code> if the image is completely
* loaded and was painted successfully;
* <code>false</code> otherwise.
* @see Image
* @see ImageObserver
* @since 1.0
*/
public abstract boolean drawImage(Image img, int x, int y,
ImageObserver observer);


/**
* Dispose of the system resources used by this graphics context.
* The Graphics context cannot be used after being disposed of.
* While the finalization process of the garbage collector will
* also dispose of the same system resources, due to the number
* of Graphics objects that can be created in short time frames
* it is preferable to manually free the associated resources
* using this method rather than to rely on a finalization
* process which may not happen for a long period of time.
* <p>
* Graphics objects which are provided as arguments to the paint
* and update methods of Components are automatically disposed
* by the system when those methods return. Programmers should,
* for efficiency, call the dispose method when finished using
* a Graphics object only if it was created directly from a
* Component or another Graphics object.
*
* @see #create(int, int, int, int)
* @see #finalize()
* @see Component#getGraphics()
* @see Component#paint(Graphics)
* @see Component#update(Graphics)
* @since 1.0
*/
public abstract void dispose();

/**
* Disposes of this graphics context once it is no longer
* referenced.
*
* @see #dispose()
* @since 1.0
*/
public void finalize() {
dispose();
}
}

编程实践

  • switch, default不能省略。
  • 静态成员:使用类进行调用
  • @Override:能用则用
  • Finalizers: 禁用

参考Google Java编程风格指南