经过一段时间的编码(咦,我已经经历了将近0年的编程生涯,快乐的日子总是过得很快),我们开始感谢那些好习惯。因为,你知道…
“任何可能出错的事情,最后都会出错。”
这就是人们为什么喜欢进行“防错性程序设计”的原因。偏执的习惯有时很有意义,有时则不够清晰也不够聪明,也许当你想到这样写的人的时候还会觉得有点怪异。下面是我列出的的个人感觉最有用而又偏执的10项Java编程技巧。请看:
1.把字符串常量放在前面通过把字符串常量放在比较函数equals()比较项的左侧来防止偶然的NullPointerException从来都不是一个坏主意,就像这样:
//Badif(variable.equals(literal)){...}//Goodif(literal.equals(variable)){...}
这是毫无疑问的,把一种表达式转换成另一种更好的表达式,并不会失去什么。只要我们的Options是真实存在的(Java8中Optional是对可以为空的对象进行的封装),不是吗?讨论一下…
.不要相信早期的JDKAPIsJava刚出现的时候,编程一定是件很痛苦的事。那时的API仍然不够成熟,你可能曾经遇到过这样一段代码:
String[]files=file.list();//Watchoutif(files!=null){for(inti=0;ifiles.length;i++){...}}
看起来很奇怪对吗?也许吧,但是看看这个Javadoc:
“如果抽象路径名表示的不是一个目录,那么这个方法返回null。否则返回一个字符串数组,其中每个字符串表示当前目录下的一个文件或目录。”
是的,最好再加上判空检查,以确保正确:
if(file.isDirectory()){String[]files=file.list();//Watchoutif(files!=null){for(inti=0;ifiles.length;i++){...}}}
糟糕!前者违反了Java编码中10个微妙的最佳实践的规则#5和#6。因此一定要记得判null检查!
3.不要相信“-1”我知道这很偏执,Javadoc中关于String.indexOf()的早期描述是这样的…
“字符在字符序列中第一次出现的位置将作为结果[被返回],如果字符不存在则返回-1。”
所以,-1就可以理所当然被拿来用,对吗?我说不对,看看这个:
//Badif(string.indexOf(character)!=-1){...}//Goodif(string.indexOf(character)=0){...}
谁知道呢。也许在某个特定场合下他们将会需要另一种编码值,如果不区分大小写的话,otherString就会被包含进去…此时或许可以返回-呢?谁知道呢。
毕竟,我们有非常多关于NULL——价值亿万美金的错误的讨论。为什么不开始讨论-1呢,某种意义上来说-1是null在int类型下的另一种形式。
4.避免意外的赋值是的。即使最优秀的程序员也可能犯这种错误(当然,不包括我。看#7)。
(假设这是JavaScript,我们暂且偏执地认为是这种语言)
//Ooopsif(variable=5){...}//Better(becausecausesanerror)if(5=variable){...}//Intent(remember.ParanoidJavaScript:===)if(5===variable){...}
再说一遍。如果你的表达式中有常量,将它放在等式左边。这样当你打算再添加一个=时,不容易出错。
5.检查null和长度不管什么时候你有一个集合、数组或者其他的,确保它存在并且不为空。
//Badif(array.length0){...}//Goodif(array!=nullarray.length0){...}
你不知道这些数组来自哪儿,也许是早期的JDKAPI呢?
6.所有的方法都用final声明你可以告诉我任何你想要的开闭原则,不过那都是胡说八道。我不相信你(可以正确继承我的类),也不相信我自己(不会意外地继承我的类)。因此除了接口(专门用于继承)都应该是严格的final。可以查看我们的Java编码中10个微妙的最佳实践中的#9。
//Badpublicvoidboom(){...}//Good.Donttouch.publicfinalvoiddontTouch(){...}
是的,写成final。如果这样做对你来说没有意义,你也可以通过修改或重写字节码来改变类和方法,或者发送功能请求。我敢肯定重写类/方法并不是一个好主意。
7.所有的变量和参数都用final声明就像我说的。我不相信自己不会无意间重写了某个值。这么说来,我的确一点都不相信自己。因为:
这也是为什么所有的变量和参数都用final声明的原因。
//Badvoidinput(StringimportantMessage){Stringanswer=...;answer=importantMessage=LOLaccident;}//Goodfinalvoidinput(finalStringimportantMessage){finalStringanswer=...;}
好吧,我承认,这一条我自己也不常用,虽然我应该用。我希望Java能像Scala语言一样,人们在所有地方都直接用val来表示变量,甚至都不考虑易变性,除非明确需要的时候他们才用var来声明变量,但是这样的机会特别少。
8.重载的时候不要相信泛型是的,这是会发生的。你觉得你写了一个超好的API,它真的是既酷炫又直观;接着就出现了一群用户,他们只是把一切类型生搬硬套进Object中直到那该死的编译器停止工作,然后他们突然链接到了错误的方法,认为这一切都是你的错(事情总是这样)。
思考一下这个:
//BadTvoidbad(Tvalue){bad(Collections.singletonList(value));}Tvoidbad(ListTvalues){...}//GoodfinalTvoidgood(finalTvalue){if(valueinstanceofList)good((List?)value);elsegood(Collections.singletonList(value));}finalTvoidgood(finalListTvalues){...}
因为,你知道的…你的用户们,他们就像这样
//Thislibrarysucks
SuppressWarnings(all)Objectt=(Object)(List)Arrays.asList(abc);bad(t);相信我,我看过的多了,还有这样的
所以说偏执是有好处的。
9.总是在switch语句里加上defaultSwitch…作为最滑稽的表达式之一,我不知道是该心存敬畏还是默默哭泣。不管怎样,我们既然无法摆脱switch,在必要的时候我们最好能够正确使用它,例如:
//Badswitch(value){case1:foo();break;case:bar();break;}//Goodswitch(value){case1:foo();break;case:bar();break;default:thrownewThreadDeath(Thatllteachthem);}
因为在当value=3被引入到软件中的时候,default就能发挥作用,使其正常运行!别和我提enum类型,因为这对enums也一样适用。
10.用大括号隔开switch的每一个case块事实上,switch是最坑爹的语句,任何喝醉了或是赌输了的人都可以在某种语言中使用它。看看下面这个例子:
//Bad,doesnt乌鲁木齐治疗白癜风最好的乌鲁木齐治疗白癜风最好的