`
java_xiaodi
  • 浏览: 10094 次
  • 性别: Icon_minigender_1
  • 来自: 河北唐山
社区版块
存档分类
最新评论

高效的使用java

    博客分类:
  • java
阅读更多
每一种语言都有其自身的特点,只有掌握了其自身的特点,才能用它编写出高效的程序。下面就我个人实践所知谈谈javaSE方面的性能问题,
javaEE方面的性能暂不讨论,要是时间可以再写一javaEE方面的性能问题的帖子。

1, 尽量不要使用+号来连接字符串。
2, 对小数据int的Integer封装,尽量的使用Integer.valueOf()创建,而不要使用new来创建。因为Integer类缓存了从-128到256个 状态的Integer。
3, 对Boolean类,要用valueOf()或使用Boolean.TRUE或Boolean.FALSE来创建对象。我个人觉得对Boolean类用private构造函数,可能就会避免不好的使用Boolean类了。
4, 在设计类时应尽可能地避免在类的默认构造函数中创建,初始化大量的对象。
5, 合理的申请数组空间,如果数组中所保存的元素占用内存空间较大或数组本身长度较长的情况,我们釆用可以釆用软引用的技术来引用数组,以“提醒”JVM及时的回收垃圾内存,维护系统的稳定性。
6,  避免创建重复的对象,我们在编写一个方法的时候应该先考虑方法里的局部对象域能否改为private static final,从而避免创建重复的对象。
7, 把try/catch块放入循环体内,会极大的影响性能,如果编译JIT被关闭或者你所使用的一个不带JIT的JVM,性能会将下降21%之多!
8,StringBuffer的构造器会创建一个默认大小(通常是16)的字符数组。在使用中,如果超出这个大小,就会重新分配内存,创建一 个更大的数组,并将原先的数组复制过来,再丢弃旧的数组。在大多数情况下,你可以在创建StringBuffer的时候指定大小,这样 就避免了在容量不够的时候自动增长,以提高性能。
9,   使用Java NIO提高服务端程序的性能。
10,考虑用静态工厂方法替代构造函数。
11,在对大量的数组拷贝时,可以考虑用Arrays.copyOf()来拷贝。
12, 在并发的情况下,要合理的选择同步的策略,应该谨慎的控制synchronized块的大小,不可以将一个操作分解到多个synchronized 但也要尽量地从synchronized块中分离耗时的且不影响并发的操作。
13,要合理的选择集合框架,例如:ArrayList和LinkedList在某些不同的场合,其性能相差很大。对要做大量的插入时,LinkedList 的性能比ArrayList的性能好。对要做大量随机查找的时候用ArrayList的性能比用LinkedList的性能好。还有在不需要并发操作的 情况下,选择非线程安全的集合比线程安全的集合要好。如在非线程安全的要求下,选择ArrayList要比Vector好。
14,如果是想把数据封装成Double类型的,不要这样使用new Double("1.23"),而要应这样使用new Double(1.23),虽然二者都没有语法 的错误,也都能达到预期的结果,但其性能有着很大的差异。
15, 应尽量的通过缓冲流类来提高I/O操作效率,但也要合理的选择缓冲大小 。
    呵呵,好了,今天就写到这吧,以后要是有时间再继续写。小弟我初学java,不对之地,欢迎大家指正,补遗。
分享到:
评论
54 楼 kakaluyi 2008-07-10  
其实楼主整理的挺好的,10个新手也太不厚道了,投新手票的人你们敢说这十几个错误你们从来或者将来肯定不会犯!?
53 楼 j1a2b3c 2008-07-09  
大师呀。还说自己初学呢。这些虽然是基础,但是一个人写的东西好不好全靠基础扎不扎实。现在有很多程序员对这些知识可能都不太了解。
52 楼 yuankai 2008-07-09  
虽然Effective Java  和Thinking in Java都说过了不应优化。
本人觉得LZ说的使用StringBuffer对String操作时是有必要的。
51 楼 sg552 2008-03-05  
完全同意楼上的。

不用优化。

50 楼 icewubin 2008-03-05  
gjsyd 写道
java_xiaodi 写道
danceflash 写道
14,不要在循环语句块中调用length()方法做为循环结束的条件。
15,如果字符串特别长,不要釆用charAt()一一的获取特定位置的字符,而应该调用toCharArray()方法转化为字符数组,然后通过数组 索引值获取指定位置的字符。


请问这两条是为什么?


14条的意思是不要这样使用
   for(int i = 0 ; i < str.length();i++)
  而要这样使用
   int size = length();
   for(int i = 0 ; i < size; i++)
15条是因为charAt()因为每次获取指定索引位置的字符都要引起新的检索过程。

14条是错误的,先不说jdk对length()有优化,只说你的做法就是错的,比如:

   int size = str.length();
   for(int i = 0 ; i < size; i++){
   //此时如果对str有操作的话,很容易产生死循环
   //而且如果对str有插入和弹出的操作的化,size和str.length()已经不一样了
  
}

这个倒是你歪打不正着了,不是说你说的不对,而是你举的例子不好。
你正好举了个String的例子,而Java中String正好是不可变类,length不会变化的。
49 楼 gjsyd 2007-12-11  
java_xiaodi 写道
danceflash 写道
14,不要在循环语句块中调用length()方法做为循环结束的条件。
15,如果字符串特别长,不要釆用charAt()一一的获取特定位置的字符,而应该调用toCharArray()方法转化为字符数组,然后通过数组 索引值获取指定位置的字符。


请问这两条是为什么?


14条的意思是不要这样使用
   for(int i = 0 ; i < str.length();i++)
  而要这样使用
   int size = length();
   for(int i = 0 ; i < size; i++)
15条是因为charAt()因为每次获取指定索引位置的字符都要引起新的检索过程。

14条是错误的,先不说jdk对length()有优化,只说你的做法就是错的,比如:

   int size = str.length();
   for(int i = 0 ; i < size; i++){
   //此时如果对str有操作的话,很容易产生死循环
   //而且如果对str有插入和弹出的操作的化,size和str.length()已经不一样了
  
}
48 楼 heavens 2007-12-11  
同意, 系统的瓶颈通常都是I/O操作, 这方面搞定了就没什么问题了。
47 楼 fhjxp 2007-12-11  
<p>
moonranger 写道
引用
1, 尽量不要使用+号来连接字符串,至少不要在隔行中使用+来连接字符串。因为有的java虚拟机可能对字符串连接+做了性能优化,在都同行的+字符串连接,转化为StringBuffer的append()方法来连接,所以在同行使用+和使用StringBuffer的append 来做连接性能上差不多。
放在多行和放在一行还不一样??!!以前好像没有听说啊,感觉
 String a = "Hello" + "," + "JavaEye"; 
 String a = "Hello" + "," + "JavaEye"; 
对编译器来说应该没区别啊~难道是小弟理解错了?
</p>
<p>“加一个回车键就算隔行”应该不是原文本意(此处本意不等于楼主的本意),真正的隔行应该是指:</p>

<p>String a = "Hello" + ",";</p>
<p>a=a + "JavaEye"; </p>

<p>这样隔行性能损失还是比较大的。而这种 "Hello" + "," + "JavaEye"连加的方式据我自己测试的结果,jdk1.5确实是做了优化相当于用StringBuffer,但比起StringBuilder还是稍微差一点点。</p>
<p>还有一位朋友说喜欢用+拼接sql或者Hql语句,我也有同感,只要不出现那种真正的隔行,我也都用+</p>
46 楼 sorphi 2007-11-30  
关于方法调用那里,尊重你的意见,但是保留我的习惯,:)

iterator倒是最习惯使用,没有你研究和考虑那么深入的原因。
45 楼 icewubin 2007-11-30  
cbhyk 写道
引用

要看应用场合的,如果是一个超过1000次的循环,或者是一段反复被调用的逻辑,或者是其他+号不能连续(编译器无法优化)且被执行频率很高的场合,StringBuffer还是要用的。


类似这样的情况太难遇到了。遇到了自然有对应的办法。把拼装string的操作放在循环以外、做为static final 属性...还是可以用'+'号的


只有类变量才能static final属性,局部量不可以这样定义的。
碰到你这种情况就应该用StringBuffer了,只有极少数情况,编译器才能优化跨多次循环的+号和+=号。
44 楼 icewubin 2007-11-30  
sorphi 写道
icewubin 写道
lzmhehe 写道
icewubin 写道
bfox 写道
14 不要在循环语句块中调用length()方法做为循环结束的条件。
对这条比较怀疑,虽然没看过length()方法的代码,但是做过在循环里,删除list中元素的操作,在循环中删除list里面的元素,list.length()是不变的,因此我觉得length是直接读某个private属性的可能性比较高。

楼主还是指名下是从哪本书里抄过来的吧,免得误导大家。


不用怀疑,我第一时间看过JDK的源码了,大家放心用,没问题的。

不过有错误要指出:
1.list只有size()没有length()。
2.list.size()的实现是有缓存的,当发生add、remove类似的操作会同时更新缓存的。
3.在循环(假定你是指循环遍历的话)的时候删除list里面的元素,list.size()会改变的,大家切记,极其容易犯错。关于循环遍历中删除元素的策略有人要听的话,我可以举两个例子。


在effective java 122页 也有论述,

提倡使用
for(int i=0,len=list.size();i<len;i++) 这种方式
源码 我没有看过,不过我认为这种方式就算没有提高性能,但是这种想法或者是习惯至少没有错
List 性能是不错但是,难保其他的也这么好(这里还包括我们自己写的结构对象)


对于性能调优的3条原则
我十分赞同,但是一些显而易见的,不影响结构的,还是需要做的


关于这种提倡我是不赞成的,不能尽信书,书也有历史局限因素:
1.书中这个例子是指调用者可能不知道List到底是哪个具体的类来实现的,所以采用保守编程法,防止因size()算法效率不高可能导致的性能问题,但实际这种情况很少出现。
2.就算自己实现一个List接口,也是有义务用缓存(没什么高深的,一个private int 变量而已)来优化size()的效率。一般写的实现List的接口的类都会这么做的。
3.这种方式在循环遍历的时候更容易导致出错,size()已经起了变化,但是len没有变。在特殊场合和极端情况(科学计算、深井型的编程、针对特殊实现的List)下是应该这么做,但是90%的情况下,我更反对这么写。



钻一下牛角尖

就算所有实现List接口的都有义务缓存size()

但是临时变量引用的开销总比方法调用开销小吧?

循环遍历的时候,开发者肯定知道list的size()是不变的才采取for(int i = 0,loop=list.size();i<loop;i++){}这种办法

可变集合的遍历倒相比是少数情况


哈哈,果然又有人牛角尖钻到方法调用上来了,方法调用的性能问题在分代垃圾回收之前就已经解决了,根本不用考虑。
新版JDK(JDK 1.3+)下3大优化误区:要减少临时对象的创建、尽量不要用反射(废话,不用反射大家还用什么框架)、尽量减少函数调用。
假设没有JDK的优化,以上三者对性能的影响顺序也是如此,函数调用的影响相对是最小的。所以你既然要用OO和很多基于反射的工具或框架,就别再钻牛角尖说什么函数调用影响性能了。

icewubin 写道
第十三条:实际使用中应该大力提倡LinkedList,并推荐是用迭代子遍历,尽量避免随机访问的需求,一般随机访问根据需求可能会用到HashMap。


还有之前已经提到过,循环遍历一般推荐迭代子,所以我才会说,这种情况反而是应该不多见。
因为如果某个List底层实现是类似于LinkedList的话,get(i)方式访问在数据量过万的时候,性能急剧下降。
43 楼 sorphi 2007-11-29  
icewubin 写道
lzmhehe 写道
icewubin 写道
bfox 写道
14 不要在循环语句块中调用length()方法做为循环结束的条件。
对这条比较怀疑,虽然没看过length()方法的代码,但是做过在循环里,删除list中元素的操作,在循环中删除list里面的元素,list.length()是不变的,因此我觉得length是直接读某个private属性的可能性比较高。

楼主还是指名下是从哪本书里抄过来的吧,免得误导大家。


不用怀疑,我第一时间看过JDK的源码了,大家放心用,没问题的。

不过有错误要指出:
1.list只有size()没有length()。
2.list.size()的实现是有缓存的,当发生add、remove类似的操作会同时更新缓存的。
3.在循环(假定你是指循环遍历的话)的时候删除list里面的元素,list.size()会改变的,大家切记,极其容易犯错。关于循环遍历中删除元素的策略有人要听的话,我可以举两个例子。


在effective java 122页 也有论述,

提倡使用
for(int i=0,len=list.size();i<len;i++) 这种方式
源码 我没有看过,不过我认为这种方式就算没有提高性能,但是这种想法或者是习惯至少没有错
List 性能是不错但是,难保其他的也这么好(这里还包括我们自己写的结构对象)


对于性能调优的3条原则
我十分赞同,但是一些显而易见的,不影响结构的,还是需要做的


关于这种提倡我是不赞成的,不能尽信书,书也有历史局限因素:
1.书中这个例子是指调用者可能不知道List到底是哪个具体的类来实现的,所以采用保守编程法,防止因size()算法效率不高可能导致的性能问题,但实际这种情况很少出现。
2.就算自己实现一个List接口,也是有义务用缓存(没什么高深的,一个private int 变量而已)来优化size()的效率。一般写的实现List的接口的类都会这么做的。
3.这种方式在循环遍历的时候更容易导致出错,size()已经起了变化,但是len没有变。在特殊场合和极端情况(科学计算、深井型的编程、针对特殊实现的List)下是应该这么做,但是90%的情况下,我更反对这么写。



钻一下牛角尖

就算所有实现List接口的都有义务缓存size()

但是临时变量引用的开销总比方法调用开销小吧?

循环遍历的时候,开发者肯定知道list的size()是不变的才采取for(int i = 0,loop=list.size();i<loop;i++){}这种办法

可变集合的遍历倒相比是少数情况
42 楼 hanjian 2007-11-29  
不错学习一下
41 楼 ken1984 2007-11-29  
效率差不了多少,何必钻牛角尖?
40 楼 myworkfirst 2007-11-29  
danceflash 写道
java_xiaodi 写道
danceflash 写道
14,不要在循环语句块中调用length()方法做为循环结束的条件。
15,如果字符串特别长,不要釆用charAt()一一的获取特定位置的字符,而应该调用toCharArray()方法转化为字符数组,然后通过数组 索引值获取指定位置的字符。


请问这两条是为什么?


14条的意思是不要这样使用
   for(int i = 0 ; i < str.length();i++)
  而要这样使用
   int size = length();
   for(int i = 0 ; i < size; i++)
15条是因为charAt()因为每次获取指定索引位置的字符都要引起新的检索过程。



14.
你给出了优化意见,但是原因呢?
JDK1.5中String.java的length()方法源代码如下:
    public int length() {
        return count;
    }
表明他是从一个内部维护的计数器中取值,本身不存在效率问题
那么为什么还要做一个size去缓存呢?尤其是String本身还是一个不可变对象

15.
以下是JDK1.5中String.java的charAt的源代码,我很奇怪搂主是从哪里得到“都要引起新的检索过程”这样的结论的?
    public char charAt(int index) {
        if ((index < 0) || (index >= count)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return value[index + offset];
    }

      对于上面那个length写法,我一般是:
      for(int i = str.length()-1 ; i >=0,i--)  //这样写,就行了.
  对于其性能上,IO操作,以及数据库连接,和socket连接,这些上性能得需要优化的,其它一些小细节,影响不是很大.
sb.append() 方法当然比 str的+效率要高些. 至于类的创建,可以考虑到 单态模式.
39 楼 cbhyk 2007-11-29  
引用

要看应用场合的,如果是一个超过1000次的循环,或者是一段反复被调用的逻辑,或者是其他+号不能连续(编译器无法优化)且被执行频率很高的场合,StringBuffer还是要用的。


类似这样的情况太难遇到了。遇到了自然有对应的办法。把拼装string的操作放在循环以外、做为static final 属性...还是可以用'+'号的
38 楼 icewubin 2007-11-29  
cbhyk 写道
引用
1, 尽量不要使用+号来连接字符串。


恰恰相反,我喜欢故意用+号,这样更直观,性能影响在绝大多数系统中可以忽略不计。需要连接字符串的时候经常是是拼装SQL/HQL,用+号更好,基本可以直接复制到数据库的查询工具中运行,用StringBuffer就麻烦得多。


要看应用场合的,如果是一个超过1000次的循环,或者是一段反复被调用的逻辑,或者是其他+号不能连续(编译器无法优化)且被执行频率很高的场合,StringBuffer还是要用的。
37 楼 yangdefeng95802 2007-11-28  
icewubin 写道
第十四条:str.length是不计算的,没有性能问题的,大家可以自己看看String.java的源代码,我一开始还怀疑自己的理解不对,特意看了下,长度是有一个值专门缓存的,所以没有性能问题。

优化三原则:
第一:不要优化。
第二:不要优化。
第三:不要优化。

楼主中的不少优化其实是没有必要的。
除了刚才说的length以外,列举其他毫无必要的条目。
第一条,据我了解新版jdk跨行一样会优化的。
第四条,有些古老的类没办法的,可以像JDBC驱动那样,要求调用放先调一次Class.forName()可以确保初始化。
第六条,JDK 1.3+以上可以不考虑。
第十三条:实际使用中应该大力提倡LinkedList,并推荐是用迭代子遍历,尽量避免随机访问的需求,一般随机访问会用到HashMap。
第十五条:参考String.java的实现,本身就是数组,任何一本优化书都不会这么说的吧,我开始怀疑楼主参考的书的作者的忽悠程度了。
第十六条:各位同学们,企业计算很少碰到Double,大家赶快换成BigDecimal吧,Double和Float都会产生精度丢失的问题的。

上楼说的有道理!
36 楼 joyfun 2007-11-28  
cbhyk 写道
引用
1, 尽量不要使用+号来连接字符串。


恰恰相反,我喜欢故意用+号,这样更直观,性能影响在绝大多数系统中可以忽略不计。需要连接字符串的时候经常是是拼装SQL/HQL,用+号更好,基本可以直接复制到数据库的查询工具中运行,用StringBuffer就麻烦得多。

拼的字符比较少的话我直接用加
有时候自己看 +的代码太长了也确实不舒服
以前在某个地方看到String 操作频繁的话 最好用
sb.append 
35 楼 cbhyk 2007-11-28  
laiseeme 写道
cbhyk 写道
引用
1, 尽量不要使用+号来连接字符串。


恰恰相反,我喜欢故意用+号,这样更直观,性能影响在绝大多数系统中可以忽略不计。需要连接字符串的时候经常是是拼装SQL/HQL,用+号更好,基本可以直接复制到数据库的查询工具中运行,用StringBuffer就麻烦得多。

某位老大经常说,这样会引起注入攻击


注入跟这个没关系。

相关推荐

Global site tag (gtag.js) - Google Analytics