防御性编程以及我的一些感想
By bxbxbai
防御性编程小例,最开始我是关注了这个人的微信,才看到这篇文章的,最近也工作了一段时间了~对自己以及别人写的代码有了一些新的想法(因为我遇到过很多坑啊囧)。因此,本文来谈谈这个话题。
在公司,我们碰到的很大一部分问题都是NullPointerException
。我常常就想:这段程序明明在我手机上运行好好的,为什么会出现这种情况呢?
因为,我们永远都无法预测用户使用 App 时会发生的各种情况。所以防御性编程可以让我们减少很大一部分错误。
先说一个故事
先来说一个我去年面试过的问题,面试官问我:请你用最熟悉的语言写一个 atoi 程序。
我心里一想:这么简单!!我要好好写!不要写出 bug!我马上就写好了,并且用“12334”这种简单的字符串试了又试,没问题就交给他看。
public int atoi (String a){ int len = a.length (); int num = 0; for(int i = len - 1; i >= 0; i--) { num += (a.charAt (i) - '0') * Math.pow (10, len - i - 1); } return num; }
面试官一看就问我,你找找有什么问题没,我看了好几遍(我都在找 bug),说没问题啊!我又仔细一想:如果传进来一个负数怎么办呢?比如“-12333”,这段程序就错了!
我就和面试官说了~他说嗯,还有问题吗?我心里想还有啊?想了几遍都想不出!我说:效率问题?他不给面子直接就说:你先别管效率!
他说如果我传进来一个null
会怎么样?我恍然大悟!!!我有太多东西没考虑到(我图样图森破啊囧!!)!
上面就是我的一个真实的故事~不知你看了有什么感想,反正我觉得这次面试可以让我反思很多我存在的问题。
自从看了前面提到的那篇文章后,我现在写代码就时刻装着“防御性编程”这 5 个字。
那么怎么写防御性代码呢?
请看Integer.parseInt (String)
这个方法,好好看!我现在分析一下~JDK 的大神们怎么写健壮的代码!(如果有错误请指正~ =.=)
public static int parseInt (String string, int radix) throws NumberFormatException { if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { throw new NumberFormatException ("Invalid radix: " + radix); } if (string == null) { throw invalidInt (string); } int length = string.length (), i = 0; if (length == 0) { throw invalidInt (string); } boolean negative = string.charAt (i) == '-'; if (negative && ++i == length) { throw invalidInt (string); } return parse (string, i, radix, negative); }
上面这段代码出自 java 1.7.0_51 的java.lang.Integer
类,JDK 开发大神是如何写代码的呢?
可以看到这段代码最开始的一部分就是在验证每个参数的正确性(代码中 radix 表示进制数),这里最小的进制就是2,最大进制是 36。如果进制数不满足要求,直接抛出异常。
然后判断传入字符串是否为null
,如果字符串不为null
,然后可以取字符串的长度。 后面再判断字符串是否一个负数, 当所有参数都验证过了以后再做正事——将字符串转换成一个数字。
我现在得到的一个重要的经验就是:
当你写一个方法需要对传入的参数进行处理或者计算的时候,你必须要严格验证传入参数的正确性,如果不符合,就应当给出提示!
上面提到的那篇文章里说到:
这就是防御性编程的最基本规则:保护程序免遭非法输入数据的破坏。
这些都是我以前编程不会考虑的事情啊!
如果你的代码没有防御性措施,那么你一定会遇到各种坑的~只是时候未到~
但也不是说所有的程序都应该这么写。如果你在写一个 private 方法只供类里面使用,那么我觉得就不必写这种防御性代码了。当然没有绝对的事情,如果一个 public 方法接受外部传入的参数,这个参数又传入这个 private 方法,那么你在使用这个 private 方法时候就需要先验证参数的合法性,然后再调用这个 private 方法。
当你在写一个 public 方法可以接受来自任何地方的参数时,就必须要验证参数的合法性了!
那么为什么要防御性编程
我觉得最终的目的就是为了让你写的代码正确运行,当面对各种各样的参数时,同时要向外部提供参数错误的原因,可以快速找到 bug。
在 Android 开发里面,主线程(UI 线程)中的一个微小的问题都会导致程序的崩溃,可能是一不小心一个 View 对象传入某个方法的时候是一个null
,也可能一个方法的返回值是null
等等,各种坑会在隐藏的地方等着你来踩哦~
要知道当一个对象为null
的时候(你肯定不知道它为null
),然后调用它的方法时,就会发生程序崩溃,这是应该是程序崩溃最常见的原因之一了~
比如在我最开始写的那个 atoi 程序里,如果别人用我的程序不小心传入一个null
,那么我的程序就崩溃了~