通过一段时间的编码(咦,我已经经历了将近20年的编程生涯,快乐的日子老是过得很快),咱们开始感谢那些好习惯。由于,你知道…html
“任何可能出错的事情,最后都会出错。”java
这就是人们为何喜欢进行“防错性程序设计”的缘由。偏执的习惯有时颇有意义,有时则不够清晰也不够聪明,也许当你想到这样写的人的时候还会以为有点怪异。下面是我列出的的我的感受最有用而又偏执的 10 项 Java 编程技巧。请看:程序员
1. 把字符串常量放在前面
经过把字符串常量放在比较函数equals()
比较项的左侧来防止偶然的 NullPointerException
历来都不是一个坏主意,就像这样:编程
1
2
3
4
5
|
// Bad
if
(variable.equals(
"literal"
)) { ... }
// Good
if
(
"literal"
.equals(variable)) { ... }
|
这是毫无疑问的,把一种表达式转换成另外一种更好的表达式,并不会失去什么。只要咱们的Options是真实存在的(Java 8中 Optional是对能够为空的对象进行的封装),不是吗?讨论一下…api
2. 不要相信早期的JDK APIs
Java刚出现的时候,编程必定是件很痛苦的事。那时的API仍然不够成熟,你可能曾经遇到过这样一段代码:数组
1
2
3
4
5
6
7
8
|
String[] files = file.list();
// Watch out
if
(files !=
null
) {
for
(
int
i =
0
; i < files.length; i++) {
...
}
}
|
看起来很奇怪对吗?也许吧,可是看看这个Javadoc:oracle
“若是抽象路径名表示的不是一个目录,那么这个方法返回null。不然返回一个字符串数组,其中每一个字符串表示当前目录下的一个文件或目录。”编程语言
是的,最好再加上判空检查,以确保正确:ide
1
2
3
4
5
6
7
8
9
10
|
if
(file.isDirectory()) {
String[] files = file.list();
// Watch out
if
(files !=
null
) {
for
(
int
i =
0
; i < files.length; i++) {
...
}
}
}
|
糟糕!前者违反了 Java 编码中 10 个微妙的最佳实践 的规则#5和#6。所以必定要记得判 null
检查!函数
3. 不要相信“-1”
我知道这很偏执,Javadoc中关于 String.indexOf()
的早期描述是这样的…
“字符在字符序列中第一次出现的位置将做为结果[被返回],若是字符不存在则返回-1。”
因此,-1
就能够理所固然被拿来用,对吗?我说不对,看看这个:
1
2
3
4
5
|
// Bad
if
(string.indexOf(character) != -
1
) { ... }
// Good
if
(string.indexOf(character) >=
0
) { ... }
|
谁知道呢。也许在某个特定场合下他们将会须要另外一种 编码值,若是不区分大小写的话,otherString
就会被包含进去…此时或许能够返回 -2呢
?谁知道呢。
毕竟,咱们有很是多关于NULL——价值亿万美金的错误的讨论。为何不开始讨论 -1呢,
某种意义上来讲 -1
是 null
在int类型下的另外一种形式。
4. 避免意外的赋值
是的。即便最优秀的程序员也可能犯这种错误(固然,不包括我。看#7)。
(假设这是JavaScript,咱们暂且偏执地认为是这种语言)
1
2
3
4
5
6
7
8
|
// Ooops
if
(variable =
5
) { ... }
// Better (because causes an error)
if
(
5
= variable) { ... }
// Intent (remember. Paranoid JavaScript: ===)
if
(
5
=== variable) { ... }
|
再说一遍。若是你的表达式中有常量,将它放在等式左边。这样当你打算再添加一个 = 时,不容易出错。
5. 检查null和长度
无论何时你有一个集合、数组或者其余的,确保它存在而且不为空。
1
2
3
4
5
|
// Bad
if
(array.length >
0
) { ... }
// Good
if
(array !=
null
&& array.length >
0
) { ... }
|
你不知道这些数组来自哪儿,也许是早期的JDK API呢?
6. 全部的方法都用 final 声明
你能够告诉我任何你想要的开闭原则,不过那都是胡说八道。我不相信你(能够正确继承个人类),也不相信我本身(不会意外地继承个人类)。所以除了接口(专门用于继承)都应该是严格的 final
。能够查看咱们的 Java 编码中 10 个微妙的最佳实践 中的#9。
1
2
3
4
5
|
// Bad
public
void
boom() { ... }
// Good. Don't touch.
public
final
void
dontTouch() { ... }
|
是的,写成final。若是这样作对你来讲没有意义,你也能够经过修改或重写字节码来改变类和方法,或者发送功能请求。我敢确定重写类/方法并非一个好主意。
7. 全部的变量和参数都用 final 声明
就像我说的。我不相信本身不会无心间重写了某个值。这么说来,个人确一点都不相信本身。由于:
这也是为何全部的变量和参数都用final声明的缘由
。
1
2
3
4
5
6
7
8
9
10
11
|
// Bad
void
input(String importantMessage) {
String answer =
"..."
;
answer = importantMessage =
"LOL accident"
;
}
// Good
final
void
input(
final
String importantMessage) {
final
String answer =
"..."
;
}
|
好吧,我认可,这一条我本身也不经常使用,虽然我应该用。我但愿Java能像Scala语言同样,人们在全部地方都直接用 val 来表示变量
,甚至都不考虑易变性,除非明确须要的时候他们才用 var 来声明变量,可是这样的机会特别少。
8. 重载的时候不要相信泛型
是的,这是会发生的。你以为你写了一个超好的API,它真的是既酷炫又直观;接着就出现了一群用户,他们只是把一切类型生搬硬套进 Object 中 直到那该死的编译器中止工做,而后他们忽然连接到了错误的方法,认为这一切都是你的错(事情老是这样)。
思考一下这个:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
// Bad
<T>
void
bad(T value) {
bad(Collections.singletonList(value));
}
<T>
void
bad(List<T> values) {
...
}
// Good
final
<T>
void
good(
final
T value) {
if
(value
instanceof
List)
good((List<?>) value);
else
good(Collections.singletonList(value));
}
final
<T>
void
good(
final
List<T> values) {
...
}
|
由于,你知道的…你的用户们,他们就像这样
1
2
3
4
|
// This library sucks
@SuppressWarnings
(
"all"
)
Object t = (Object) (List) Arrays.asList(
"abc"
);
bad(t);
|
相信我,我看过的多了,还有这样的
因此说偏执是有好处的。
9. 老是在switch语句里加上default
Switch…做为最滑稽的表达式之一,我不知道是该心存敬畏仍是默默哭泣。无论怎样,咱们既然没法摆脱 switch ,在必要的时候咱们最好可以正确使用它,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// Bad
switch
(value) {
case
1
: foo();
break
;
case
2
: bar();
break
;
}
// Good
switch
(value) {
case
1
: foo();
break
;
case
2
: bar();
break
;
default
:
throw
new
ThreadDeath(
"That'll teach them"
);
}
|
由于在当 value=3
被引入到软件中的时候,default 就能发挥做用,使其正常运行!别和我提 enum 类型
,由于这对 enums
也同样适用。
10. 用大括号隔开 switch 的每个 case 块
事实上,switch
是最坑爹的语句,任何喝醉了或是赌输了的人均可以在某种语言中使用它。看看下面这个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
// Bad, doesn't compile
switch
(value) {
case
1
:
int
j =
1
;
break
;
case
2
:
int
j =
2
;
break
;
}
// Good
switch
(value) {
case
1
: {
final
int
j =
1
;
break
;
}
case
2
: {
final
int
j =
2
;
break
;
}
// Remember:
default
:
throw
new
ThreadDeath(
"That'll teach them"
);
}
|
在switch
语句中,为全部的case
都只定义了一个做用域。事实上,这些case
不是真正意义上的语句,他们更像是标签,而switch
就是指向这些标签的goto语句。事实上,你甚至能够把case
语句和 惊人的FORTRAN77项声明 类比,对于FORTRAN,它的神秘已经超越了它的功能。
这意味着变量final int j
能够被任何case访问,不论咱们是否有break
。看起来并非很直观。咱们能够经过添加简单的花括号为每个case
建立一个新的嵌套的做用域,固然不要忘了在每一个 case 的语句块最后加 break。
结论
编程时的强迫症有时候看起来会很奇怪,会使得代码每每比必需的还要冗长。你可能会想,“啊,这种状况永远不会发生!”,可是正如我所说的,在经历了20年左右的编程生涯后,你不会想要再去修正那些只是由于编程语言的古老和固有缺陷而致使的愚蠢而没必要要的bug了。由于你知道…..
如今,轮到你了!
你在编程时有哪些强迫症呢?
原文连接: javacodegeeks 翻译: ImportNew.com - LynnShaw译文连接: http://www.importnew.com/16805.html
[ 转载请保留原文出处、译者和译文连接。]