检查项 | 清单项目 | 分类 |
信息安全 或权限访问 |
如果不用于继承,使类为final | 可访问性的扩展(Accessibility Extensibility) |
最小化包,类,接口,方法和域的可访问性 | 可访问性的扩展(Accessibility Extensibility) | |
为native方法定义包装类(而不是定义native方法为public) | 可访问性的扩展(Accessibility Extensibility) | |
注释出安全相关的信息 | 文档化 | |
系统的输入必须检查是否有效和在允许范围内 | 拒绝服务(Denial of Service) | |
检验输入是否含有非法或恶意字符, 防止注入性攻击 | 拒绝服务(Denial of Service) | |
避免对于一些不寻常行为的过分日志 | 拒绝服务(Denial of Service) | |
把从不可信对象得到的输出作为输入来检验 | 输入检验(Input Validation) | |
对命令行执行的代码,需要详细检查命令行参数 | 输入检验(Input Validation) | |
WEB类程序检查是否对访问参数进行合法性验证 | 输入检验(Input Validation) | |
使public static域为final(避免调用方(caller)修改它的值) | 访问限制 | |
避免服务器暴露应用系统的目录结构的配置 | 访问限制 | |
定义合理的角色权限分级, 并授予合适的人员 | 访问限制 | |
从异常中清除敏感信息(暴露文件路径,系统内部相关,配置) | 私密信息(Confidential Information) | |
不要把高度敏感的信息写到日志 | 私密信息(Confidential Information) | |
考虑把高度敏感的信息在使用后从内存中清除 | 私密信息(Confidential Information) | |
重要信息如密码的保存是否选用难以破解的不可逆加密算法 | 私密信息(Confidential Information) | |
通讯时考虑是否使用了安全的通讯方式 | 私密信息(Confidential Information) | |
避免暴露敏感类的构造函数 | 对象构造 | |
避免安全敏感类的序列化 | 序列化反序列化(Serialization Deserialization) | |
通过序列化来保护敏感数据 | 序列化反序列化(Serialization Deserialization) | |
小心地缓存潜在的特权操作结果 | 序列化反序列化(Serialization Deserialization) | |
只有在需要的时候才使用JNI | 访问限制 | |
并发安全 | 代码中所有的全局可见可变变量是否是线程安全的 | 并发 |
需要被多个线程访问的对象是否线程安全,有无通过同步方法保护 | 并发 | |
同步对象上的锁是否按相同的顺序获得和释放以避免死锁,注意错误处理代码 | 并发 | |
是否存在可能的死锁或是竞争 | 并发 | |
对共享可变的数据使用同步访问 | 并发 | |
使用executors而不是task和thread ; 使用并发库、框架而不是线程对象 | 并发 | |
性能 | 同步方法是否过度使用, 同步区域是否过大 | 并发 |
拼接大量字符串时是否使用 StringBuilder 而不是 String | 综合编程 | |
处理大量数据时,是否选取了合适的数据结构和高效的算法 | 综合编程 | |
对hashtable,vector等集合类数据结构的选择和设置是否合适,如正确设置capacity,load factor等参数,数据结构的是否是同步的 | 综合编程 | |
有无滥用String对象, 不停地创建 String 对象 | 综合编程 | |
是否采用通用的线程池、对象池、连接池等cache技术以提高性能 | 综合编程 | |
是否采用内存或硬盘缓冲机制以提高效率 | 综合编程 | |
I/O方面是否使用了合适的类或采用良好的方法以提高性能(如减少序列化,使用buffer类封装流等) | 综合编程 | |
递归方法中的叠代次数是否合适,应该保证在合理的栈空间范围内 | 综合编程 | |
如果调用了阻塞方法,是否考虑了保证性能的措施 | 综合编程 | |
避免过度优化,对性能要求高的代码是否使用profile工具,如Jprobe等 | 工具使用 | |
通用 | 避免重复代码 | 综合编程 |
在public类中,使用访问器方法(getter/setter方法)而不是public域 | 类和接口 | |
最小化局部变量的范围, 需要使用时才定义 | 基础 | |
遵循广泛接受的命名规则, 排版、缩进、空白行、格式化等 | 基础 | |
使用枚举来代替 int, string 常量 | 基础 | |
使用标记接口(marker interface)来定义类型 | 基础 | |
数组类结构是否做了边界校验 | 基础 | |
变量在使用前是否做了初始化 | 基础 | |
注释中适宜地描述了方法的用途、业务逻辑、作者及日期 | 基础 | |
静态代码检查 | 查看静态代码分析器的报告来进行类的添加和修改 | 工具使用 |
资源回收与泄露 | 是否集合中的失效对象的reference 已经设置为 null 可以被回收 | 综合编程 |
是否所有的资源对象被正确释放,如数据库连接、Socket、文件等 | 综合编程 | |
资源是否被释放多次 | 综合编程 | |
避免使用finalizer | 综合编程 | |
控制流与逻辑 | 循环初始值、结束条件以及循环变量递进是否正确 | 基础 |
是否避免了死循环的产生(for, 递归, 对象嵌套) | 基础 | |
循环次数是否正确 | 基础 | |
是否在 for 循环的过程中删除元素 | 基础 | |
switch case 是否缺少 break 和 default; | 基础 | |
if-else 嵌套是否正确 | 基础 | |
&&, ||, ! 逻辑运算符是否使用正确 | 基础 | |
数据库访问 | 数据库设计或SQL语句是否便于移植(注意和性能方面会存在冲突) | 综合编程 |
数据库资源是否正常关闭和释放 | 综合编程 | |
数据库访问模块是否正确封装,便于管理和提高性能 | 综合编程 | |
是否采用合适的事务隔离级别 | 综合编程 | |
是否采用存储过程以提高性能 | 综合编程 | |
是否采用PreparedStatement以提高性能 | 综合编程 | |
网络通信 | socket通讯是否存在长期阻塞问题 | 综合编程 |
发送接收的数据流是否采用缓冲机制 | 综合编程 | |
socket超时处理,异常处理 | 综合编程 | |
数据传输的流量控制问题 | 综合编程 | |
错误处理 | 每次当方法返回时是否正确处理了异常,记录日志到日志文件中 | 日志 |
是否对数据的值和范围的合法进行校验 | 输入检验(Input Validation) | |
在出错路径上是否所有的资源和内存都已经释放 | 综合编程 | |
所有抛出的异常都得到正确的处理,特别是对子方法抛出的异常,在整个调用栈中必须能够被捕捉并处理 | 异常 | |
当调用导致错误发生时,方法的调用者应该得到一个通知 | 异常 | |
对可以恢复的情况使用已受检异常(checked exceptions),对于程序错误使用运行时异常(runtime exceptions) | 异常 | |
是否更多地使用标准异常 | 异常 | |
是否定义了具有合理名称的自定义异常 | 异常 | |
是否“默默地吞掉了”异常 | 异常 | |
面向对象编程 | 通过接口而不是实现类来引用对象, 是否符合面向接口编程的思想 | 设计与重构 |
方法API是否被良好定义, 便于维护和重构 | 设计与重构 | |
重写对象的equals时, 总是重写hashCode | 基础 | |
总是重写对象的 toString | 基础 | |
对象的传值或传引用方面有无问题 | 基础 | |
需要 update 的对象的值是否正确地设置 | 基础 | |
是否大量或频繁地创建临时对象 | 基础 | |
是否尽量使用局部对象(堆栈对象) | 基础 | |
是否使用了全局可变对象且在某处代码里进行了修改 | 基础 | |
是否修改了全局可见 final Reference 的内容 | 基础 | |
在只需要对象reference的地方是否创建了不必要的对象实例 | 基础 | |
类的接口是否定义良好,如参数类型等,避免内部转换 | 基础 | |
是否有丑陋的强制类型转换 | 基础 | |
是否存在不必要的使用反射来获取私密信息 | 基础 | |
返回堆对象的reference,不要返回栈对象的reference | 基础 | |
测试 | 代码变更存在有效的单元测试用例 | 基础 |
是否对错误处理部分的代码进行了测试 | 基础 | |
代码规范 | 是否符合JAVA编码规范(Java Code Conventions) | 标准规范 |
其它 | 配置信息如何获得, 是否有硬编码 | 配置 |
1)增加有用功你得多问问你的需求方,为什么要加这个需求?干这个事到底有多大的价值?能让多少人受益?你得多问问你的需求方,能不能稍微简化一下需求,这样可以让我付出的努力更少一些?你得要多去思考一下,你是在干一个建筑队的活呢?还是在干一个装修队的活?你得要多去思考一下,业务上和用户的最大的痛点是什么?像乔布斯那样,告诉你的产品经理或是业务方,你现在提的10需求,我只能做3个,会是哪3个?为什么是这3个?有用功的来源不是拼命做需求,而是砍需求。2)降低总功你得多问问自己,你有多少时间是在干一些支持性而不是产出性的工作?你得多问问自己,有没有残酷无情地减少重复劳动的劳动密集型的工作?3)形成合力有一个很不错的产品经理对我说,他看了南京那两个小女孩被饿死的消息,感到很震惊。与之有关联的每一方都说自己尽力,但是最终结果人还是饿死了,你几乎不敢相信这是真的。类比一下我们的项目,这种事似乎又发生在我们的公司当中,尤其是大公司中。每一个团队都说自己尽力了,结果项目就是没做好,底层团队说自己只干底层,已经尽力了,前端说自己只负责前端,也尽力了,后端说自己只管后端,不管前端和底层,运维说对于这样的设计和部署自己也尽力了,产品经理,运营都这样说,自己尽力了。你会发现,你几乎很难批评他们,因为他们的确如他们所说的那样,把他们自己的那块都做得很好了,而且的确做得很好了。但是,最终的结果却是:整个产品问题很多。所以说,效率不是每个团队各自的效率,而是整个团队对整个产品负责的共同使命,这样才会现整体的效率。没有整体的效率,只有个体的效率,最终也等于没有效率。T-Shirt Size EstimationAmazon用一种T-Shirt Size 估计的方式来做项目。产品经理会对每一条需求评估上业务影响力的尺寸,如:XXXL 影响一千万人以上或是可以占到上亿美金的市场,XXL,影响百万用户或是占了千万金级别以上的市场,后面还有XL,L,M,S,这样下来。开发团队也一样,要评估投入的人员时间成本,XXXL表示要干1年,XXL干半年,XL干3个月,L干两个月,M干一个月,S干两周以下。等等。于是,当业务影响力是XL,时间人员成本是S,这是最高优先级。当业务影响力是M,时间人员成本是M,这是低优先级。当业务影响力是S,时间人员成本是XL,直接砍掉这个需求。因为是亏的。当业务影响力是XXL,时间人员成本是XXL,需要简化需求,把需求简化成XL,时间人员成本变成M以下。
参考文章:
1. 《11个高效的同行代码评审最佳实践》: http://kb.cnblogs.com/page/153632/
2. 《Java CodeReview 清单》: http://www.importnew.com/12511.html