防御性编程以及我的一些感想

jopen 10年前

  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,那么我的程序就崩溃了~