Java开发笔记(七十六)如何预防异常的产生 川长思鸟来 2022-03-01 17:10 175阅读 0赞 每个程序员都希望自己的程序稳定运行,不要隔三岔五出什么差错,可是程序运行时冒出来的各种异常着实烦人,令人不胜其扰。虽然可以在代码中补上try/catch语句捕捉异常,但毕竟属于事后的补救措施。与其后知后觉地亡羊补牢,不如一开始就未雨绸缪,只要防患于未然,必能收到事半功倍的成效。 就编码时的常见异常而言,绝大多数异常都能通过适当的校验加以规避,也就是事先指定可让程序正常运行的合法条件,只有条件满足才开展业务逻辑处理,否则进行失败情况的处理。这样用于异常捕捉的try/catch语句便转换为了条件分支的if/else语句,对于熟能生巧的if/else流程控制,想必程序员在编码时更能游刃有余。接下来以几个常见的异常为例,阐述一下如何预防这些异常的发生。 首先看最简单的算术异常,如果是除数为零的异常,检查一下除数的值是否为零就行了。如果是大小数除法运算遇到的“商为无限循环小数”异常,就得在调用divide方法之时指定本次除法运算的小数精度,以及精度范围最后一位数字的舍入方式。下面是优化后的大小数除法代码例子: // 测试算术异常:商是无限循环小数 private static void testDivideByDecimal() { BigDecimal one = BigDecimal.valueOf(1); BigDecimal three = BigDecimal.valueOf(3); // 大小数的除法运算,小数点后面保留64位,其中最后一位做四舍五入 BigDecimal result = one.divide(three, 64, BigDecimal.ROUND_HALF_UP); System.out.println("sqrt result=" + result); } 其次看数组越界异常,不管是根据下标访问数组元素,还是根据索引访问清单元素,都要保证待访问元素的下标必须落在数组内部(或索引落在清单内部)。因此,合法的下标数值应当大于零,且小于数组的长度,于是访问数组元素的代码可改写如下: // 测试越界异常:下标超出数组范围 private static void testArrayByIndex() { int[] array = { 1, 2, 3 }; // 在根据下标获取数组元素之前,先判断该下标是否落在数组范围之内 if (array.length > 3) { int item = array[3]; System.out.println("array item=" + item); } else { System.out.println("array's length isn't more than 3"); } } 再次看空指针异常,无论是访问某对象的实例属性,还是调用某对象的实例方法,都要求该对象是真切存在着的,否则面对一个空指针瞎指挥,只能落得竹篮打水一场空的境遇了。在Java编程中,可比较某对象是否等于null来判断它是否为空指针,这样添加了空指针校验的对象访问代码示例如下: // 测试空指针异常:对象不存在 private static void testStringByNull() { String str = null; // 在跟某位对象约会之前,先打个电话问问有没空,否则牵肠挂肚空欢喜一场 if (str != null) { int length = str.length(); System.out.println("str length=" + length); } else { System.out.println("str is null"); } } 另外还有类型转换异常,因为一个父类会衍生出许多子类,所以若将父类实例想当然强行转换为某个子类,则很可能遭到类型不匹配的失败。此时为了确保万无一失,需要在类型转换之前增加条件判断,即利用instanceof检查该实例是否属于指定的子类类型,只有类型完全一致方可进行类型转换的操作。先做instanceof校验后做类型转换的代码例子如下所示: // 测试类型转换异常:原始数据与目标类型不匹配 private static void testConvertLyList() { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); // 在做强制类型转换之前,先把它的底细摸清楚。正所谓“知己知彼,百战不殆” if (list instanceof ArrayList<?>) { ArrayList<Integer> arrays = (ArrayList<Integer>) list; System.out.println("arrays size=" + arrays.size()); } else { System.out.println("arrays is not belong to ArrayList"); } } 其它异常的预防措施大体类似,基本思路是检验某项操作的前提条件是否满足,所谓万变不离其宗,掌握了异常预防的要领即可举一反三。 因代码逻辑缺陷而导致的程序异常,可以通过改进相关业务逻辑加以预防,那么因系统资源不足而导致的程序错误,能否也采取类似的预防措施呢?前面在分析内存溢出和栈溢出错误的时候,两个示例代码都包含了方法递归的过程,这是否意味着,只要不进行递归调用,或者只进行有限次的递归调用,就能避免撑爆系统的错误吗?从表面上看,前述的内存溢出和栈溢出错误,都由无限次的递归调用而引发。然而这不能全赖递归,无限次递归只是产生错误的其中一种情况,事实上还有别的情况也会造成溢出错误,根源在于一个程序分配到的堆内存和栈内存大小是个有限的数值,倘若某项业务操作想要占据一块非常大的空间,或者某次方法调用需要传递一个非常大的参数,那么不必多次递归调用,只需仅仅一次调用就会让程序瘫痪了。 使用eclipse开发Java的话,默认的堆内存和栈内存大小在eclipse.ini里面设置,ini文件中的Xmx参数表示JVM最大的堆内存大小,该参数通常配置为512M;另一个Xss参数表示每个线程的栈内存大小,该参数默认为1M。所以,一旦程序意图占用超过512M的内存空间,就会报堆内存溢出的错误;一旦某次方法调用需要传送超过1M大小的参数信息,就会报栈溢出的错误。 举个实际应用的场景,比如在电脑上看电影,现在一个高清电影的视频大小普遍有好几个G,要是将几G大小的文件全加载进内存才播放,转眼间电脑内存便所剩无几。显然这种做法不可取,合理的方案是边加载边播放,每次只要提前加载当前播放位置后面若干秒的视频,同时释放掉已经播放完毕的那部分视频资源,那么真正用到的内存大小只有当前位置以及之后的一段视频缓冲,如此便能在极大程度上节约了内存资源消耗。 再来一个跟方法调用有关的场景,有时调用某方法需要传递图像数据,而位图格式的图像体积是相当大的。以一幅分辨率为800*600的图片为例,它共有800*600=48万个像素,每个像素又需要8位的灰度、8位的红色、8位的绿色、8位的蓝色加起来是4个字节空间,于是该图片的位图数据大小=48万\*4字节=192万字节≈1.83M,那么光光这幅图片的数据便足以耗光程序的栈内存了。想想你去买房子,一套房子的总价要好几百万,难道要拎着数百捆的百元大钞去付房款吗?就算麻袋装得下这几百捆钞票,恐怕也没人拎得动无比沉重的麻袋。正常的做法是把钱存在银行里面,然后带上银行卡在售楼部直接刷卡,银行系统就知晓有多少资金发生了交易。同理,在方法调用时传递图片,无需直接传送完整的图像数据,而是先把图像保存为某个图片文件,再向该方法传递图片文件的存储路径,这样下级方法去指定路径读取图片便是。 更多Java技术文章参见《[Java开发笔记(序)章节目录][Java]》 [Java]: https://blog.csdn.net/pinlantu/article/details/83957672
相关 Java的空指针异常,如何有效预防? Java中的空指针异常(NullPointerException)通常发生在尝试访问null对象的属性、方法或者调用其操作时。以下是一些有效预防空指针异常的方法: 1. ** 逃离我推掉我的手/ 2024年09月10日 20:12/ 0 赞/ 35 阅读
相关 Java开发笔记(十六)非此即彼的条件分支 前面花了大量篇幅介绍布尔类型及相应的关系运算和逻辑运算,那可不仅仅是为了求真值或假值,更是为了通过布尔值控制流程的走向。在现实生活中,常常需要在岔路口抉择走去何方,往南还是往北 阳光穿透心脏的1/2处/ 2022年05月03日 07:26/ 0 赞/ 154 阅读
相关 Java开发笔记(五十七)因抽象方法而产生的抽象类 前面介绍了类的常见用法,令人感叹面向对象的强大,几乎日常生活中的所有事物,都可以抽象成Java的基类及其子类。然而抽象操作也有副作用,就是某个抽象而来的行为可能是不确定的,比如 「爱情、让人受尽委屈。」/ 2022年03月21日 15:24/ 0 赞/ 125 阅读
相关 Java开发笔记(六十二)如何定义函数式接口 前面介绍了Lambda表达式的用法,从实践中发现它确实极大地方便了开发者,然而不管是匿名内部类还是Lambda表达式,所举的例子都离不开各类数组的排序方法,倘使Lambda表达 末蓝、/ 2022年03月18日 01:18/ 0 赞/ 175 阅读
相关 Java开发笔记(六十七)清单:ArrayList和LinkedList 前面介绍了集合与映射两类容器,它们的共同特点是每个元素都是唯一的,并且采用二叉树方式的类型还自带有序性。然而这两个特点也存在弊端:其一,为啥内部元素必须是唯一的呢?像手机店卖出 亦凉/ 2022年03月15日 10:48/ 0 赞/ 145 阅读
相关 Java开发笔记(七十三)常见的程序异常 一个程序开发出来之后,无论是用户还是程序员,都希望它稳定地运行,然而程序毕竟是人写的,人无完人哪能不犯点错误呢?就算事先考虑得天衣无缝,揣着一笔巨款跑去岛国买了栋抗震性能良好的 港控/mmm°/ 2022年03月07日 01:14/ 0 赞/ 189 阅读
相关 Java开发笔记(七十六)如何预防异常的产生 每个程序员都希望自己的程序稳定运行,不要隔三岔五出什么差错,可是程序运行时冒出来的各种异常着实烦人,令人不胜其扰。虽然可以在代码中补上try/catch语句捕捉异常,但毕竟属于 川长思鸟来/ 2022年03月01日 17:10/ 0 赞/ 176 阅读
相关 Java开发笔记(七十五)异常的处理:扔出与捕捉 前面介绍的几种异常(不包含错误),编码的时候没认真看还发现不了,直到程序运行到特定的代码跑不下去了,程序员才会恍然大悟:原来这里的代码逻辑有问题。像这些在运行的时候才暴露出来的 向右看齐/ 2022年03月01日 06:36/ 0 赞/ 178 阅读
相关 Java开发笔记(七十七)使用Optional规避空指针异常 前面在介绍清单用法的时候,讲到了既能使用for循环遍历清单,也能通过stream流式加工清单。譬如从一个苹果清单中挑选出红苹果清单,采取for循环和流式处理都可以实现。下面是通 Dear 丶/ 2022年02月28日 06:58/ 0 赞/ 238 阅读
还没有评论,来说两句吧...