建议19: 断言绝对不是鸡肋

在防御式编程中经常会用断言(Assertion)对参数和环境做出判断,避免程序因不当的输入或错误的环境而产生逻辑异常,断言在很多语言中都存在,C、C++、Python都有不同的断言表示形式。在Java中的断言使用的是assert关键字,其基本的用法如下:

assert <布尔表达式>
assert <布尔表达式> : <错误信息>

在布尔表达式为假时,抛出AssertionError错误,并附带了错误信息。assert的语法较简单,有以下两个特性:

(1)assert默认是不启用的

我们知道断言是为调试程序服务的,目的是为了能够快速、方便地检查到程序异常,但Java在默认条件下是不启用的,要启用就需要在编译、运行时加上相关的关键字,这就不多说,有需要的话可以参考一下Java规范。

(2)assert抛出的异常AssertionError是继承自Error的

断言失败后,JVM会抛出一个AssertionError错误,它继承自Error,注意,这是一个错误,是不可恢复的,也就表示这是一个严重问题,开发者必须予以关注并解决之。

assert虽然是做断言的,但不能将其等价于if…else…这样的条件判断,它在以下两种情况不可使用:

(1)在对外公开的方法中

我们知道防御式编程最核心的一点就是:所有的外部因素(输入参数、环境变量、上下文)都是“邪恶”的,都存在着企图摧毁程序的罪恶本源,为了抵制它,我们要在程序中处处检验,满地设卡,不满足条件就不再执行后续程序,以保护主程序的正确性,处处设卡没问题,但就是不能用断言做输入校验,特别是公开方法。我们来看一个例子:

 public class Client {
public static void main(String[] args) {
StringUtils.encode(null);
}
}
//字符串处理工具类
class StringUtils{
public static String encode(String str){
assert str!=null:"加密的字符串为null";
/*加密处理*/
}
}

encode方法对输入参数做了不为空的假设,如果为空,则抛出AssertionError错误,但这段程序存在一个严重的问题,encode是一个public方法,这标志着是它对外公开的,任何一个类只要能够传递一个String类型的参数(遵守契约)就可以调用,但是Client类按照规范和契约调用enocde方法,却获得了一个AssertionError错误信息,是谁破坏了契约协定?—是encode方法自己。

(2)在执行逻辑代码的情况下

assert的支持是可选的,在开发时可以让它运行,但在生产系统中则不需要其运行了(以便提高性能),因此在assert的布尔表达式中不能执行逻辑代码,否则会因为环境不同而产生不同的逻辑,例如:

 public void doSomething(List list,Object element){
assert list.remove(element):"删除元素 " + element + " 失败";
/*业务处理*/
}

这段代码在assert启用的环境下,没有任何问题,但是一旦投入到生产环境,就不会启用断言了,而这个方法也就彻底完蛋了,list的删除动作永远都不会执行,所以也就永远不会报错或异常,因为根本就没有执行嘛!

以上两种情况下不能使用assert,那在什么情况下能够使用assert呢?一句话:按照正常执行逻辑不可能到达的代码区域可以放置assert。具体分为三种情况:

(1)在私有方法中放置assert作为输入参数的校验

在私有方法中可以放置assert校验输入参数,因为私有方法的使用者是作者自己,私有方法的调用者和被调用者之间是一种弱契约关系,或者说没有契约关系,其间的约束是依靠作者自己控制的,因此加上assert可以更好地预防自己犯错,或者无意的程序犯错。

(2)流程控制中不可能达到的区域

这类似于JUnit的fail方法,其标志性的意义就是:程序执行到这里就是错误的,例如:

 public void doSomething(){
int i = 7;
while(i >7){
/*业务处理*/
}
assert false:"到达这里就表示错误";
}

(3)建立程序探针

我们可能会在一段程序中定义两个变量,分别代表两个不同的业务含义,但是两者有固定的关系,例如var1=var2*2,那我们就可以在程序中到处设“桩”,断言这两者的关系,如果不满足即表明程序已经出现了异常,业务也就没有必要运行下去了。

//===========================================================================

Eclipse中默认是关闭断言assert的.怎么样在eclipse中开启断言...

最简单的方式:

博客园对上传的图片有压缩,显示不清楚,点击看高清大图:http://images2015.cnblogs.com/blog/610238/201604/610238-20160421154632101-286208268.png

[改善Java代码]断言绝对不是鸡肋的更多相关文章

  1. [改善Java代码]易变业务使用脚本语言编写

    建议16: 易变业务使用脚本语言编写 Java世界一直在遭受着异种语言的入侵,比如PHP.Ruby.Groovy.JavaScript等,这些“入侵者”都有一个共同特征:全是同一类语言—脚本语言,它们 ...

  2. [改善Java代码]对字符串排序 持一种宽容的心态

    在Java中一涉及到中文处理就会冒出很多的问题来,其中的排序也是一个让人头疼的问题,看代码: import java.util.Arrays; public class Client { public ...

  3. [改善Java代码]推荐在复杂字符串操作中使用正则表达式

    一.分析  字符串的操作,诸如追加.合并.替换.倒序.分隔等,都是在编码过程中经常用到的,而且Java也提供了append.replace.reverse.split等方法来完成这些操作,它们使用起来 ...

  4. [改善Java代码]非稳定排序推荐使用List

    我们知道Set与List的最大区别就是Set中的元素不可以重复(这个重复指的equals方法的返回值相等),其他方面则没有太大的区别了,在Set的实现类中有一个比较常用的类需要了解一下:TreeSet ...

  5. [改善Java代码]多线程使用Vector或HashTable

    Vector是ArrayList的多线程版本,HashTable是HashMap的多线程版本,这些概念我 们都很清楚,也被前辈嘱咐过很多次,但我们经常会逃避使用Vector和HashTable,因为用 ...

  6. [改善Java代码]减少HashMap中元素的数量

    在系统开发中我们经常会使用HashMap作为数据集容器,或者是用缓冲池来处理,一般很稳定,但偶尔也会出现内存溢出的问题(OutOfMemory错误),而且这经常是与HashMap有关的.而且这经常是与 ...

  7. [改善Java代码]集合运算时使用更优雅的方式

    在初中代数中,我们经常会求两个集合的并集.交集.差集等,在Java中也存在着此 类运算,那如何实现呢? 一提到此类集合操作,大部分的实现者都会说:对两个集合进行遍历,即可求出结果.是的,遍历可以实现并 ...

  8. [改善Java代码]不推荐使用binarySearch对列表进行检索

    对一个列表进行检索时,我们使用的最多的是indexOf方法,它简单好用,而且也不会出错,虽然它只能检索到第一个符合条件的值,但是我们可以生成子列表后再检索.这样也就可以查找到所有符合条件的值了. Co ...

  9. [改善Java代码]子列表只是原列表的一个视图

    List接口提供了subList方法,其作用是返回一个列表的子列表.这与String类的subString有点类似.但是他们的功能是否相同?看代码: import java.util.ArrayLis ...

随机推荐

  1. google proto buffer安装和简单示例

    1.安装 下载google proto buff. 解压下载的包,并且阅读README.txt,根据里面的指引进行安装. $ ./configure $ make $ make check $ mak ...

  2. POJ 1258 Agri-Net (最小生成树)

    Agri-Net 题目链接: http://acm.hust.edu.cn/vjudge/contest/124434#problem/H Description Farmer John has be ...

  3. POJ 2421 Constructing Roads (最小生成树)

    Constructing Roads 题目链接: http://acm.hust.edu.cn/vjudge/contest/124434#problem/D Description There ar ...

  4. OLEVARIANT的替代——FIREDAC的TFDJSONDataSets和TFDJSONDeltas

    OLEVARIANT——这个COM的序列格式,也是DATASNAP已使用了20年的序列格式,在20年以后的今天,终于有了它的替代者:FIREDAC的TFDJSONDataSets和TFDJSONDel ...

  5. javascript中数组的迭代等操作

  6. freemarker截取字符串subString

    转至:http://fengzhijie1103.iteye.com/blog/1142918 freemarker截取字符串其实和JAVA语法是差不多了,也有substring 方法       如 ...

  7. python list(列表)和tuple(元组)

    200 ? "200px" : this.width)!important;} --> 介绍 python中存在两种有序的类型列表,分别是list(列表)和tuple(元组) ...

  8. Objective-C的singleton模式

    最近因为在ios应用开发中,考虑到一些公共方法的封装使用,就决定使用单例模式的写法了..不知道,Object-c中的单例模式的写法是否和java中的写法是否有所区别?于是阿堂从网上一搜,发现“ Obj ...

  9. UVa657 The die is cast

    // 题意:给一个图案,其中'.'表示背景,非'.'字符组成的连通块为筛子.每个筛子里又包含两种字符,其中'X'组成的连通块表示筛子上的点 // 统计每个筛子里有多少个"X"连通块 ...

  10. 方法javaJVM学习笔记-内存处理

    本文是一篇关于方法java的帖子 大多数JVM将内存区域分离为Method Area(Non-Heap),Heap,Program Counter Register,Java Method Statc ...