加入收藏 | 设为首页 | 会员中心 | 我要投稿 伊春站长网 (https://www.0458zz.com/)- 管理运维、图像技术、数据标注、智能营销、数据计算!
当前位置: 首页 > 站长资讯 > 外闻 > 正文

安全风险分析和安全解决方案建议

发布时间:2021-02-19 14:44:13 所属栏目:外闻 来源:互联网
导读:这个例子看起来根本不知道在干什,这段代码其实是在用数组越界异常来控制遍历数组,这个脑洞开的非常拙劣。如何正确遍历一个数组我想不需要再给出例子,那是对读者的亵渎。 那为什么有人这么开脑洞呢?因为这个做法企图使用 Java 错误判断机制来提高性能,因

这个例子看起来根本不知道在干什,这段代码其实是在用数组越界异常来控制遍历数组,这个脑洞开的非常拙劣。如何正确遍历一个数组我想不需要再给出例子,那是对读者的亵渎。

那为什么有人这么开脑洞呢?因为这个做法企图使用 Java 错误判断机制来提高性能,因为 JVM 对每一次数组访问都会检查越界情况,所以他们认为检查到的错误才应该是循环终止的条件,然而 for-each 循环对已经检查到的错误视而不见,将其隐藏了,所以用应该避免使用 for-each。

对于这个脑洞的原因 Joshua Bloch 给出了三点反驳:

  • 因为异常机制的设计初衷是用于不正常的情形,所以很少会有 JVM 实现试图对它们进行优化,使得与显示测试一样快速。
  • 把代码放在 try-catch 块中反而阻止了现代 JVM 实现本来可能要执行的某些特定优化。
  • 对数组进行遍历的标准模式并不会导致冗余的检查。有些现代的 JVM 实现会将他们优化掉。

还有一个例子是我曾经遇到的,但是由于年代久远已经找不到项目地址了。我一个朋友曾经给我看过一个 github 上的 MVC 框架项目,虽然时隔多年但令我印象深刻的是这个项目使用自定义异常和异常码来控制 Dispatcher,把异常当成一种方便的结果传递方式来使用,当成 goto 来使用,这太可怕了。不过 try-catch 方式从字节码表述上来看,确实是一种 goto 的表述。这样的方式我们最好想都不要想。

这两个例子主要就是为了说明,异常应该只用于异常的情况下;永远不应该用在正常的流程中,不管你的理由看着多么的聪明。这样做往往会弄巧成拙,使得代码可读性大大下降。

受检异常和非受检异常

曾经不止一次的见过有人提倡将系统中的受检异常都包装成非受检异常,对于这个建议我并不以为然。因为 Java 的设计者其实是希望通过区分异常种类来指导我们编程的。

Java 一共提供了三类可抛出结构 (throwable),受检异常、非受检异常(运行时异常)和错误 (error)。他们的界限我也经常傻傻的分不清,不过还是有迹可循的。

  • 受检异常:如果期望调用者能够适当的恢复,比如 RMI 每次调用必须处理的异常,设计者是期望调用者可以重试或别的方式来尝试恢复;比如上边提到的 FileInputStream 的构造方法,会抛出 FileNotFoundException,设计者或许希望调用者尝试从其他目录来读取该文件,使得程序可以继续执行下去。
  • 非受检异常和错误:表明是编程错误,往往属于不可恢复的情景,而且是不应该被提前捕获的,应该快速抛出到顶层处理器,比如在服务接口的基类方法中统一处理非受检异常。这种非受检异常往往也说明了在编程中违反了某些约定。比如数组越界异常,说明违反了访问数组不能越界的前提约定。

总而言之,对于可恢复的情况使用受检异常;对于程序错误使用非受检异常。因此你自己程序内部定义的异常都应该是非受检异常;在面向接口或面向二方/三方库的方法尽量使用受检异常。

说到面向接口或面向二/三方库,你可能碰到的就是一辆失控的汽车。搞清楚你所调用的接口或者库里的异常情况也是我们能够码出健壮代码的一个强力保证。

不要忽略异常

这个建议显而易见,但却常常被违反。当一个 API 的设计者声明一个方法将抛出异常的时候,通常都是想要说明某件事发生了。忽略异常就是我们通常说的吃掉异常,try-catch 但什么也不做。吃掉一个异常就好比破坏了一个报警器,当灾难真正来临没人搞清楚发生了什么。

对于每一个 catch 块至少打印一条日志,说明异常情况或者说明为什么不处理。

这个显而易见的建议同时适用于受检异常和非受检异常。

DRY (Don't Repeat Yourself)

DRY 原则最先在《The pragmatic Programmer》被提出,如今已经被业界广泛的认知,我相信每个软件工程师都认识它。我想有很多人对它的认识含混不清仅仅是不要有重复的代码;也有些人对此原则不屑一顾抽象什么的都是浪费时间快速上线是大义;也有人誓死捍卫这个原则不能忍受任何重复。今天我们来谈谈这个熟悉又陌生的话题。

DRY 是什么

DRY 的原则是“系统中的每一部分,都必须有一个单一的、明确的、权威的代表”,指的是(由人编写而非机器生成的)代码和测试所构成的系统,必须能够表达所应表达的内容,但是不能含有任何重复代码。当 DRY 原则被成功应用时,一个系统中任何单个元素的修改都不需要与其逻辑无关的其他元素发生改变。此外,与之逻辑上相关的其他元素的变化均为可预见的、均匀的,并如此保持同步。

这段定义来自于中文维基百科,但这个定义似乎与 Andrew Hunt 和 David Thomas 给出的定义有所出入。寻根溯源在《The pragmatic Programmer》作者是这样定义这个原则的:

EVERY PIECE OF KNOWLEDGE MUST HAVE A SINGLE, UNAMBIGUOUS, AUTHORITATIVE REPRESENTATION WITHIN A SYSTEM.

系统中的每一项知识都必须具有单一、无歧义、权威的表示。

作者所提倡禁止的是知识 (knowledge) 的重复而不是单纯的代码上的重复。那什么是知识呢?我斗胆给一个自己的理解,知识就是系统中对于一个逻辑的解释/定义,系统中的逻辑都是要对外输出或者让外界感知的。逻辑的定义/解释包括代码和写在代码上的文档还有宏观上实现。我们要避免的是在改动时的一个逻辑的时候需要去修改十处,如果漏掉了任何一处就会造成 bug 甚至线上故障。变更在软件开发中又是一个常态,在互联网行业中更是如此,而在一个到处是重复的系统中维护变更是非常艰难的。



(编辑:伊春站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读