一、优化的品质
1.简练;2.可读性强;3.模块化;4.层次性;5.设计良好;6.高效;7.优雅;8.清晰。
二、常见的编程规范
1. 基本要求
· 结构清晰,简单易懂,单个函数不超过100行;目标明确,代码精简
· 尽量使用标准库函数和公共函数
· 不随意定义全局变量,尽量使用局部变量
· 使用括号,以避免二义性
2. 可读性要求
· 可读性第一,效率第二
· 保证注释与代码完全一致
· 都有文件头说明,都有函数头说明
· 定义变量时,注释能反映含义;常量定义有说明
· 处理过程每个阶段都有注释说明;典型算法亦是如此
· 使用缩进;循环,分支不超过5层
· 空白行也是一种特殊的注释;一目了然的语句不加注释
3. 结构化要求
· 禁止出现两条等价的支路;用case实现多路分支
· 避免从循环引出多个出口;函数只有一个出口
· 不要用条件赋值语句;避免不必要的分支;不要轻易用条件分支去替换逻辑表达式
4. 正确性与容错性要求
· 首先保证正确,其次才是优美
· 时刻回头检查;修改前考虑对其他程序的影响
· 变量调用前必须被初始化
· 对用户输入进行合法性检查
· 不要比较浮点数的相等,如:10.0*0.1 == 1.0
· 主动处理程序与环境状态的关系,如:打印机是否联机,文件能否逻辑锁定
· 做单元测试
5. 可重用性要求
· 重复使用的完成相对独立功能的算法或代码应抽象为公共控件或类
· 公共空间或类应考虑面向对象思想,减少外界联系,考虑独立性或封装性
三、程序性能测试
1. 计算性能
Java提供了 System.currentTimeMillis()方法,可以得到毫秒级的当前时间,通过该方法来计算执行某一段代码所消耗的时间。
我们可以通过 Java的 java.lang.reflect.Proxy 和 java.lang.reflect.InvocationHandler 利用动态代理来解决枯燥的输入System.currentTimeMillis() 来计算执行时间的问题。
可以参考该例子:http://www.iteye.com/topic/683613
2. 内存消耗
利用 Runtime 类的freeMemory() 和 totalMemory() 方法来考虑程序中的对象所消耗的虚拟机堆空间
参考链接:http://blog.csdn.net/wgw335363240/article/details/8878644
3. 启动时间
4. 可伸缩性
5. 用户察觉性能
四、 初级优化
在 Java 程序中 性能问题大部分原因并不在于 Java 语言,而是在程序本身。养成好的代码编写习惯非常重要。
1. 尽量指定类的 final 修饰符
带有final修饰符的类不可派生。如果指定一个类为 final ,则该类所有的方法都是 final 。Java 编译器会寻找机会内联 (inline) 所有的 final 方法。此举能使性能平均提高 50%。
2. 尽量重用对象
特别是 String 对象的使用中,出现字符串连接情况应用 StringBuffer 代替。
3. 尽量使用局部变量
调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快。其他的都保存在堆(Heap)中,速度较慢。
4. 不要重复初始化变量
默认情况下,调用类的构造函数时,Java 会把变量初始化成确定的值,所有的对象被设置成 null,整数变量设置为 0,float 和 double 为 0.0,逻辑为 false。当一个类从另一个类派生时,尤为要注意,因为用 new 关键词创建一个对象时,构造函数链中的所有构造函数都会被自动调用。
5. Java + Oracle 的应用开发中, Java内嵌的SQL语句使用大写
6. Java 编译过程中,数据库连接、I/O 流操作要及时关闭释放
7. 对象使用完毕后,手动设置为 null
8. 在使用同步机制时,应尽量使用方法同步代替代码块同步
9. 尽量减少对变量的重复计算
如:
- for(int i = 0; i< list.size(); i++){....}
应替换为:
- for(int i = 0,int len = list.size(); i < len; i++){....}
如:
- String str = "aaa";
- if(i == 1){
- list.add(str);
- }
应替换为:
- if(i == 1){
- String str = "aaa";
- list.add(str);
- }
抛出异常首先要创建一个新的对象。Throwable 接口的构造函数用名为 fillInStackTrace() 的本地方法,fillInStackTrace() 方法检查栈,收集调用跟踪信息。只要有异常被抛出,VM 就必要调整调用栈,因为在处理过程中创建了一个新的对象。
异常只能用于错误处理,不应该用来控制程序流程。
12. 不要再循环中使用 Try/Catch 语句,应把其放在最外层
13. StringBuffer 的使用, StringBuffer 表示了可变的、可写的字符串
- StringBuffer(); //默认分配16个字符的空间
- StringBuffer(int size); //分配size个字符的空间
- StringBuffer(String str); //分配16个字符+str.length()个字符空间
你可以通过 StringBuffer 的构造函数来设定它的初始化容量,这样可以明显地提升性能。使用一个合适的容量值来初始化 StringBuffer 永远都是一个最佳的建议。
14. 合理的使用 Java 类 java.util.Vector
简单的说,一个Vector 就是一个 java.lang.Object 实例的数组。 Vector 与数组相似。考虑如下例子:
- Object obj = new Object();
- Vector v = new Vector(100000);
- for(int i = 0; i < 100000; i++){
- v.add(0, obj);
- }
除非有绝对充足的理由要求每次都把新元素插入到 Vector 的前面,否则上面的代码对性能不利。下面的代码比上面的要快好几个数量级:
- Object obj = new Object();
- Vector v = new Vector(100000);
- for(int i = 0; i < 100000; i++){
- v.add(obj);
- }
- for(int i = 0; i++; i < v.length)
改成
- int size = v.size()
- for(int i = 0; i++;i < size)
16. 代码重构:增强代码可读性
17. 不用new 关键词创建类的实例
将对象实现 Clonewable 接口,调用它的clone() 方法。在设计模式的场合,用工厂模式创建对象,则改用 clone() 方法创建新的对象实例非常简单。下面是工厂模式的一个典型实现:
- public static Credit getNewCredit(){
- return new Credit();
- }
改进后的代码使用clone() 方法,如下:
- private static Credit BaseCredit = new Credit();
- public static Credit getNewCredit(){
- return (Credit)BaseCredit.clone();
- }
18. 乘法和除法
- a = a * 8; b = b * 2;
改成移位操作:
- a = a << 3; b = b << 1;
19. 不要讲数组申明为:public static final
20. HaspMap 的遍历效率
如下两种方法:
- Map<String, String[]> paraMap = new HashMap<String, String[]>();
- //第一个循环
- Set<String> appFieldDefIds = paraMap.keySet();
- for(String appFieldDefId : appFieldDefIds){
- String[] values = paraMap.get(appFieldDefId);
- }
- //第二个循环
- for(Entry<String, String[]> entry : paraMap.entrySet()){
- String appFieldDefId = entry.getKey();
- String[] values = entry.getValue();
- }
第一种的实现的效率明显不如第二种实现。第一种是先从 HashMap 中取得 keySet 值,第二种则是每次循环时都取值,增加了CPU要处理的任务。
按照 Map 的概念来看,将key 和 value 分开操作在这里不是个好选择
21. array 和 ArrayList 的使用
array 最高效,但容量固定且无法改变
ArrayList :容量动态增长,但牺牲效率。
具体使用视情况而定。
22. 尽量使用 HashMap 和 ArrayList
除非必要,否则不推荐使用 HashTable,和Vector,他们由于使用同步机制,导致性能的开销。
23. StringBuffer 和 StringBuilder 的区别
java.lang.StringBuffer 线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。
如果性能瓶颈能确定是在 StringBuffer 上,并且确定你的模块不会运行在多线程模式下,否则还是用 StringBuffer 吧。