代码优化,可能说起来一些人觉得没用.可是我觉得应该平时开发过程中,就尽量要求自己,养成良好习惯,一个个小的优化点,积攒起来绝对是有大幅度效率提升的。
代码优化目标:
1.減小代码体积
2.提高整个系统的运行效率,代码细节忧化
3.尽量指定类,方法的final修饰符,带有final修饰符的类是不可派生的,在Java核心APl中,有许多应用final的例子,例如java.lang.string,整个类都是finaI的.为类指定final修饰符可以让类不可以被继承,为方法指定finaI可以让方法不可以被重写,如果指定一个类为finaI,该类的所有方法都是final的- Java编译器会寻找机会内联所有的final方法,内联对于提升Java运行效率作用重大,具体参见Java這行期优化,此举能够提升性能平均50%。
4. 尽量重用对象
特别是 string对象的使用,出现字特申連接时应该使用 StringBuilder/StringBuffer代替-由于 Java虚拟机不仅要花时间生成对象,以后可能还需要花时间对这些对象进行垃圾回收和处理,因此,,生成过多的对象将会给程序的性能带来很大的影响,
5.尽可能使用局部対象
调用方法时传選的參数以及在调用中创
建的临时变量都保存在浅中速度较快,,其他变量,如静态变量、实例变量等,,都在堆中创建,速度较慢-另外, 栈中创建的变量,随着方法的运行结東,,这些内容就没了,不需要额外的垃圾回收,
6. 及时关闭流
Java编程中, 进行数据库连接、l/o流操作时务必小心,在使用完毕后,及时关闭以释放資源-因为对这些大对象的操作会造成系统大的开销,稍有不慎,将会导致严重的后果。
7. 尽量减少对变量的重复计算
明确一个概念,对方法的调用,即使方法中只有一句语句,也是有消耗的,包括创建栈帧、调用方法时保护现场、调用方法完毕时恢复现场等-所以例如下面的操作:
for(int i= 01 i< list.size0; i++)
{_}建议答換为, for(int i= 0, int length= list.size(); i< length; i++) {_}这样,在list.size0很大的时候.就減少了很多的消耗
8、尽量采用懒加载的策略.即在需要的时候才创建
例如:String str= "aaa";
if(i== 1) {
list.add(str); }建议替換为: if(i== l) {String str= •aaan;
list.add(str); }
9、慎用异常
异常对性能不利-抛出异常首先要创建一个新的对象, Throwable接口的构造函数调用名为 filllnstackrrace0的本地同步方法, filllnstackrrace0方法检査栈堆,收集调用跟踪信息-只要有异常被抛出, Java虚拟机就必须调整调用栈堆,因为在处理过程中创建了一个新的对象-异常只能用于错误处理,不应该用来控制程序流程。
10、不要在循环中使用try_catch_ ,应该把其放在最外,除非不得已-如果毫无理由地这么写了,只要你的领导資深一点、有强追症一点,八成就要骂你为什么写出这种垃圾代码来了。
11、如果能估计到带加的内容长度,为底层以数组方式实現的集合、 工具类指定初始长度
比如ArrayList、 LinkedLlist、StringBuiIder、 StringBufler、HashMap、 HashSet等等,以StringBuilder为例:
(l) StringBuilder//般认分配l6个字符的空间
(2) StringBuilder(int size) //默认分配size个字符的空间
(3) StringBuilder(String str) //般认分配l6个字符+str.length个字符空间,可以通过类(这里指的不仅仅是上面的
stringBuilder)的来设定它的初始化容量, 这样可以明显地提升性能-比如StringBuilder吧, length表示当前的stringBuilder能保持的字待数量.因为当stringBuilder达到最大容量的时候,它会将自身容量增加到当前的2倍再加2 , 无论何时只要stringBuilder达到它的最大容量, 它就不得不创建一个新的字符数组然后将l日的字符数组内容拷贝到新字符数组中一一这是十分耗费性能的一个操作-试想,如果能预估到字符数组中大概要存放5ooo个字符而不指定长度,最接近5ooo的2次幂是4o96,每次扩容加的2 不管,那么:
(l)在4096的基础上.再申请 8194个大小的字符数组,加起来相当于一次申清了l229o个大小的字符数组,如果一开始能指定5ooo个大小的字特数组,就节省了一倍以上的空间
(2)把原来的4o96个字符持贝到新的的字特数组中去、这样,既浪费内存空间,又降低代得运行效率-所以,给底层以数组实現的集合、工具类设置一个合理的初始化容量是不错的,这会带来立竿见影的效果-但是,注意,像HashMap这种是以数组+链表实现的集合, 别把初始大小和你估计的大小设置得一样,因为一个table上只連接一个对象的可能性几乎为o,初始大小建议设置为2的N次幂,如果能估计到有2ooo个元素,设置成new HashMap(l28)、 new HashMap(256) 都可以。
同样道理,应尽可能的以指明容量大小的方式对ArrayList进行实例化
public ArrayList();默认的构造器,JAVA以10个元素的大小初始化
public ArrayList(int);用指定的容量大小大小初始化
不指明容量大小时,如集合容量不够,则JAVA会以1.5倍的容量递增扩充,而且每次扩充,系统会重新拷贝一遍已经加入到ArrayList的数据,从而导致额外的内存开销。
补充:使用StringBuilder来连接字符串
有很多不同的选项来连接Java中的字符串。例如,您可以使用简单的+或+ =,StringBuffer或一个StringBuilder。
那么,你应该选择哪种方法?
答案取决于连接字符串的代码。如果以编程方式将新内容添加到字符串中,例如在for循环中,则应使用StringBuilder。它很容易使用,并提供比StringBuffer更好的性能。但请记住,与StringBuffer相比,StringBuilder不是线程安全的,可能不适合所有用例。
你只需要实例化一个新的StringBuilder并调用append方法来向String中添加一个新的部分。而当你添加了所有的部分,你可以调用toString()方法来检索连接的字符串。
有很多不同的选项来连接Java中的字符串。例如,您可以使用简单的+或+ =,StringBuffer或一个StringBuilder。
那么,你应该选择哪种方法?
答案取决于连接字符串的代码。如果以编程方式将新内容添加到字符串中,例如在for循环中,则应使用StringBuilder。它很容易使用,并提供比StringBuffer更好的性能。但请记住,与StringBuffer相比,StringBuilder不是线程安全的,可能不适合所有用例。
你只需要实例化一个新的StringBuilder并调用append方法来向String中添加一个新的部分。而当你添加了所有的部分,你可以调用toString()方法来检索连接的字符串。
在一个语句中使用+连接字符串
当你用Java实现你的第一个应用程序时,可能有人告诉过你不应该用+来连接字符串。 如果您在应用程序逻辑中连接字符串,这是正确的。 字符串是不可变的,每个字符串连接的结果都存储在一个新的String对象中。 这需要额外的内存,并减慢你的应用程序,特别是如果你在一个循环内连接多个字符串。
在这些情况下,您应该遵循上面的规则并使用StringBuilder。
但是,如果您只是将字符串分成多行来改善代码的可读性,情况并非如此。
在这些情况下,你应该用一个简单的+来连接你的字符串。 您的Java编译器将优化这个并在编译时执行连接。 所以,在运行时,你的代码将只使用1个字符串,不需要连接。
12、当复制大量数据时,使用 System.arraycopy()命令
13、乘法和除法使用移位操作
例如: for(val= 0; val< l00000; val +=5){a=val*8:b=val/2;}用移位操作可以极大地提高性能,因为在计算机底层,对位的操作是最方便、最快的,因此建议修改为: for(val= o:val<100000; val+= 5) { a=val << 3; b = val>> 1;}移位操作星然快,但是可能会使代码不太好理解,因此最好加上相应的注释
14、循环内不要不断创建对象引用及不要在循环体内声明变量
例如: for(inti= l:i<=count; i++) {0bject obj= new 〇bject0; }这种做法会导致内存中有count份object对象引用存在, count很大的话,就耗费内存了,,建议为改为: 〇bject obj= null;for(int i= 01 i<=count; i十十) {obj= new object0; }这样的话,内存中只有一份Object对象引用,每次new完object0的时候, Object对象引用指向不同的0bject 罢了,但是内存中只有一份, 这样就大大节省了内存空间 -
l5、基于效率和类型检査的考虑, 应该尽可能使用array,无法确定数组大小时才使用ArrayList
l6、 尽量使用HashMap、 ArrayList、stringBuilder,除非线程安全需要,否则不推荐使用Hashtable、 Vector、stringBuffer,后三者由于使用同步机制而导致了性能开销
l7、不要将数组声明为 public static final因为这室无意义, 这样只是定义了引用为 static final, 数组的内容还是可以随意改变的,将数组声明为 public更是一个安全構洞,这意味着这个数组可以被外部类所改变
l8、 尽量在合适的场合使用单例
使用单例可以減轻加裁的负担、结短加载的时间、提高加裁的效率,但并不是所有地方都适用于単例,笛単来说,単例主要适用于以下三个方面:
(l)控制資源的使用,通过线程同步来控制資源的并发访问
(2)控制实例的产生,以达到节约資源的目的
(3)控制数据的共事,在不建立直接关联的条件下,让多个不相关的进程或线程之间实現通信
19、 尽量进免随意使用静态变量
要知道,当某个对象被定义为 static的变量所引用,那么 gc通常是不会回收这个対象所占有的堆内存的,如:
public class A{private static B b= new B0; }
20、避免使用包装类构造函数
按照SUN公司的说明,使用自动装箱或静态工厂方法比使用new一个对象快3到4倍,该规则可以用在valueOf或其它静态工厂的调用中(如:Short、Integer, Long、Double, Byte 、Boolean等)。
public static void main(String args[]){
//不推荐写法 int a=new Integer(100); System.out.println(a); //虽然这对于jvm来说,体现不出来明显效果。 //推荐写法 int b=Integer.valueOf(100); System.out.println(b); }21、面向接口编程时,推荐使用接口的声明方式
为什么要用 List list = new ArrayList() ,而不用 ArrayList alist = new ArrayList()呢?
问题就在于List有多个实现类,现在你用的是ArrayList,也许哪一天你需要换成其它的实现类,如 LinkedList或者Vector等等,这时你只要改变这一行就行了: List list = new LinkedList(); 其它使用了list地方的代码根本不需要改动。 假设你开始用 ArrayList alist = new ArrayList(), 这下你有的改了,特别是如果你使用了 ArrayList特有的方法和属性。List list = new ArrayList();这句创建了一个ArrayList的对象后把上溯到了List。此时它是一个List对象了,有些ArrayList有但是List没有的属性和方法,它就不能再用了。
而ArrayList list=new ArrayList();创建一对象则保留了ArrayList的所有属性。List list = new ArrayList();
ArrayList arrayList = new ArrayList(); list.trimToSize(); //错误,没有该方法。 arrayList.trimToSize(); //ArrayList里有该方法。22、单个方法代码行数尽量保持在80-100行之内
23、数据库及磁盘IO等操作,必须在try-catch-finally块的finally中执行close()方法
如有多个对象需要关闭,则须分别对每个对象的close()方法进行try-catch,以防止出现一个对象关闭失败而导致其他对象都未关闭的情况出现
24、不要在循环体内进行数据库的“连接-关闭”操作
如有大批量的数据需要修改,建议使用PreparedStatement的Batch功能(一次性发送多个操作给数据库)
25、大量(如超过五次以上的“+=”运算)的字符串操作应使用StringBuilder或StringBuffer,尽量避免使用String
26、不要在循环条件中使用表达式
27、集合中的数据如果不使用了应该及时释放
由于集合保存了对象的引用,虚拟机的垃圾收集器就不会回收
28、非正常运行产生的异常被捕获后,必须对异常进行处理
在非finally块代码中catch的异常应该重新抛出经过封装的异常,在finally中再次catch的异常不应该再次抛出,应该写日志。无论是抛出异常还是记录日志,都要传递异常对象。日志应记录详细的描述信息,避免调用异常对象的getMessage()方法,直接将该异常对象作为参数传递。
29、尽量定位异常类型,不要一律catch(Exception ex)
当需要在某些出口捕获所有可能出现的运行时异常或Error时,可以catch Exception 或Throwable,尽量避免一个方法中一个大的try块,catch一个Exception的代码方式,如果有必要可以使用多个try-catch块分别处理
30、不要将赋值运算符用在容易与相等关系运算符混淆的地方,如:我们可将“if (a == b && c == d)”改为可读性更强的“if ((a == b) && (c == d)) ”,可将“x >= 0 ? x : -x;"改为”(x >= 0) ? x : -x;“
31、尽可能使用基本数据
避免任何开销并提高应用程序性能的另一种简便快速的方法是使用基本类型而不是其包装类。 所以,最好使用int来代替Integer,或者使用double来代替Double。 这允许您的JVM将值存储在栈而不是在堆中,以减少内存消耗,并更高效地处理它。
32、尽量避免使用BigInteger和BigDecimal
由于我们已经在讨论数据类型,所以我们也应该快速浏览一下BigInteger和BigDecimal。 尤其是后者因其精确性而受欢迎。 但是这是有代价的。
BigInteger和BigDecimal需要更多的内存比一个long或double,并且看起来会降低所有的运行效率。 所以,如果你需要额外的精度,或者如果你的数字将超过一个长的范围,最好三思。 这可能是您需要更改以解决性能问题的唯一方法,特别是在实施数学算法时。
33、使用Apache Commons的StringUtils.Replace来替代String.replace
一般来说,String.replace方法工作正常,效率很高,尤其是在使用Java 9的情况下。但是,如果您的应用程序需要大量的替换操作,并且没有更新到最新的Java版本,那么它仍然是有意义的 检查更快和更有效的替代品。
一个候选项是Apache Commons Lang的StringUtils.replace方法。 正如Lukas Eder在他最近的一篇博客文章中所描述的,它远远超过了Java 8的String.replace方法。
而且这只需要很小的改动。 您需要将Apache的Commons Lang项目的Maven依赖项添加到您的应用程序pom.xml中,并将String.replace方法的所有调用替换为StringUtils.replace方法。