• 网络学院
  • IT资讯
  • 操作系统
  • 网络技术
  • 软件应用
  • 办公软件
  • 编程技术
  • 网站架设
  • 数据库类
  • 平面设计
  • 多媒体类
  • 游戏资讯
  • 教学论文
  • 认证考试
漫谈Java程序的性能优化
  站点:
  • 首 页
  • 最新软件
  • 文章教程
  • 国内软件
  • 国外软件
  • 绿色软件
  • 源码下载
  • 字体下载
漫谈Java程序的性能优化
软件发布 漫谈Java程序的性能优化
网络软件 系统工具 应用软件 联络聊天 图形图像 多媒体类 行业软件 游戏娱乐 编程开发 安全相关 教育教学 数码软件 绿软下载
热门软件: QQ 瑞星 pplive e话通 木马克星 千千静听 office2000 五笔字根 Photoshop 视频分割
返回文章教程首页 >> 文章首页 >> 编程技术 >> JAVA教程 >> 漫谈Java程序的性能优化

漫谈Java程序的性能优化

添加时间: 2006-2-18 11:18:31  作者: JAVA教程  阅读次数:86   来源: http://www.d9soft.com

       

Java使得复杂应用的开发变得相对简单,毫无疑问,它的这种易用性对Java的大范围流行功不可没。然而,这种易用性实际上是一把双刃剑。一个设计良好的Java程序,性能表现往往不如一个同样设计良好的C++程序。在Java程序中,性能问题的大部分原因并不在于Java语言,而是在于程序本身。养成好的代码编写习惯非常重要,比如正确地、巧妙地运用java.lang.String类和java.util.Vector类,它能够显著地提高程序的性能。下面我们就来具体地分析一下这方面的问题。

  在java中,使用最频繁、同时也是滥用最多的一个类或许就是java.lang.String,它也是导致代码性能低下最主要的原因之一。请考虑下面这个例子:

String s1 = "Testing String";
String s2 = "Concatenation Performance";
String s3 = s1 + " " + s2;

  几乎所有的Java程序员都知道上面的代码效率不高。那么,我们应该怎么办呢?也许可以试试下面这种代码:

StringBuffer s = new StringBuffer();
s.append("Testing String");
s.append(" ");
s.append("Concatenation Performance");
String s3 = s.toString();

  这些代码会比第一个代码片段效率更高吗?答案是否定的。这里的代码实际上正是编译器编译第一个代码片段之后的结果。既然与使用多个独立的String对象相比,StringBuffer并没有使代码有任何效率上的提高,那为什么有那么多的Java书籍批评第一种方法、推荐使用第二种方法?

  第二个代码片段用到了StringBuffer类(编译器在第一个片段中也将使用StringBuffer类),我们来分析一下StringBuffer类的默认构造函数,下面是它的代码:

public StringBuffer() { this(16); }

  默认构造函数预设了16个字符的缓存容量。现在我们再来看看StringBuffer类的append()方法:

public synchronized StringBuffer append(String str) {
if (str == null) {
str = String.valueOf(str);
}
int len = str.length();
int newcount = count + len;
if (newcount > value.length) expandCapacity(newcount);
str.getChars(0, len, value, count);
count = newcount; return this;
}

  append()方法首先计算字符串追加完成后的总长度,如果这个总长度大于StringBuffer的存储能力,append()方法调用私有的expandCapacity()方法。expandCapacity()方法在每次被调用时使StringBuffer存储能力加倍,并把现有的字符数组内容复制到新的存储空间。

  在第二个代码片段中(以及在第一个代码片段的编译结果中),由于字符串追加操作的最后结果是“Testing String Concatenation Performance”,它有40个字符,StringBuffer的存储能力必须扩展两次,从而导致了两次代价昂贵的复制操作。因此,我们至少有一点可以做得比编译器更好,这就是分配一个初始存储容量大于或者等于40个字符的StringBuffer,如下所示:

StringBuffer s = new StringBuffer(45);
s.append("Testing String");
s.append(" ");
s.append("Concatenation Performance");
String s3 = s.toString();

  再考虑下面这个例子:

String s = "";
int sum = 0;
for(int I=1; I<10; I++) {
sum += I;
s = s + "+" +I ;
}
s = s + "=" + sum;

分析一下为何前面的代码比下面的代码效率低:

StringBuffer sb = new StringBuffer();
int sum = 0;
for(int I=1;
I<10; I++){
sum + = I;
sb.append(I).append("+");
}
String s = sb.append("=").append(sum).toString();

  原因就在于每个s = s + "+" + I操作都要创建并拆除一个StringBuffer对象以及一个String对象。这完全是一种浪费,而在第二个例子中我们避免了这种情况。

  我们再来看看另外一个常用的Java类??java.util.Vector。简单地说,一个Vector就是一个java.lang.Object实例的数组。Vector与数组相似,它的元素可以通过整数形式的索引访问。但是,Vector类型的对象在创建之后,对象的大小能够根据元素的增加或者删除而扩展、缩小。请考虑下面这个向Vector加入元素的例子:

Object obj = new Object();
Vector v = new Vector(100000);
for(int I=0;
I<100000; I++) { v.add(0,obj); }

  除非有绝对充足的理由要求每次都把新元素插入到Vector的前面,否则上面的代码对性能不利。在默认构造函数中,Vector的初始存储能力是10个元素,如果新元素加入时存储能力不足,则以后存储能力每次加倍。Vector类就象StringBuffer类一样,每次扩展存储能力时,所有现有的元素都要复制到新的存储空间之中。下面的代码片段要比前面的例子快几个数量级:

Object obj = new Object();
Vector v = new Vector(100000);
for(int I=0; I<100000; I++) { v.add(obj); }

  同样的规则也适用于Vector类的remove()方法。由于Vector中各个元素之间不能含有“空隙”,删除除最后一个元素之外的任意其他元素都导致被删除元素之后的元素向前移动。也就是说,从Vector删除最后一个元素要比删除第一个元素“开销”低好几倍。

  假设要从前面的Vector删除所有元素,我们可以使用这种代码:

for(int I=0; I<100000; I++)
{
 v.remove(0);
}

  但是,与下面的代码相比,前面的代码要慢几个数量级:

for(int I=0; I<100000; I++)
{
 v.remove(v.size()-1);
}

  从Vector类型的对象v删除所有元素的最好方法是:

v.removeAllElements();

  假设Vector类型的对象v包含字符串“Hello”。考虑下面的代码,它要从这个Vector中删除“Hello”字符串:

String s = "Hello";
int i = v.indexOf(s);
if(I != -1) v.remove(s);

  这些代码看起来没什么错误,但它同样对性能不利。在这段代码中,indexOf()方法对v进行顺序搜索寻找字符串“Hello”,remove(s)方法也要进行同样的顺序搜索。改进之后的版本是:

String s = "Hello";
int i = v.indexOf(s);
if(I != -1) v.remove(i);

  这个版本中我们直接在remove()方法中给出待删除元素的精确索引位置,从而避免了第二次搜索。一个更好的版本是:

String s = "Hello"; v.remove(s);

  最后,我们再来看一个有关Vector类的代码片段:

for(int I=0; I++;I
  如果v包含100,000个元素,这个代码片段将调用v.size()方法100,000次。虽然size方法是一个简单的方法,但它仍旧需要一次方法调用的开销,至少JVM需要为它配置以及清除堆栈环境。在这里,for循环内部的代码不会以任何方式修改Vector类型对象v的大小,因此上面的代码最好改写成下面这种形式:

int size = v.size(); for(int I=0; I++;I
  虽然这是一个简单的改动,但它仍旧赢得了性能。毕竟,每一个CPU周期都是宝贵的。

  拙劣的代码编写方式导致代码性能下降。但是,正如本文例子所显示的,我们只要采取一些简单的措施就能够显著地改善代码性能。

 

上下文章:

 

上一篇文章: 漫谈Hibernate的前世今生 下一篇文章: Java 2005回顾&2006展望

相关文章:

  • Vista系统的官方优化指南文档下载
  • 上网高效率 宽带路由优化技巧放送
  • AV杀手变种强行关闭杀度软件与下载恶意程序
  • Oracle加入Grails开源计划提升Java生产力
  • ORACLE考试 Oracle8i : 性能及其调优 大纲

相关软件:

  • FullJava 1.5
  • FTP远程文件同步更新程序 1.0.0.0
  • 网页特效小精灵(javatx) V2.0
  • Java Perk V6.07
  • 小颖JAVA源代码反编译超级引挚 V1.4 标准版
  • ExcelEverywhere for Java/JSP V3.4.1

 

快速导航

  • 网络学院
  • 精品汇聚
  • 字体下载
  • 教程下载
  • ASP源码
  • PHP源码
  • Net源码
  • JSP 源码

编程技术分类导航

  • ASP & ASP.NET教程
  • PHP教程
  • JSP教程
  • C/C++教程
  • VB & VB.NET教程
  • VC教程
  • Delphi教程
  • BCB教程
  • VFP教程
  • PB教程
  • JAVA教程
  • XML教程
  • C#教程
  • CGI教程

本类经典文章推荐

  • 深入理解AbstractClass和interface
  • JBUILDERX 初体验
  • 成为编程高手的八大奥秘
  • 为什么Java中继承是有害的一
  • 为什么Java中继承是有害的二
  • 初学Java要注意什么
  • JAVA开发中的中文处理问题
  • 汉字问题深入谈
  • 为什么会排队等待?
  • Java 程序中的多线程

JAVA教程阅读排行

  • Java连接数据库实例
  • 两种Java容器类List和Set分析
  • Java多线程编程经验谈
  • Java Web Start开发指南(一)
  • 如何将JAVA生成可执行文件?
  • 成为编程高手的八大奥秘
  • Java基础教程:打包详解
  • 初学Java要注意什么
  • Java Web Start 开发指南(三)
  • Java编出一个自己的JSP日历

编程技术阅读总排行

  • VB入门教程之一
  • Java连接数据库实例
  • 第二章 PowerBuilder 入门之创建新...
  • VC++之List Box/Check List Box控...
  • 第一章 什么是PowerBuilder
  • VC++ List Ctrl控件
  • VC++ Combo Box/Combo Box Ex控件
  • 学C++不得不看的一篇文章
  • VB入门教程之二
  • VC++之Button控件

广告位置

字母检索 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 回到顶部

关于我们 | 版权声明 | 免责条款 | 广告联系 | 软件发布 | 下载帮助 | 下载排行 | 网站地图 | 特别鸣谢 | 友情连接

copyright; 2005-2008 D9soft.com 第九软件网 版权所有