[Java]剑走偏锋的10条编程技巧

标签: java 编程 技巧 | 发表时间:2015-10-19 18:09 | 作者:abv123456789
出处:http://blog.csdn.net


任何可能出错的事情,最后都会出错。” 




这就是人们为什么喜欢进行“防错性程序设计”的原因。偏执的习惯有时很有意义,有时则不够清晰也不够聪明,也许当你想到这样写的人的时候还会觉得有点怪异。下面是我列出的的个人感觉最有用而又偏执的 10 项 Java 编程技巧。请看: 

1.把字符串常量放在前面 

通过把字符串常量放在比较函数equals()比较项的左侧来防止偶然的 NullPointerException 从来都不是一个坏主意,就像这样: 
Java代码 
  1. // Bad  
  2. if (variable.equals("literal")) { ... }  
  3.    
  4. // Good  
  5. if ("literal".equals(variable)) { ... }  

这是毫无疑问的,把一种表达式转换成另一种更好的表达式,并不会失去什么。只要我们的Options是真实存在的(Java 8中 Optional是对可以为空的对象进行的封装),不是吗?讨论一下… 

2.不要相信早期的JDK APIs 

Java刚出现的时候,编程一定是件很痛苦的事。那时的API仍然不够成熟,你可能曾经遇到过这样一段代码: 
Java代码 
  1. String[] files = file.list();  
  2.    
  3. // Watch out  
  4. if (files != null) {  
  5.     for (int i = 0; i < files.length; i++) {  
  6.         ...  
  7.     }  
  8. }  

看起来很奇怪对吗?也许吧, 但是看看这个Javadoc: 

“如果抽象路径名表示的不是一个目录,那么这个方法返回null。否则返回一个字符串数组,其中每个字符串表示当前目录下的一个文件或目录。” 

是的,最好再加上判空检查,以确保正确: 
Java代码 
  1. if (file.isDirectory()) {  
  2.     String[] files = file.list();  
  3.    
  4.     // Watch out  
  5.     if (files != null) {  
  6.         for (int i = 0; i < files.length; i++) {  
  7.             ...  
  8.         }  
  9.     }  
  10. }  

糟糕!前者违反了 Java编码中10个微妙的最佳实践的规则#5和#6。因此一定要记得判 null检查! 

3. 不要相信“-1” 

我知道这很偏执,Javadoc中关于 String.indexOf() 的早期描述是这样的… 

“字符在字符序列中第一次出现的位置将作为结果[被返回],如果字符不存在则返回-1。” 

所以,-1 就可以理所当然被拿来用,对吗?我说不对,看看这个: 
Java代码 
  1. // Bad  
  2. if (string.indexOf(character) != -1) { ... }  
  3.    
  4. // Good  
  5. if (string.indexOf(character) >= 0) { ... }  

谁知道呢。也许在某个特定场合下他们将会需要另一种 编码值,如果不区分大小写的话,otherString 就会被包含进去…此时或许可以返回 -2呢?谁知道呢。 

毕竟,我们有非常多关于NULL——价值亿万美金的错误的讨论。为什么不开始讨论 -1呢,某种意义上来说 -1 是 null 在int类型下的另一种形式。 

4.避免意外的赋值 

是的。即使最优秀的程序员也可能犯这种错误(当然,不包括我。看#7)。 

(假设这是JavaScript,我们暂且偏执地认为是这种语言) 
Java代码 
  1. // Ooops  
  2. if (variable = 5) { ... }  
  3.    
  4. // Better (because causes an error)  
  5. if (5 = variable) { ... }  
  6.    
  7. // Intent (remember. Paranoid JavaScript: ===)  
  8. if (5 === variable) { ... }  

再说一遍。如果你的表达式中有常量,将它放在等式左边。这样当你打算再添加一个 = 时,不容易出错。 

5.检查null和长度 

不管什么时候你有一个集合、数组或者其他的,确保它存在并且不为空。 
Java代码 
  1. // Bad  
  2. if (array.length > 0) { ... }  
  3.    
  4. // Good  
  5. if (array != null && array.length > 0) { ... }  

你不知道这些数组来自哪儿,也许是早期的JDK API呢? 

6.所有的方法都用 final 声明 

你可以告诉我任何你想要的开闭原则,不过那都是胡说八道。我不相信你(可以正确继承我的类),也不相信我自己(不会意外地继承我的类)。因此除了接口(专门用于继承)都应该是严格的 final。可以查看我们的 Java编码中10个微妙的最佳实践中的#9。 
Java代码 
  1. // Bad  
  2. public void boom() { ... }  
  3.    
  4. // Good. Don't touch.  
  5. public final void dontTouch() { ... }  

是的,写成final。如果这样做对你来说没有意义,你也可以通过修改或重写字节码来改变类和方法,或者发送功能请求。我敢肯定重写类/方法并不是一个好主意。 

7.所有的变量和参数都用final声明 

就像我说的。我不相信自己不会无意间重写了某个值。这么说来,我的确一点都不相信自己。因为: 

 

这也是为什么所有的变量和参数都用final声明的原因。 
Java代码 
  1. // Bad  
  2. void input(String importantMessage) {  
  3.     String answer = "...";  
  4.    
  5.     answer = importantMessage = "LOL accident";  
  6. }  
  7.    
  8. // Good  
  9. final void input(final String importantMessage) {  
  10.     final String answer = "...";  
  11. }  

好吧,我承认,这一条我自己也不常用,虽然我应该用。 我希望Java能像Scala语言一样,人们在所有地方都直接用 val 来表示变量,甚至都不考虑易变性,除非明确需要的时候他们才用 var 来声明变量,但是这样的机会特别少。 

8.重载的时候不要相信泛型 

是的,这是会发生的。你觉得你写了一个超好的API,它真的是既酷炫又直观;接着就出现了一群用户,他们只是把一切类型生搬硬套进 Object 中 直到那该死的编译器停止工作,然后他们突然链接到了错误的方法,认为这一切都是你的错(事情总是这样)。 

思考一下这个: 
Java代码 
  1. // Bad  
  2. <T> void bad(T value) {  
  3.     bad(Collections.singletonList(value));  
  4. }  
  5.    
  6. <T> void bad(List<T> values) {  
  7.     ...  
  8. }  
  9.    
  10. // Good  
  11. final <T> void good(final T value) {  
  12.     if (value instanceof List)  
  13.         good((List<?>) value);  
  14.     else  
  15.         good(Collections.singletonList(value));  
  16. }  
  17.    
  18. final <T> void good(final List<T> values) {  
  19.     ...  
  20. }  

因为,你知道的…你的用户们,他们就像这样 
Java代码 
  1. // This library sucks  
  2. @SuppressWarnings("all")  
  3. Object t = (Object) (List) Arrays.asList("abc");  
  4. bad(t);  

相信我,我看过的多了,还有这样的 

 

所以说偏执是有好处的。 

9.总是在switch语句里加上default 

Switch…作为最滑稽的表达式之一,我不知道是该心存敬畏还是默默哭泣。不管怎样,我们既然无法摆脱 switch ,在必要的时候我们最好能够正确使用它,例如: 
Java代码 
  1. // Bad  
  2. switch (value) {  
  3.     case 1: foo(); break;  
  4.     case 2: bar(); break;  
  5. }  
  6.    
  7. // Good  
  8. switch (value) {  
  9.     case 1: foo(); break;  
  10.     case 2: bar(); break;  
  11.     default:  
  12.         throw new ThreadDeath("That'll teach them");  
  13. }  

因为在当 value=3 被引入到软件中的时候,default 就能发挥作用,使其正常运行!别和我提 enum 类型,因为这对 enums 也一样适用。 

10.用大括号隔开switch的每一个case块 

事实上,switch是最坑爹的语句,任何喝醉了或是赌输了的人都可以在某种语言中使用它。看看下面这个例子:
Java代码 
  1. // Bad, doesn't compile  
  2. switch (value) {  
  3.     case 1: int j = 1; break;  
  4.     case 2: int j = 2; break;  
  5. }  
  6.    
  7. // Good  
  8. switch (value) {  
  9.     case 1: {  
  10.         final int j = 1;  
  11.         break;  
  12.     }  
  13.     case 2: {  
  14.         final int j = 2;  
  15.         break;  
  16.     }  
  17.    
  18.     // Remember:  
  19.     default:   
  20.         throw new ThreadDeath("That'll teach them");  
  21. }  

在switch语句中,为所有的case都只定义了一个作用域。事实上,这些case不是真正意义上的语句,他们更像是标签,而switch就是指向这些标签的goto语句。事实上,你甚至可以把case语句和 惊人的FORTRAN77项声明类比,对于FORTRAN,它的神秘已经超越了它的功能。 

这意味着变量final int j 可以被任何case访问,不论我们是否有break。看起来并不是很直观。我们可以通过添加简单的花括号为每一个case创建一个新的嵌套的作用域,当然不要忘了在每个 case 的语句块最后加 break。 

结论 

编程时的强迫症有时候看起来会很奇怪,会使得代码往往比必需的还要冗长。你可能会想,“啊,这种情况永远不会发生!”,但是正如我所说的,在经历了20年左右的编程生涯后,你不会想要再去修正那些只是因为编程语言的古老和固有缺陷而导致的愚蠢而不必要的bug了。因为你知道….. 
https://youtu.be/oO3YmT2d-8k 

现在,轮到你了! 

你在编程时有哪些强迫症呢? 
作者:abv123456789 发表于2015/10/19 10:09:36 原文链接
阅读:2 评论:0 查看评论

相关 [java 编程 技巧] 推荐:

Java NIO编程的技巧和陷阱

- 小丑鱼 - 淘宝JAVA中间件团队博客
去年做的分享,一直上传slideshare失败,今天又试了下,成功了. 这个主题主要介绍Java NIO编程的技巧和陷阱,解读了一些NIO框架的源码,以及编写高性能NIO网络框架所需要注意的技巧和缺陷. 去年写了篇blog提供了pdf版本的下载,看这里.

[Java]剑走偏锋的10条编程技巧

- - CSDN博客推荐文章
“ 任何可能出错的事情,最后都会出错. 这就是人们为什么喜欢进行“防错性程序设计”的原因. 偏执的习惯有时很有意义,有时则不够清晰也不够聪明,也许当你想到这样写的人的时候还会觉得有点怪异. 下面是我列出的的个人感觉最有用而又偏执的 10 项 Java 编程技巧. 通过把字符串常量放在比较函数equals()比较项的左侧来防止偶然的 NullPointerException 从来都不是一个坏主意,就像这样: .

Java编程规范

- - Web前端 - ITeye博客
本文档的编写从简,绝大多数内容以条款或者表格形式列出,不做过多的补充说明,代码格式规范遵循eclipse的默认编码规范要求. •    简单,易执行. 1.    名字含义要明确,做到见名知义,如: User,Role, UserManager. 2.    尽量使用英文名字作为变量名,如果要使用中文,请写上备注.

Java并发编程基础

- - 并发编程网 - ifeve.com
并发是一种能并行运行多个程序或并行运行一个程序中多个部分的能力. 如果程序中一个耗时的任务能以异步或并行的方式运行,那么整个程序的吞吐量和可交互性将大大改善. 现代的PC都有多个CPU或一个CPU中有多个核. 是否能合理运用多核的能力将成为一个大规模应用程序的关键. 进程是以独立于其他进程的方式运行的,进程间是互相隔离的.

java编程风格指南

- - 行业应用 - ITeye博客
受不了的可以直接到以下网址查看. 作者:Hawstein 出处:http://hawstein.com/posts/google-java-style.html 声明:本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0 ,转载请注明作者及出处.

面向GC的Java编程

- - 并发编程网 - ifeve.com
Java程序员在编码过程中通常不需要考虑内存问题,JVM经过高度优化的GC机制大部分情况下都能够很好地处理堆(Heap)的清理问题. 以至于许多Java程序员认为,我只需要关心何时创建对象,而回收对象,就交给GC来做吧. 甚至有人说,如果在编程过程中频繁考虑内存问题,是一种退化,这些事情应该交给编译器,交给虚拟机来解决.

java 并发编程 synchronized

- - Java - 编程语言 - ITeye博客
同步原语--synchronized. synchronized(class)很特别,它会让另一个线程在任何需要获取class做为monitor的地方等待.class与this做为不同的监视器可以同时使用,不存在一个线程获取了class,另一个线程就不能获取该class的一切实例.. ->线程各自获取monitor,不会有等待..

Java调用外部程序技巧

- d0ngd0ng - 我自然
前些天使用Java调用外部程序的时候,发现线程会堵塞在waitfor()方法. 如果直接在Shell中调用这个程序,程序会很快结束,不会僵死. 为什么会堵塞呢,原因是当调用exec(cmd)后,JVM会启动一个子进程,该进程会与JVM进程建立3个管道连接,标准输入,标准输出和标准错误流. 假设该程序不断在向标准输出流和标准错误流写数据,而JVM不读取,数据会暂时缓冲在Linux的缓冲区,缓冲区满后该程序将无法继续写数据,会僵死,所以Java程序就会僵死在waitfor(),永远无法结束.

Java线程的5个使用技巧

- - Java译站
Java线程有哪些不太为人所知的技巧与用法. 学无止境,这也是我喜欢它的一个原因. 日常工作中你所用到的工具,通常都有些你从来没有了解过的东西,比方说某个方法或者是一些有趣的用法. 或者确切说是Thread这个类. 当我们在构建高可扩展性系统的时候,通常会面临各种各样的并发编程的问题,不过我们现在所要讲的可能会略有不同.

Java程序性能优化技巧

- - CSDN博客推荐文章
如果循环次数很多,循环体内代码处理不好问题就会被放大. for(int i=0;i