竹笋

首页 » 问答 » 灌水 » 吐血整理必须收藏,44条代码优化细节
TUhjnbcbe - 2023/5/13 18:49:00

来源:五月的仓颉链接:cnblogs.
  list.add(str);}

7、慎用异常

异常对性能不利。抛出异常首先要创建一个新的对象,Throwable接口的构造函数调用名为

fillInStackTrace()

的本地同步方法,

fillInStackTrace()

方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,Java虚拟机就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。异常只能用于错误处理,不应该用来控制程序流程。

8、不要在循环中使用try…catch…,应该把其放在最外层

根据网友们提出的意见,这一点我认为值得商榷,欢迎大家提出看法!

9、如果能估计到待添加的内容长度,为底层以数组方式实现的集合、工具类指定初始长度

比如ArrayList、LinkedLlist、StringBuilder、StringBuffer、HashMap、HashSet等等,以StringBuilder为例:

StringBuilder()
  
  
  //默认分配16个字符的空间StringBuilder(intsize)
  //默认分配size个字符的空间StringBuilder(Stringstr) //默认分配16个字符+str.length()个字符空间

可以通过类(这里指的不仅仅是上面的StringBuilder)的构造函数来设定它的初始化容量,这样可以明显地提升性能。比如StringBuilder吧,length表示当前的StringBuilder能保持的字符数量。

因为当StringBuilder达到最大容量的时候,它会将自身容量增加到当前的2倍再加2,无论何时只要StringBuilder达到它的最大容量,它就不得不创建一个新的字符数组然后将旧的字符数组内容拷贝到新字符数组中----这是十分耗费性能的一个操作。试想,如果能预估到字符数组中大概要存放个字符而不指定长度,最接近的2次幂是,每次扩容加的2不管,那么:

在的基础上,再申请个大小的字符数组,加起来相当于一次申请了个大小的字符数组,如果一开始能指定个大小的字符数组,就节省了一倍以上的空间把原来的个字符拷贝到新的的字符数组中去这样,既浪费内存空间又降低代码运行效率。所以,给底层以数组实现的集合、工具类设置一个合理的初始化容量是错不了的,这会带来立竿见影的效果。

但是,注意,像HashMap这种是以数组+链表实现的集合,别把初始大小和你估计的大小设置得一样,因为一个table上只连接一个对象的可能性几乎为0。初始大小建议设置为2的N次幂,如果能估计到有个元素,设置成

newHashMap()

newHashMap()

都可以。

10、当复制大量数据时,使用

System.arraycopy()

命令

11、乘法和除法使用移位操作

例如:

for(val=0;val;val+=5){a=val*8;
  b=val/2;}

用移位操作可以极大地提高性能,因为在计算机底层,对位的操作是最方便、最快的,因此建议修改为:

for(val=0;val;val+=5){a=val3;
  b=val1;}

移位操作虽然快,但是可能会使代码不太好理解,因此最好加上相应的注释。

12、循环内不要不断创建对象引用

例如:

for(inti=1;i=count;i++){Objectobj=newObject();}

这种做法会导致内存中有count份Object对象引用存在,count很大的话,就耗费内存了,建议为改为:

Objectobj=null;for(inti=0;i=count;i++){obj=newObject();}

这样的话,内存中只有一份Object对象引用,每次

newObject()

的时候,Object对象引用指向不同的Object罢了,但是内存中只有一份,这样就大大节省了内存空间了。

13、基于效率和类型检查的考虑,应该尽可能使用array,无法确定数组大小时才使用ArrayList

14、尽量使用HashMap、ArrayList、StringBuilder,除非线程安全需要,否则不推荐使用Hashtable、Vector、StringBuffer,后三者由于使用同步机制而导致了性能开销

15、不要将数组声明为publicstaticfinal

因为这毫无意义,这样只是定义了引用为

staticfinal

,数组的内容还是可以随意改变的,将数组声明为public更是一个安全漏洞,这意味着这个数组可以被外部类所改变

16、尽量在合适的场合使用单例

使用单例可以减轻加载的负担、缩短加载的时间、提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面:

控制资源的使用,通过线程同步来控制资源的并发访问控制实例的产生,以达到节约资源的目的控制数据的共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信17、尽量避免随意使用静态变量

要知道,当某个对象被定义为static的变量所引用,那么gc通常是不会回收这个对象所占有的堆内存的,如:

publicclassA{privatestaticBb=newB();}

此时静态变量b的生命周期与A类相同,如果A类不被卸载,那么引用B指向的B对象会常驻内存,直到程序终止

18、及时清除不再需要的会话

为了清除不再活动的会话,许多应用服务器都有默认的会话超时时间,一般为30分钟。当应用服务器需要保存更多的会话时,如果内存不足,那么操作系统会把部分数据转移到磁盘,应用服务器也可能根据MRU(最近最频繁使用)算法把部分不活跃的会话转储到磁盘,甚至可能抛出内存不足的异常。如果会话要被转储到磁盘,那么必须要先被序列化,在大规模集群中,对对象进行序列化的代价是很昂贵的。因此,当会话不再需要时,应当及时调用HttpSession的

invalidate()

方法清除会话。

19、实现RandomAccess接口的集合比如ArrayList,应当使用最普通的for循环而不是foreach循环来遍历

这是JDK推荐给用户的。JDKAPI对于

RandomAccess

接口的解释是:实现

RandomAccess

接口用来表明其支持快速随机访问,此接口的主要目的是允许一般的算法更改其行为,从而将其应用到随机或连续访问列表时能提供良好的性能。

实际经验表明,实现RandomAccess接口的类实例,假如是随机访问的,使用普通for循环效率将高于使用foreach循环;反过来,如果是顺序访问的,则使用Iterator会效率更高。可以使用类似如下的代码作判断:

if(listinstanceofRandomAccess){for(inti=0;ilist.size();i++){}}else{Iterator?iterator=list.iterable();while(iterator.hasNext()){iterator.next()}}

foreach循环的底层实现原理就是迭代器Iterator,参见Java语法糖1:可变长度参数以及foreach循环原理。所以后半句反过来,如果是顺序访问的,则使用Iterator会效率更高的意思就是顺序访问的那些类实例,使用foreach循环去遍历。

1
查看完整版本: 吐血整理必须收藏,44条代码优化细节