• 1. 如何编写高质量的代码 陈建良
  • 2. 如何编写高质量的代码软件的质量属性 JAVA高质量代码实例 编写高质量代码的措施
  • 3. 软件的质量属性正确性、有效性、可用性、健壮性、可靠性、容错性、效率、响应时间、吞吐量、负载、可伸缩性、安全性、适应性、可读性、可测试性、可维护性、易用性
  • 4. 软件的质量属性-正确性正确性是软件最基本,最重要的属性。他代表了这个软件能够正确的执行计算并给出用户正确的结果。如果软件不能保证正确性,那么这个软件将没有价值可言。比如,一个总是计算错误的财务软件,显然是没有任何用处的。对小型的,功能单一的软件来说,正确性是显而易见的,要么正确,要么不正确。但是对于大型的复杂的软件来说,衡量正确性的标准都相当复杂或不确定,所以正确性本身也不是个简单的是和否的问题了。需求不等于正确性,需求不仅要求正确性,还会要求其他属性,如性能等,需求也不一定要求100%的正确性,只要计算结果对于最终用户来说是可用的就可以了。
  • 5. 软件的质量属性-有效性 有效性是指软件能在有效的时间内给出计算结果。一个无效的软件,即便其必然能得到正确的结果,也是无价值的。比如,穷举法总是正确的,但是在解决某些问题时,穷举法并不能在有效的时间内给出结果。如果一个用来预报明天天气的软件,却要在后天才能算出明天的天气情况,即使算正确了,也是没有意义的。
  • 6. 软件的质量属性-可用性 可用性和有效性是对正确性和和有效性的一个综合,一个正确而有效的软件,才是可用的软件。如果在一段里,一个软件总能在有效的时间里给出正确的结果,那么这个软件在这段时间里就是可用的。而高可用,就是说一个软件可以在相当长的时间里保持可用性。
  • 7. 软件的质量属性-健壮性 在异常状况下,软件仍能够保持可用性,被称为健壮性。如果一个软件,由于输入数据不正确,或者运行时发生了些不正常状况等,就立刻崩溃,以致于不能再工作,显然是不健壮的。相反的,在这样恶劣的情况下,仍能够工作,则是健壮的。
  • 8. 软件的质量属性-可靠性 可靠性是指一个软件能在很长的时间内保持可用。一个健壮的软件,显然能提可靠性。对于某些持续提供服务的软件来说,高可用是很重要的。比如,网络服务器,我们往往需要它7×24不停的可用。通过故障恢复等措施,可以提供软件的可靠性。
  • 9. 软件的质量属性-容错性 容错性是指软件能够自动的纠正错误的输入,得出正确的结果。比如大部分的浏览器,都能够解析不是很严格正确的html。容错性可以提高最终用户的用户体验。
  • 10. 软件的质量属性-效率 在完成相同的计算任务时,软件占用越少的CPU时间越少的内存空间等计算资源,性能越高。换句话说,在相同的计算资源的条件下,完成的计算任务越多,效率越高。效率就是软件利用计算资源的能力。
  • 11. 软件的质量属性-响应时间 响应是软件对用户操作作出回应的速度。用户使用软件时,犹如和人交流,快速的响应,犹如和用户对答如流,会让用户更加的开心。比如,点击开始按钮后,出现一个显示计算进度的进度条,要比立刻全屏锁定,不让用户做任何操作要好多了即使后者可以更快的完成所有的任务。
  • 12. 软件的质量属性-吞吐量 单位时间内,软件能处理的数据量,或任务量。比如,一个网络服务器,单位时间内内够处理的http请求,和生成的html的数据量。吞吐量侧重于从输入输出上衡量软件的性能。
  • 13. 软件的质量属性-负载 负载是软件能承受的访问压力的能力。同样以网络服务器举例,能同时支持越多的用户访问,负载能力就越强。负载和吞吐量不完全相同。吞吐量很高,不一定能支持很多人访问;相应的,负载很好,单位时间内的输入输出可能并不高。
  • 14. 软件的质量属性-可伸缩性 在不修改,或者很少修改代码的情况下,通过添加硬件计算资源就可以提高软件整体性能的能力。对于大型的服务器软件,可伸缩性是很被重视的。理想情况下,系统的计算能力能随着硬件的添加而线性增长,很可惜,没有软件能达到这一程度。大部分软件在设计之初并没有考虑到可伸缩性,根本就不可伸缩,而另一些软件,增添硬件会使性能下降,或降低可靠性。
  • 15. 软件的质量属性-安全性 软件保护重要资源免受非法访问或恶意攻击的能力。安全性对于某些软件来说是非常重要的,对于另外一些软件来说则不那么重要。安全是一个复杂的工程,往往和整个软件运行环境相关。
  • 16. 软件的质量属性-适应性 软件不需要修改,就可以在别的环境下运行的能力。适应性高的程序,在设计之初就要考虑到软件可能运行的各种计算环境,并做好相对的准备。这样在程序生成后,不需要修改就可以在不同的环境下运行。
  • 17. 软件的质量属性-可移植性 将软件从一个环境下迁移到另一个环境下运行的能力。为了实现可移植,要抽象出软件所依赖的计算资源,在这一抽象层之上开发。移植时,只要修改抽象层在别的环境下的实现,而不必修改其上的部分。
  • 18. 软件的质量属性-一致性 软件的结构一致,是指软件用相同的方式处理相同的问题,用相同的符号表示相同的概念。一致性给程序带来美感,使程序更简单。一致性差的软件,就像被混淆器混淆过,最终只能使程序失去生命力。
  • 19. 软件的质量属性-可读性 软件代码可以被阅读和理解的能力。除了正确和有效,可读性可能就是我们最关心的属性。可读性好的代码,即便有缺陷,还有修正的机会,相反,犹如天书一般的代码,不能进化,没有前途。一致性可以提高可读性。
  • 20. 软件的质量属性-可扩展性 在尽量不修改原有代码的基础上通过添加新的代码而修正缺陷,实现新功能的能力。扩展性要求程序在设计之初就考虑到程序的哪些地方将来可能需要扩展,并留下余地。
  • 21. 软件的质量属性-可测试性 程序对测试方法和测试工具的友好性。容易测试的程序,在变动之后才容易验证其正确性。
  • 22. 软件的质量属性-可维护性 软件投入生产后被维护的难易程度。理想状态下,软件在投入生产后不需要停止服务,只要修改少部分代码,就可以实现新的功能需求或错误修正。一致性好,可读性好,可扩展性好,可测试性好的软件拥有更好的可维护性。一个上线后不久就没人看得懂源程序,修改一个小bug就要牵一发而动全身,修改后不知道其他功能能否正常运行的软件,是维护人员的噩梦。
  • 23. 软件的质量属性-易用性 指最终使用者学习和使用软件的难以程度。
  • 24. 软件的质量属性-相互间的关系各属性并非相互之间没有关系的,有些属性的提高利于其它属性的提高,而有些属性的提高则会降低其他属性。比如,程序中做了很多便于用户使用的计算工作,必然会降低程序的吞吐量;为了提高代码效率而使用各种小技巧导致代码难以阅读和理解;越容易阅读的代码,越容易扩展,越容易维护,等等。具体它们之间是怎样的关系要具体问题具体分析。 软件的众多属性可以分成内在和外在两大类。内在属性是用户感觉不到的,而外在属性是用户感觉得到的属性。比如正确性和响应速度是用户感觉得到的,但是可扩展性和易读性用户就不知道了。我们应当在满足外在属性要求的同时提高内在属性,不能外表光鲜,内里脏乱。不同的软件,不同的场景,对不同的属性的要求是不同的,而且这些属性也有相互冲突的,因此我们不可能兼顾所有,只能抓住主要矛盾。一般情况下,正确性,有效性和易读性是最重要的。
  • 25. JAVA高质量代码实例1、内存管理原理 2、避免创建不必要的对象 3、使类和成员的可访问能力最小化 4、在公有类中使用方法而非公有域 5、检查参数的有效性 6、只针对异常的情况才使用异常 7、对可恢复的情况使用受检异常, 8、对编程错误使用运行时异常 9、只针对异常的情况才使用异常 10、努力使失败保持原子性 11、对共享可变数据的同步访问
  • 26. JAVA高质量代码实例- 内存管理原理 为了判断Java中是否有内存泄露,我们首先必须了解Java是如何管理内存的。Java的内存管理就是对象的分配 和释放问题。在Java中,程序员需要通过关键字new为每个对象申请内存空间 (基本类型除外),所有的对象都在堆 (Heap)中分配空间。另外,对象的释放是由GC决定和执行的。在Java中,内存的分配是由程序完成的,而内存的释放是有GC完成的,这种收支两条线 的方法确实简化了程序员的工作。但同时,它也加重了JVM的工作。这也是Java程序运行速度较慢的原因之一。因为,GC为了能够正确释放对象,GC必须 监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控。 监视对象状态是为了更加准确地、及时地释放对象,而释放对象的根本原则就是该对象不再被引用。 Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点, 首先,这些对象是可达的;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。
  • 27. JAVA高质量代码实例- 内存管理原理 基于数组的集合中的对象游离 public class Stack { private Object[] elements = new Object[MAX_ELEMENTS]; private int size = 0; public void push(Object o) { elements[size++] = o; } public Object pop() { if (size == 0) throw new EmptyStackException(); else { Object result = elements[--size]; // elements[size] = null; return result; } } } 修复这种情况下的对象游离的方法是,当对象从堆栈弹出之后,就消除它的引用
  • 28. JAVA高质量代码实例-避免创建不必要的对象 一般来说,最好能重用对象而不是在每次需要的时候就创建一个相同功能的新对象。重用方式既快速,又流行。如果对象是不可变的,它就始终可以被重用。 最为一个极端的反面例子,考虑下面的语句: Java代码 1.String s = new String("string"); 2.String s = new String("string"); 该语句每次被执行的时候都创建一个新的String实例,但是这些创建对象的动作全都是不必要的。传递给String构造器的参数("string")本身就是一个String实例,功能方面等同于构造器创建的所有对象。如果这种用法是在一个循环中,或者是在一个被频繁调用的方法中,就会创建出成千上万不必要的String实例。改进后:String s="stirng"; 这个版本只用了一个Stirng实例,而且每次执行的时候都创建一个新的实例。而且,它可以保证,对于所有的同一台虚拟机中运行的代码,只要它们包含相同的字符串字面常量,该对象就会被重用。
  • 29. JAVA高质量代码实例-使类和成员的可访问能力最小化 要想区别一个设计良好的模块与一个设计不好的模块,最重要的因素是,这个模块对于外部的其它模块而言,是否隐藏了内部的数据和其他的实现细节。换句话说,就是模块的设计者是否对其进行了良好的封装。 经验表明,你应该尽可能地使每一个类或成员不被外界访问。也就是说,在保证与该模块相关的程序能够正确运行的前提下,尽可能使用最低可能的访问级别。那些一上来不加思索就统统 public 的做法显然是要着重声讨的。 对于顶层的(非嵌套的)类和接口,它们只有2种访问级别:包级私有(package-private)和公有(public)。如果选择了包级私有,那么它只是这个包的实现的一部分,而不是该包对外提供服务的API的一部分。在以后的版本中,你可以对它进行修改、替换甚至删除,而无需担心会伤害到现有的使用者。而如果选择的公有的,你就有义务永远支持它,以保持兼容性。对于成员(域和方法),访问级别共为4种: ● 私有的(private)——只有该成员的顶层类中才能访问 ● 包级私有(package-private)——本包内任何类都可访问 ● 保护的(protected)——本包内的任何类和所在类的子类都可以访问 ● 公有的(public)——任何地方都可以访问 具有公有的静态final数组域几乎总是错误的。注意这句话共有4个定语——公有的、静态的、final的、数组。 Java代码 这可以肯定是错误的 public static final Type[] VALUES = { ... }; 应该改成这样 private static final Type[] PRIVATE_VALUES = { ... }; public static final List VALUE = Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES)); 应该总是尽量降低可访问性,否则你就有义务对你开放出去的东西负责到底。
  • 30. JAVA高质量代码实例-在公有类中使用方法而非公有域 正确的做法应该是在公有类中使用方法而非公有域,公有类不应该直接暴露数据域。 public class PhoneNumber { private String localNumber; public String getLocalNumber() { return localNumber; } public void setLocalNumber(String localNumber) { this.localNumber = localNumber; } } 这样以后客户端只能调用get或set方法来访问域,当数据向外界展示发生变化的时候,只需要更改get ,set方法就可以了,一处修改,到处惠及。
  • 31. JAVA高质量代码实例-检查参数的有效性 参数有效性检查,不得不说一下运行时异常。Java有两种异常:一般异常与运行时异常。继承自Exception的异常称为一般异常,继承自RuntimeException的异常称为运行时异常。与一般异常最大的不同之处在于,函数调用方可以不必处理实现方抛出的运行时异常。 但是异常的抛出通常会被当成一种错误的发生。如果调用方可以不用处理运行时异常,那就意味着调用方必须尽量避免实现方抛出运行时异常。 例如: public static boolean equalObject(Object obj1, Object obj2) { return obj1.equals(obj2); } 上述代码在obj1为null的场合下会抛出NullPointerException,为了避免这种情况发生,正确的写法应该是: public static boolean equalObject(Object obj1, Object obj2) { return (obj1 != null && obj1.equals(obj2)); } 可以看出,运行时异常是对函数调用方一种无形的约束,这种约束方式正好符合参数有效性检查的要求,即调用方法时必须提供正确的参数,否则就会导致系统崩溃。
  • 32. JAVA高质量代码实例-将局部变量的作用域最小化 将局部变量的作用域最小化,可以增加代码的可读性和可维护性,并降低出错的可能性。为了使局部变量的作用域最小化,最好的办法就是在第一次使用的时候再声明之。换句话说,就是在你还没有足够的信息初始化一个变量时,应该推迟它的声明。也就是说几乎每一个局部变量的声明都应该包含一个初始化表达式(有些try-catch的情况下除外)。 Java代码 for (Iterator i = c.iterator(); i.hasNext();){ doSomething(i.next()); } 几乎总是比 Java代码 Iterator i = c.iterator(); while (i.hasNext()){ doSomething(i.next()); } 安全。因为后者的 变量i 的生命周期被拉长了,在之后的代码中就有可能发生二义性Bug或“复制 — 粘贴”Bug。
  • 33. JAVA高质量代码实例-只针对异常的情况才使用异常 异常应该只用于异常的情况下,不该用于正常的控制流 Java代码 try { int i = 0; while(true) range[i++].climb(); } catch(ArrayIndexOutOfBoundsException e) { } 设计良好的API不应该强迫它的客户端为了正常的控制流而使用异常,可以提供状态测试(hashNext())或者可识别的返回值(return null)。
  • 34. JAVA高质量代码实例-对可恢复的情况使用受检异常,对编程错误使用运行时异常 java有三种可抛出的结构(throwable),受检的异常(checked exception)、运行时异常(run-time exception)和错误(error)。 如果期望调用者能够适当地恢复,对于这种情况就应该使用受检的异常。或者在catch中处理,或者传播出去,如sql,io Exception。这样的异常可以提供辅助方法,使得调用者得到一些有助于恢复的信息。 用运行时异常来表明编程错误。大多数运行时异常都是前提违例(precondition violation)。就是没有遵守API规范建立的约定。如数组下标不能越界情况并不是绝对的,如考虑资源枯竭的情形,可能是因为程序的错误引起的,如分配了不合理的大数组。如果资源是因为临时的短缺或者临时需求太大所造成的,这种情况可能就是可恢复的。API设计者需要判断这样的资源是否允许恢复。如果你相信一种情况可能允许恢复,那么使用受检异常,否则使用运行时异常。不清楚,最好使用未受检的异常。
  • 35. JAVA高质量代码实例-努力使失败保持原子性 一般而言,失败的方法调用应该使对象保持在被调用之前的状态。具有这种熟悉的方法被成为具有失败原子性。 最简单的办法莫过于设计一个不可变的对象,如果对象不可变,失败原子性就是显然的了,对于在可变对象上执行操作的方法,获得原子性最常见的办法是,在执行操作之前检查参数的有效性。如 Java代码 public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; return result; } 一般而言,作为方法规范的一部分,产生任何异常都应该让对象保持在该方法调用之前的状态。如果违反这条规则,API文档就应该清楚地指明对象将会处于什么样的状态。
  • 36. JAVA高质量代码实例-对共享可变数据的同步访问 B/S的并发访问,本身就是多线程的,它往往已经由Servlet容器或EJB容器来完成了,我们所需要编写的“服务”只需要关心如何去处理一个客户端的访问即可。在这种情形下,多线程编程往往就被遗忘了。尤其是在企业应用开发和网站开发中,对某一客户的一次服务请求时,大多数情况下是用不着多线程的(业务逻辑没有复杂到那个份上)。 然而,对于那些静态属性和单例模式的对象来说,就必须考略到他们的同步了。就是那些用于在不同客户间传递信息的单例对象和用于记录整个系统(网站)的某些状态的静态属性。 synchronized关键字可以保证在同一时刻,只有一个线程在执行被它“保护”起来的代码块。当一个线程在修改一个对象时,其他线程都不会看到对象处于不一致的状态中。或者说,当一个线程在同步代码块中修改一个对象时,其他线程也进入这个代码块时,看到的对象的状态仍然是第一线程修改前的状态;当第一个线程结束修改离开同步代码块后,其他线程再看此对象就是修改后的状态了。 为了在线程之间可靠地通信,以及为了互斥访问,同步是需要的。 一个网站中有一个计数器,以提示来访者是第几位访客。 Java代码 private static int visitorCounter = 0; public static int getVisitorCounter(){ return ++visitorCounter; } Java代码 private static int visitorCounter = 0; public static synchronized int getVisitorCounter(){ return ++visitorCounter; } 这样就避免了当一个线程正在读/写对象状态时,被另一个线程读到“不稳定”的状态。 总而言之,无论何时,当多个线程共享可变数据的时候,每个读或者写数据的线程必须获得一把锁。
  • 37. 编写高质量代码的措施1、严格遵守编码规范 2、最大程度代码复用 3、细心地单步调试 4、持续的代码重构 5、代码复查
  • 38. 编写高质量代码的措施-严格遵守编码规范 参见《JAVA编码规范》。 包括:变量和方法,类和包的命名规范;代码排版及缩进规范;注释要求和规范等。《JAVA编码规范》有明确详细的规定。
  • 39. 编写高质量代码的措施-最大程度代码复用通过重复使用代码来避免编写新的代码。这样做并不是因为懒,而是因为重新使用已有的代码可以降低成本、增加代码的可靠性并提高它们的一致性。在理想情况下,一个新的项目是将已有的可重新利用的组件进行组合,并将新的开发难度降低到最小。
  • 40. 编写高质量代码的措施-细心地单步调试通过单步跟踪代码,进入白盒测试可以发现程序的隐蔽Bug,虽然单步调试是很费时费力的,但远少于以后在测试或现网环境上发现问题的代价和成本,总体成本会减少。
  • 41. 编写高质量代码的措施-持续的代码重构重构就是在不改变软件现有功能的基础上,通过调整程序代码改善软件的质量、性能,使其程序的设计模式和架构更趋合理,提高软件的扩展性和维护性。 重构是持续的过程,特别有下列症状时,一定要重构代码。 代码中存在重复的代码   重复建设只会导致效率的低效和资源的浪费。程序代码更是不能搞重复建设,如果同一个类中有相同的代码块,请把它提炼成类的一个独立方法,如果不同类中具有相同的代码,请把它提炼成一个公共类,永远不要重复代码。 过大的类和过长的方法 过大的类往往是类抽象不合理的结果,类抽象不合理将降低了代码的复用率。方法是类王国中的诸侯国,诸侯国太大势必动摇中央集权。过长的方法由于包含的逻辑过于复杂,错误机率将直线上升,而可读性则直线下降,类的健壮性很容易被打破。当看到一个过长的方法时,需要想办法将其划分为多个小方法,以便于分而治之。
  • 42. 编写高质量代码的措施-持续的代码重构 续牵一毛而需要动全身的修改   当你发现修改一个小功能,或增加一个小功能时,就引发一次代码地震,也许是你的设计抽象度不够理想,功能代码太过分散所引起的。 类之间需要过多的通讯   A类需要调用B类的过多方法访问B的内部数据,在关系上这两个类显得有点来回引用,可能这两个类本应该在一起,而不应该分家。 过度耦合的信息链   "计算机是这样一门科学,它相信可以通过添加一个中间层解决任何问题",所以往往中间层会被过多地追加到程序中。如果你在代码中看到需要获取一个信息,需要一个类的方法调用另一个类的方法,层层挂接,就象输油管一样节节相连。这往往是因为衔接层太多造成的,需要查看就否有可移除的中间层,或是否可以提供更直接的调用方法。如果你发现有两个类或两个方法虽然命名不同但却拥有相似或相同的功能,你会发现往往是因为开发团队成员协调不够造成的。
  • 43. 编写高质量代码的措施-持续的代码重构 续不完美的设计 每个系统都或多或少存在不完美的设计,刚开始可能注意不到,到后来才会慢慢凸显出来,此时唯有勇于更改才是最好的出路。 缺少必要的注释 虽然许多软件工程的书籍常提醒程序员需要防止过多注释,但这个担心好象并没有什么必要。往往程序员更感兴趣的是功能实现而非代码注释,因为前者更能带来成就感,所以代码注释往往不是过多而是过少,过于简单。人的记忆曲线下降的坡度是陡得吓人的,当过了一段时间后再回头补注释时,很容易发生"提笔忘字,愈言且止"的情形。
  • 44. 编写高质量代码的措施-代码复查 代码复查是一种用来确认方案设计和代码实现的质量保证机制,通过这个机制我们可以对代码,测试过程和注释进行检查。 代码复查的三个前提: 1).代码可以顺利运行; 2).程序员已经做过单元测试; 3).其次最重要的是开发人员对代码已经基本了解。
  • 45. 编写高质量代码的措施-代码复查 续代码复查所做的工作(检查软件的质量属性): 1).完整性检查; 2).一致性检查; 代码的逻辑是否符合设计文档;代码中使用的格式、符号、结构等风格是否保持一致; 3).正确性检查 代码是否符合制定的标准 所有的变量都被正确定义和使用 所有的注释都是准确的 所有的程序调用都使用了正确的参数个数 4).可修改性检查 代码涉及到的常量是否易于修改(如使用配置、定义为类常量、使用专门的常量类等) 代码中是否包含了交叉说明或数据字典,以描述程序是如何对变量和常量进行访问的,代码是否只有一个出口和一个入口(严重的异常处理除外)
  • 46. 编写高质量代码的措施-代码复查 续5).可预测性检查 代码是否无意中陷入了死循环 代码是否是否避免了无穷递归 6).健壮性检查 代码是否采取措施避免运行时错误(如数组边界溢出、被零除、值越界、堆栈溢出等)。 7).结构性检查 程序的每个功能是否都作为一个可辩识的代码块存在,循环是否只有一个入口。 8).可追溯性检查 代码是否对每个程序进行了唯一标识 是否有一个交叉引用的框架可以用来在代码和开发文档之间相互对应 代码是否包括一个修订历史记录,记录中对代码的修改和原因都有记录 是否所有的安全功能都有标识
  • 47. 编写高质量代码的措施-代码复查 续9).可理解性检查 注释是否足够清晰的描述每个子程序 是否使用到不明确或不必要的复杂代码,它们是否被清楚的注释 使用一些统一的格式化技巧(如缩进、空白等)用来增强代码的清晰度 是否在定义命名规则时采用了便于记忆,反映类型等方法 每个变量都定义了合法的取值范围 代码中的算法是否符合开发文档中描述的数学模型 10).可验证性检查 代码中的实现技术是否便于测试。
  • 48. 编写高质量代码的措施-代码复查 续代码复查经验检查项(代码复查检查列表(checklist)) 以下是在实践中建立的检查列表(checklist),通过分类和有针对性的检查项,保证了代码复查可以有的放矢。 1 JAVA编码规范方面检查项 检查项参照JAVA编码规范执行,见《JAVA编码规范(Java Code Conventions)》 2 面向对象设计方面检查项 A) 类设计和抽象是否合适 B) 是否符合面向接口编程的思想 C) 是否采用合适的设计范式
  • 49. 编写高质量代码的措施-代码复查 续3 性能方面检查项 性能检查在大多数代码中都是需要严重关注的方面,也是最容易出现问题的方面,常常有程序员写出了功能和语法没有丝毫问题的代码后,正式运行时却在性能上表现不佳,从而不得不做大量的返工,甚至是推倒重来。 A) 在海量数据出现时,队列,表,文件,在传输,upload等方面是否会出现问题,有无控制,如分配的内存块大小,队列长度等控制参数。 B) 对hashtable,vector等集合类数据结构的选择和设置是否合适。 C) 有无滥用String对象的现象。 D) 是否采用通用的线程池、对象池模块等cache技术以提高性能。 E) 类的接口是否定义良好,如参数类型等,避免内部转换。 F) 是否采用内存或硬盘缓冲机制以提高效率。 G) 并发访问时的应对策略。 H) I/O方面是否使用了合适的类或采用良好的方法以提高性能(如减少序列化,使用buffer类封装流等) I) 同步方法的使用是否得当,是否过度使用。 J) 递归方法中的叠代次数是否合适,应该保证在合理的栈空间范围内。 K) 如果调用了阻塞方法,是否考虑了保证性能的措施。 L) 避免过度优化等
  • 50. 编写高质量代码的措施-代码复查 续4 资源泄漏处理方面检查项 对于JAVA来说由于存在垃圾收集机制,所以内存泄漏不是太明显,但使用不当,仍然存在内存泄漏的问题。当然数据库连接资源不释放的问题也是广大程序员最常见的。 A) 分配的内存是否释放,尤其在错误处理路径上。 B) 错误发生时是否所有的对象被释放,如数据库连接、Socket、文件等
  • 51. 编写高质量代码的措施-代码复查 续5 线程安全方面检查项 线程安全问题实际涉及两个方面,一个是性能,另一个是资源的一致性,我们需要在这两方面做个权衡,现在就是到了权衡利弊的时候了。 A) 代码中所有的全局变量是否是线程安全的。 B) 需要被多个线程访问的对象是否线程安全,检查有无通过同步方法保护。 C) 同步对象上的锁是否按相同的顺序获得和释放以避免死锁,注意错误处理代码。 D) 是否存在可能的死锁或是竞争,当用到多个锁时,避免出现类似情况:线程A获得锁1,然后锁2,线程B获得锁2,然后锁1。 E) 在保证线程安全的同时,要注意避免过度使用同步,导致性能降低。
  • 52. 编写高质量代码的措施-代码复查 续6 程序流程方面检查项 A) 循环结束条件是否准确 B) 是否避免了死循环的产生 C) 对循环的处理是否合适,如循环变量,局部对象,循环次数等能够考虑到性能方面的影响
  • 53. 编写高质量代码的措施-代码复查 续7 数据库处理方面 A) 数据库设计或SQL语句是否便于移植(注意和性能方面会存在冲突) B) 数据库资源是否正常关闭和释放 C) 数据库访问模块是否正确封装,便于管理和提高性能 D) 是否采用合适的事务隔离级别 E) 是否采用存储过程以提高性能 F) 是否采用PreparedStatement以提高性能。
  • 54. 编写高质量代码的措施-代码复查 续8 通讯方面检查项 A) socket通讯是否存在长期阻塞问题 B) 发送接收的数据流是否采用缓冲机制 C) socket超时处理,异常处理 D) 数据传输的流量控制问题
  • 55. 编写高质量代码的措施-代码复查 续9 JAVA对象处理方面检查项 A) 对象生命周期的处理,是否对象的reference已经失效,能够设置为null,并被回收。 B) 在对象的传值和传参方面有无问题,对象的clone方法使用是否过度。 C) 是否大量经常的创建临时对象。 D) 是否尽量使用局部对象(堆栈对象)。 E) 在只需要对象reference的地方是否创建了新的对象实例。
  • 56. 编写高质量代码的措施-代码复查 续10 异常处理方面检查项 JAVA中提供了方便的异常处理机制,但普遍存在的是异常被捕获,但并没有得到处理。我们可以打开一段代码,最常见的现象是进入某个方法后,一个大的try/catch将所有代码行括住,然后在catch中将异常打印到控制台,而且该异常是Exception对象。 A) 每次当方法返回时是否正确处理了异常,如最简单的处理,记录日志到日志文件中 B) 是否对数据的值和范围是否合法进行校验。 C) 在出错路径上是否所有的资源和内存都已经释放 D) 所有抛出的异常都得到正确的处理,特别是对子方法抛出的异常,在整个调用栈中必须能够被捕捉并处理。 E) 当调用导致错误发生时,方法的调用者应该得到一个通知 F) 不要忘了对错误处理部分的代码进行测试,很多代码在正常情况下执行良好,而一旦出错,整个系统就崩溃了
  • 57. 编写高质量代码的措施-代码复查 续11 方法(函数)方面检查项 A) 方法的参数是否都做了校验 B) 数组类结构是否做了边界校验 C) 变量在使用前是否做了初始化 D) 返回堆对象的reference,不要返回栈对象的reference E) 方法API是否被良好定义,即是否尽量面向接口编程,便于维护和重构
  • 58. 编写高质量代码的措施-代码复查 续12 安全方面检查项 A) 对命令行执行的代码,需要详细检查命令行参数 B) WEB类程序检查是否对访问参数进行合法性验证 C) 重要信息的保存是否选用合适的加密算法 D) 通讯时考虑是否选用安全的通讯方式
  • 59. 谢 谢