基于Oracle, Google , Twitter 和Spring Framework的 编码 标准
本文的目的是为您提供基于Oracle,Google,Twitter和Spring Framework等技术巨头的编码标准的”做与不做”的简要概述,换句话说,您更喜欢和避免。
您可能同意或不同意此处介绍的一些最佳做法,并且只要有某种编码标准就可以了。
为什么首先要编码标准?如果您使用Google进行搜索,有很多充分的理由,下面将为您提供以下插图
编码标准文档可能冗长而乏味。本文从Google,Oracle,Twitter和Spring的编码约定中挑选了一些零碎的内容,其目的是为您提供易于理解且较无聊的做法,以使您的代码易于阅读和维护。
几乎总是您会加入使用现有软件的团队,并且大多数作者很有可能离开或切换到其他项目,这使您陷入困扰人类的部分代码。
让我们深入了解各种编码标准中的最佳做法。
Java 源文件
关于Java源文件,以下内容被视为最佳实践:
· 源文件长度小于2,000行代码
· 源文件的组织方式包括文档注释,程序包声明,后跟类注释,分组导入(最后一个静态),类/接口签名等,如下所示
package com.example.model;
/**
* Implementation-free perspective to be read by developers
* who might not necessarily have the source code at hand
*
* @author x,y,z
* @date
* @version
* @copyright
*
*/
import com.example.util.FileUtil;
/*
* Optional class specific comment
*
*/
public class SomeClass {
// Static variables in order of visibility
public static final Integer PUBLIC_COUNT = 1;
static final Integer PROTECTED_COUNT =;
private static final Integer PRIVATE_COUNT =;
// Instance variables in order of visibility
public String name; String postalCode;
private String address;
// Constructor and overloaded in sequential order
public SomeClass() {}
public SomeClass(String name) {
this.name = name;
}
// Methods
public String doSomethingUseful() {
return "Something useful";
}
// getters, setters, equals, hashCode and toString at the end
}
命名
类和接口的名称均为CamelCase,建议使用整个单词,并避免使用缩写词/缩写。例如Raster类或ImageSprite类
· 包-在com.deepSpace或com.deep_space上命名com.deepspace
· 文件-名称为CamelCase,以.java结尾,与类名匹配。每个文件有一个公共类,文件中有每个顶级类
· 方法-名称应为混合大小写的动词,每个内部单词用大写字母表示,例如run();或runFast();
· 常量-应该用大写字母” _”分隔每个单词,例如int MIN_WIDTH = 44;并且int MAX_WIDTH = 99;
· 变量-一个名称,告诉程序的读者变量代表什么,即,如果要存储测试等级,则选择等级vs var1。变量名要简短,避免包含元数据。
// Prefer (✔️) - variable names short and describe what it stores
int schoolId;
int[] filteredSchoolIds;
int[] uniqueSchooldIds;
Map<Integer, User> usersById;
String value;
//Avoid (x) - Too detailed variable naming
int schoolIdentificationNumber;
int[] userProvidedSchoolIds;
int[] schoolIdsAfterRemovingDuplicates;
Map<Integer, User> idToUserMap;
String valueString;
请记住,变量名应该简短,并易于告诉读者它代表什么值。用你的判断。
倾向性和避免
格式和缩进都是组织代码以使其易于阅读,其中包括间距,行长,换行和换行等
· 缩进-使用2或4个空格或制表符并保持一致
· 行长-取决于可读性的影响,最多70至120个字符。消除水平滚动和在逗号和 运算符 后放置换行符的需求非常重要。
方法-以下是最佳做法清单
// Prefer (✔️) Line breaks are arbitrary and break after a comma.
String downloadAnInternet(Internet internet,
Tubes tubes,
Blogosphere blogs,
Amount< Long , Data> bandwidth) {
tubes.download(internet);
}
// Avoid (x) Hard to diff method args to method body
String downloadAnInternet(Internet internet,
Tubes tubes,
Blogosphere blogs,
Amount<Long, Data> bandwidth) {
tubes.download(internet);
}
// Prefer (✔️) Add (double of 2 or 4) spaces for deep indent
private static synchronized horkingLongMethodName(int anArg,
Object anotherArg,
String yetAnotherArg,
Object andStillAnother) {
...
}
// Prefer (✔️) Easy scanning and extra column space.
public String downloadAnInternet( Internet internet,
Tubes tubes,
Blogosphere blogs,
Amount<Long, Data> bandwidth) {
tubes.download(internet);
...
}
> A unit test would have caught that
如果检查-IMO编写格式正确的代码可以使作者和代码审阅者容易发现打字错误和错误,请参见下文:
// Avoid (x) Do not omit {}
if ( condition )
statement;
// Avoid (x)
if (x <) negative(x);
// Avoid (x)
if (a == b && c == d) {
...
}
// Prefer (✔️)
if ((a == b) && (c == d)) {
...
}
// Prefer (✔️)
if (condition) {
statements ;
} else if (condition) {
statements;
} else if (condition) {
statements;
}
// Avoid (x)
if ((condition && condition2)
|| (condition && condition4)
||!(condition && condition6)) { //BAD WRAPS
doSomethingAboutIt(); //MAKE THIS LINE EASY TO MISS
}
// Prefer (✔️)
if ((condition && condition2)
|| (condition && condition4)
||!(condition && condition6)) {
doSomethingAboutIt();
}
三元运算符-以下是推荐做法
alpha = (aLongBooleanExpression) ? beta : gamma;
alpha = (aLongBooleanExpression) ? beta
: gamma;
alpha = (aLongBooleanExpression)
? beta
: gamma;
Switch-切换时,最佳做法是
· 即使没有代码也总是有默认 default 情况
· 使用 /* falls through */ 表示控件属于下一种情况
switch (condition) {
case ABC:
statements;
/* falls through */ case DEF:
statements;
break;
default:
statements;
break;
}
异常消息-引发异常时,这里是缩进和缩进不好的消息的示例。
// Avoid (x) - Not easy to read
throw new IllegalStateException("Failed to process request " + request.getId()
+ " for user " + user.getId() + " query: '" + query.getText()
+ "'");
// Prefer (✔️) - Fairly easier to read
throw new IllegalStateException("Failed to process"
+ " request " + request.getId()
+ " for user " + user.getId()
+ " query: '" + query.getText() + "'");
迭代器和流-流变得越来越普遍,有时可能非常复杂,因此,缩进以便于阅读非常重要。
// Avoid (x) - Not easy to read
Iterable<Module> modules = ImmutableList.<Module>builder().add(new LifecycleModule())
.add(new AppLauncherModule()).addAll(application.getModules()).build();
// Prefer (✔️) - Fairly easier to read
Iterable<Module> modules = ImmutableList.<Module>builder()
.add(new LifecycleModule())
.add(new AppLauncherModule())
.addAll(application.getModules())
.build();
> Just follow a coding standard — any really
声明和分配-建议每行一个声明,因为它鼓励注释,如下所示。
// Prefer (✔️)
int level; // indentation level
int sizeMeter; // size of table
// Avoid (x) in favour of above
int level, sizeMeter;
// Prefer (✔️) - Include unit in variable name or type
long pollIntervalMs;
int fileSizeGb;
Amount<Integer, Data> fileSize;
// Avoid (x) mixing types
int foo, fooarray[];
// Avoid (x) - Do not separate with comma
Format.print(System.out, “error”), exit();
// Avoid (x) multiple assignment
fooBar.fChar = barFoo.lchar = 'c';
// Avoid (x) embedded assignments in attempt to increase performance // or save a line. I am guilty of doing this :(
d = (a = b + c) + r;
// Prefer (✔️) over above
a = b + c;
d = a + r;
// Prefer (✔️)
String[] args
// Avoid (x)
String args[]
// Prefer (✔️) Long use "L" instead of "l" to avoid confusion with
long timeout =L;
// Avoid (x) - Hard to tell last letter is l and not
long timeout =l;
仅在块的开头放置声明(块是由花括号{和}包围的代码)。不要等到首次使用变量后再声明它们。它可能会使笨拙的程序员感到困惑,并妨碍范围内的代码可移植性。
// Prefer (✔️) declare at the beginning of the block.
public void doSomething() {
int whatIRepresent; // beginning of method block
if (condition) {
int someFlag; // beginning of “if” block
…
}
}
同样重要的是要避免局部声明隐藏更高级别的声明,并避免造成混淆,如下所示
int count;
...
public void doSomething() {
if (condition) {
int count; // AVOID!
...
}
...
}
间距和换行符—避免以牺牲可读性为代价来保存1-2行代码的诱惑。这是有关间距和空白行的所有最佳实践(空格确实有所作为)
· 方法和Spring开发人员之间的一(1)条空行建议在构造函数,静态块,字段和内部类之后的两(2)条空行
· 空格键运算符,即使用int foo = a + b + 1; 而非 int foo = a + b + 1;
· 分隔除”.”外的所有二进制运算符。使用空格分割
· 括号” {“出现在声明语句或方法的同一行的末尾,右括号”}”本身以缩进开始
// Prefer (✔️) - Space after "while" and before "("
while (true) {
...
}
// Avoid (x) - Unlike above no space
while(true) {
...
}
// Prefer (✔️) - No space between "doSomething" and "("
public void doSomething() {
...
}
// Avoid (x) - Unlike above space
public void doSomething () {
...
}
// Prefer (✔️) - Add a space after an argument
public void doSomething(int a, int b) {
...
}
// Prefer (✔️) - Space between operand and operators (i.e. +, =)
a += c + d;
a = (a + b) / (c * d);
while (d++ = s++) {
n++;
}
文档和注释
值得一提的是,几乎所有代码都会在其整个生命周期中进行更改,除非您明确说明,否则有时您或某人会试图弄清楚打算使用复杂的代码块,方法或类做什么。现实几乎总是如下
有时,对复杂的代码,方法,类的注释不会增加任何值或无法达到其目的。为此,通常在注释时会发生这种情况。
注释应用于提供代码概述,并提供代码本身不容易获得的其他信息。让我们开始吧。有两种类型的评论
实施注释—用于注释掉代码或对代码的特定实现进行注释。
文档注释—旨在从无实现的角度描述代码规范,以供可能不一定拥有源代码的开发人员阅读。
注释的类型
共有四(4)种实施注释,如下所示
· 块注释-请参见下面的示例
· 单行注释-当注释不超过一行时
· 尾随注释-非常短的评论移到了右端
· 行尾注释—开始注释,并继续到换行符。它可以注释掉整行或仅部分行。不应在连续的多行中使用它来进行文本注释;但是,它可以在连续的多行中用于注释掉代码段。
// Block comment
/*
* Usage: Provides description of files, methods, data structures
* and algorithms. Can be used at the beginning of each file and
* before each method. Used for long comments that do not fit a
* single line. Blank line to proceed after the block comment.
*/// Single line comment
if (condition) {
/* Handle the condition. */ ...
}
// Trailing comment
if (a ==) {
return TRUE; /* special case */} else {
return isPrime(a); /* works only for odd a */}
// End of line comment
if (foo >) {
// Do a double-flip.
...
} else {
return false; // Explain why here.
}
//if (bar >) {
//
// // Do a triple-flip.
// ...
//}
//else
// return false;
文档注释(即Javadoc)
Javadoc是一种工具,它使用以/ **开头和以* /结束的注释从Java代码生成HTML文档,有关Javadoc的工作方式或阅读全文,请参阅Wikipedia。
这是一个Javadoc示例
/**
* 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;
}
}
当对具有以上代码的代码运行javadoc时,以上代码将导致HTML如下所示
> See here for more
这是一些关键标记,可用于增强生成的Java文档的质量。
@author => @author Raf
@code => {@code A<B>C}
@deprecated => @deprecated deprecation-message
@exception => @exception IOException thrown when
@link => {@link package.class#member label}
@param => @param parameter-name description
@return => What the method returns
@see => @see "string" OR @see <a ...></a>
@since => To indicate the version when a publicly accessible method is added
有关完整列表和更详细的说明,请参见
Twitter的编码标准建议不要使用@author标签
代码可以在其生命周期内无数次换手,并且源文件的原始作者在经过多次迭代后常常是无关紧要的。我们发现最好信任提交历史记录和OWNERS文件来确定代码主体的所有权。
以下示例说明了如何按照Twitter的编码标准中的描述撰写有见地的文档注释
// Bad.
// - The doc tells nothing that the method declaration didn't.
// - This is the 'filler doc'. It would pass style checks, but
doesn't help anybody.
/**
* Splits a string.
*
* @param s A string.
* @return A list of strings.
*/List<String> split(String s);
// Better.
// - We know what the method splits on.
// - Still some undefined behavior.
/**
* Splits a string on whitespace.
*
* @param s The string to split. An {@code null} string is treated as an empty string.
* @return A list of the whitespace-delimited parts of the input.
*/List<String> split(String s);
// Great.
// - Covers yet another edge case.
/**
* Splits a string on whitespace. Repeated whitespace characters
* are collapsed.
*
* @param s The string to split. An {@code null} string is treated as an empty string.
* @return A list of the whitespace-delimited parts of the input.
*/List<String> split(String s);
写注释时专业很重要的
// Avoid (x)
// I hate xml/soap so much, why can't it do this for me!?
try {
userId = Integer.parseInt(xml.getField("id"));
} catch (NumberFormatException e) {
...
}
// Prefer (✔️)
// TODO(Jim): Tuck field validation away in a library.
try {
userId = Integer.parseInt(xml.getField("id"));
} catch (NumberFormatException e) {
...
}
而且重要的是要记住,除非实现已更改,否则不要记录重写的方法。
还有几点需要牢记
· 避免使用通配符导入-如Twitter的编码标准中所述,它使类的来源变得不清楚。我在一个由Eclipse和IntelliJ用户组成的团队中工作,我发现Eclipse删除了通配符导入,而IntelliJ引入了通配符。可能有一个关闭它的选项,只是想指出两者的默认值。
· 覆盖时始终使用@Override注解
· 鼓励在字段或方法返回null时使用@Nullable
· 在将来的工作中使用特殊评论,不要忘了给自己留下参考,以便其他人知道是谁提出他们的问题,而不是猜测,删除它或检查git blame以查找添加它的人。一些IDE(例如Eclipse和IntelliJ)也有助于列出这些IDE,以便于访问和提醒。
//FIXME (Raf): An actionable message describe what needs to be done
//TODO (Raf): An actionable message describe what needs to be done
最终的目的是编写使将来的作者和维护者的生活变得轻松的代码。
> The end game
(本文由闻数起舞翻译自926 Followers的文章《A short summary of Java coding best practices》)