Java开发中程序和代码性能优化

现在计算机的处理性能越来越好,加上JDK升级对一些代码的优化,在代码层针对一些细节进行调整可能看不到性能的明显提升,在开发中注意这些,更多的是可以保持一种性能优先的意识。

一 条件控制语句中的优化

1.在循环中应该避免使用复杂的表达式。

在循环中,循环条件会被反复计算,应该避免把一些计算放在循环进行的部分中,程序将会运行的更快。
比如:

for(int i=0;i<list.size();i++)
可以改为
//我的电脑上,测试数量级在10^7,速度提升一倍。
for(int i=0,len=list.size();i<len;i++)

 

二 更好的使用变量
1.合理使用final修饰符

注意fina修饰变量或者方法时的用法不同。很多时候使用"static final 变量"格式是很好的选择。
2.尽量避免不必要的创建,同时避免使用随意使用静态变量

GC通常是不会回收静态变量对象所占有的内存,所以要合理的使用静态区域代码。
3.使用基本数据类型代替对象
Java中,String对象主要由3部分组成:
char数组、偏移量和String的长度。

String str = "hello"; //创建一个“hello”字符串,而且JVM的字符缓存池还会缓存这个字符串;
String str = new String("hello")// 此时程序除创建字符串外,str所引用的String对象底层还包含一个char[]数组,这个char[]数组依次存放了h,e,l,l,o

二 更高效的使用字符串
1.对于常量字符串,用'string' 代替 'stringbuffer',但是在需要String对象做累加操作的场合,使用StringBuilder或者StringBuffer;

另外,在单线程或者无需考虑线程安全的情况下,使用性能相对较好的StringBuiler(单人盖房,多人缓冲)。

2.使用更好的办法分割字符串
《Java程序性能优化》一书中指出,split()方法支持正则表达式,功能强大,但是效率最差;StringTokenzer性能优于split()方法。如果可以自己实现分割算法,性能可以做到最优,但是考虑到可读性可维护性等,可以使用StringTokenizer。
在代码中验证一下:

StringBuilder sb=new StringBuilder();
for(int i=0;i<100000;i++){
sb.append(i);
sb.append(",");
}
String str=sb.toString();

Long time1=System.currentTimeMillis();
String[] split=str.split(",");
Long time2=System.currentTimeMillis();
System.out.println(time2-time1);

Long time3=System.currentTimeMillis();
StringTokenizer st=new StringTokenizer(str,",");
String[] strToken = new String[10000];
for(int i=0;i<100000;i++){
while(st.hasMoreTokens()){
strToken[i]=st.nextToken();
}
}
Long time4=System.currentTimeMillis();
System.out.println(time4-time2);

在自己的电脑上分割10000次时,StringTokenizer平均要快3ms左右(额,好像相差也不大)。

 

三 根据线程安全要求选用合理的数据结构
1.单线程应尽量使用HashMap、ArrayList
在单线程环境下,尽量避免使用一些针对多线程环境做了优化的集合工具。
比如,避免StringBuffer和StringBuilder,HashMap(单线程地图)和HashTable(多线程表格)等的随便使用。
2.减小synchronized作用的区域

同步方法的系统开销比较大,尽量在真正需要同步的地方使用synchronized关键字。

四 合理选用集合工具类
1.使用Vector、HashTable、HashMap等部分自动扩充的数据结构时,指定初始大小
查看Vector的源码,会发现定义了initialCapacity、capacityIncrement,用来指定初始容量和集合充满后的自动扩充大小,
Vector在初始化时,默认的容量大小是10,大部分时候这个容量是不够的,都会进行扩充操作。
比如:

 public vector v = new vector(20);  

2.尽量避免使用二维数组
相比一维数组,二维数组的效率比较低,相差可以达到10倍。以前做过的一些数据结构或者算法题目里面,

比如一维数组替代二维数组表示坐标系,因为考虑到时空开支,可以用下标和值分别表示纵横坐标
3.使用System.arraycopy()代替通过来循环复制数组

五 语句控制结构中的注意
1.java中使用final关键字来指示一个函数为内联函数,final关键字会告诉编译器,在编译的时候考虑性能的提升
内联函数就是在程序编译时,编译器将程序中出现 的内联函数的调用表达式用内联函数的函数体来直接进行替换。理解内联函数,可以类比C语言的宏定义。

这篇博文对使用final优化做了测试。http://blog.csdn.net/inkfish/article/details/4849028

2.在文件读写,访问链接等操作中,相关的资源可以在finally块中释放

六 一些提升性能的数学计算
1.和C语言一样,乘除法如果可以使用位移,应尽量使用位移

通常如果需要乘以或除以2的n次方,都可以用移位的方法代替,

在Java中是左移、有符号右移和无符号右移运算符。位移运算符只对int值进行操作,如果不是int,编译器会报错。

 

a=a*4;
b=b/4;
//可以改为:
a=a<<2;
b=b>>2;
//说明:
//除2 = 右移1位 乘2 = 左移1位
//除4 = 右移2位 乘4 = 左移2位
//除8 = 右移3位 乘8 = 左移3位 

 

—————分割线—————————————————

 

一、避免在循环条件中使用复杂表达式
在不做编译优化的情况下,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循
环条件值不变的话,程序将会运行的更快。

 


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

例子:

import java.util.vector;

class cel {

void method (vector vector) {

for (int i = 0; i < vector.size (); i++) // violation

// ...

}

}

更正:

class cel_fixed {

void method (vector vector) {

int size = vector.size ()

for (int i = 0; i < size; i++)

// ...

}

}

  

 

二、为'vectors' 和 'hashtables'定义初始大小
jvm 为 vector 扩充大小的时候需要重新创建一个更大的数组,将原原先数组中的内容复制过
来,最后,原先的数组再被回收。可见 vector 容量的扩大是一个颇费时间的事。 
通常,默认的 10 个元素大小是不够的。你最好能准确的估计你所需要的最佳大小。

 


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

例子:

import java.util.vector;

public class dic {

public void addobjects (object[] o) {

// if length > 10, vector needs to expand

for (int i = 0; i< o.length;i++) {

v.add(o); // capacity before it can add more elements.

}

}

public vector v = new vector(); // no initialcapacity.

}

更正:

自己设定初始大小。

public vector v = new vector(20);

public hashtable hash = new hashtable(10);

  

 

三、在 finally 块中关闭 stream
程序中使用到的资源应当被释放,以避免资源泄漏。这最好在 finally 块中去做。不管程序执行
的结果如何,finally 块总是会执行的,以确保资源的正确关闭。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

例子:

import java.io.*;

public class cs {

public static void main (string args[]) {

cs cs = new cs ();

cs.method ();

}

public void method () {

try {

fileinputstream fis = new fileinputstream ("cs.java");

int count = 0;

while (fis.read () != -1)

count++;

system.out.println (count);

fis.close ();

catch (filenotfoundexception e1) {

catch (ioexception e2) {

}

}

}

  

更正: 
在最后一个 catch 后添加一个 finally 块

 

四、使用'system.arraycopy ()'代替通过来循环复制数组
'system.arraycopy ()' 要比通过循环来复制数组快的多。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

例子:

public class irb

{

void method () {

int[] array1 = new int [100];

for (int i = 0; i < array1.length; i++) {

array1 [i] = i;

}

int[] array2 = new int [100];

for (int i = 0; i < array2.length; i++) {

array2 [i] = array1 [i]; // violation

}

}

}

更正:

public class irb

{

void method () {

int[] array1 = new int [100];

for (int i = 0; i < array1.length; i++) {

array1 [i] = i;

}

int[] array2 = new int [100];

system.arraycopy(array1, 0, array2, 0100);

}

}

  

 

五、让访问实例内变量的 getter/setter 方法变成”final”
简单的 getter/setter 方法应该被置成 final,这会告诉编译器,这个方法不会被重载,所以,可
以变成”inlined”


1

2

3

4

5

6

7

8

9

10

11

12

13

14

例子:

class maf {

public void setsize (int size) {

_size = size;

}

private int _size;

}

更正:

class daf_fixed {

final public void setsize (int size) {

_size = size;

}

private int _size;

}

  

 

六、避免不需要的 instanceof 操作
如果左边的对象的静态类型等于右边的,instanceof 表达式返回永远为 true。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

例子:

public class uiso {

public uiso () {}

}

class dog extends uiso {

void method (dog dog, uiso u) {

dog d = dog;

if (d instanceof uiso) // always true.

system.out.println("dog is a uiso");

uiso uiso = u;

if (uiso instanceof object) // always true.

system.out.println("uiso is an object");

}

}

更正:

删掉不需要的 instanceof 操作。

class dog extends uiso {

void method () {

dog d;

system.out.println ("dog is an uiso");

system.out.println ("uiso is an uiso");

}

}

  

 

七、避免不需要的造型操作
所有的类都是直接或者间接继承自 object。同样,所有的子类也都隐含的“等于”其父类。那么,
由子类造型至父类的操作就是不必要的了。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

例子:

class unc {

string _id = "unc";

}

class dog extends unc {

void method () {

dog dog = new dog ();

unc animal = (unc)dog; // not necessary.

object o = (object)dog; // not necessary.

}

}

更正:

class dog extends unc {

void method () {

dog dog = new dog();

unc animal = dog;

object o = dog;

}

}

  

 

十一、在字符串相加的时候,使用 ' ' 代替 " ",如果该字符串只


1

2

3

4

5

6

7

8

9

10

11

12

13

14

有一个字符的话

public class str {

public void method(string s) {

string string = s + "d" // violation.

string = "abc" "d" // violation.

}

}

将一个字符的字符串替换成' '

public class str {

public void method(string s) {

string string = s + 'd'

string = "abc" 'd'

}

}

  

 

十二、不要在循环中调用 synchronized(同步)方法
方法的同步需要消耗相当大的资料,在一个循环中调用它绝对不是一个好主意。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

public class syn {

public synchronized void method (object o) {

}

private void test () {

for (int i = 0; i < vector.size(); i++) {

method (vector.elementat(i)); // violation

}

}

private vector vector = new vector (55);

}

更正:

不要在循环体中调用同步方法,如果必须同步的话,推荐以下方式:

import java.util.vector;

public class syn {

public void method (object o) {

}

private void test () {

synchronized{//在一个同步块中执行非同步方法

for (int i = 0; i < vector.size(); i++) {

method (vector.elementat(i));

}

}

}

private vector vector = new vector (55);

}

  

 

十三、将 try/catch 块移出循环
把 try/catch 块放入循环体内,会极大的影响性能,如果编译 jit 被关闭或者你所使用的是一个
不带 jit 的 jvm,性能会将下降 21%之多!

 


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

import java.io.fileinputstream;

public class try {

void method (fileinputstream fis) {

for (int i = 0; i < size; i++) {

try // violation

_sum += fis.read();

catch (exception e) {}

}

}

private int _sum;

}

更正:

将 try/catch 块移出循环

void method (fileinputstream fis) {

try {

for (int i = 0; i < size; i++) {

_sum += fis.read();

}

catch (exception e) {}

}

  

 

十四、对于 boolean 值,避免不必要的等式判断
将一个 boolean 值与一个 true 比较是一个恒等操作(直接返回该 boolean 变量的值). 移走对于
boolean 的不必要操作至少会带来 2 个好处: 
1)代码执行的更快 (生成的字节码少了 5 个字节); 
2)代码也会更加干净 。

 

十五、对于常量字符串,用'string' 代替 'stringbuffer'
常量字符串并不需要动态改变长度。

 


1

2

3

4

5

6

7

8

9

10

例子:

public class usc {

string method () {

stringbuffer s = new stringbuffer ("hello");

string t = s + "world!";

return t;

}

}

更正:

把 stringbuffer 换成 string,如果确定这个 string 不会再变的话,这将会减少运行开销提高性

  

 

十六、用'stringtokenizer' 代替 'indexof()' 和'substring()' 
字符串的分析在很多应用中都是常见的。使用 indexof()和 substring()来分析字符串容易导致
stringindexoutofboundsexception。而使用 stringtokenizer 类来分析字符串则会容易一
些,效率也会高一些。

 


1

2

3

4

5

6

7

public class ust {void parsestring(string string) {

int index = 0;

while ((index = string.indexof(".", index)) != -1) {

system.out.println (string.substring(index, string.length()));

}

}

}

  

时间: 2024-11-01 00:08:18

Java开发中程序和代码性能优化的相关文章

完整java开发中JDBC连接数据库代码和步骤

JDBC连接数据库 •创建一个以JDBC连接数据库的程序,包含7个步骤: 1.加载JDBC驱动程序: 在连接数据库之前,首先要加载想要连接的数据库的驱动到JVM(Java虚拟机), 这通过java.lang.Class类的静态方法forName(String className)实现. 例如: try{ //加载MySql的驱动类 Class.forName("com.mysql.jdbc.Driver") ; }catch(ClassNotFoundException e){ Sys

完整java开发中JDBC连接数据库代码和步骤_java

JDBC连接数据库 •创建一个以JDBC连接数据库的程序,包含7个步骤: 1.加载JDBC驱动程序: 在连接数据库之前,首先要加载想要连接的数据库的驱动到JVM(Java虚拟机), 这通过java.lang.Class类的静态方法forName(String className)实现. 例如: try{ //加载MySql的驱动类 Class.forName("com.mysql.jdbc.Driver") ; }catch(ClassNotFoundException e){ Sys

java开发中通用分页类代码

java开发中通用分页类代码 在java中要分页我们必须要有数据库教程,所以我们先准备下数据库,其数据库脚步如下: --以下是创建数据库和数据库表以及向数据库插入数据   use master  Go  if exists(select * from sysdatabases where name='pagination')  drop database pagination  Go  create database pagination  Go  use pagination  Go  cre

Java开发中的23种设计模式详解(转)

Java开发中的23种设计模式详解(转) 设计模式(Design Patterns)                                   --可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样.项目中合

jQuery代码性能优化的10种方法_jquery

1.总是使用#id去寻找element. 在jQuery中最快的选择器是ID选择器 ($('#someid')). 这是因为它直接映射为JavaScript的getElementById()方法. 选择单个元素 <div id="content"> <form method="post" action="/"> <h2>Traffic Light</h2> <ul id="traff

iOS开发:对MKMapView的性能优化

  最近做的项目主要是LBS这块 主打成员定位功能 我们的UI设计是这样的 开发:对MKMapView的性能优化-"> 乍一看上去是挺好挺美观的 不同的人会显示不同的头像 可是当人扎堆的时候 问题就来了 当人多的时候(例如上图所示) 地图滑动起来就能感觉到明显顿卡 那种不流畅感能折磨死人 所以 自然我们要解决这个问题(等等 先不要吐槽为什么不用地图聚合 因为这已经是地图放到最大了 聚合不适合这次的问题讨论) 分析 首先看下我是怎么实现这个annotationView的 由于这个annota

Java开发中使用JVMTI循环类实例教程

今天我想探讨Java的另一面,我们平时不会注意到或者不会使用到的一面.更准确的说是关于底层绑定.本地代码(native code)以及如何实现一些小魔法.虽然我们不会在JVM层面上探究这是怎么实现的,但我们会通过这篇文章展示一些奇迹. 我在ZeroTurnaround的RebelLabs团队中主要工作是做研究.撰文.编程.这个公司主要开发面向Java开发者的工具,大部分以Java插件(javaagent)的方式运行.经常会遇到这种情况,如果你想在不重写JVM的前提下增强JVM或者提高它的性能,你

Java开发中异常处理的最佳实践

异常处理是Java 开发中的一个重要部分.它是关乎每个应用的一个非功能性需求,是为了处理任何错误状况,比如资源不可访问,非法输入,空输入等等.Java提供了几个异常处理特性,以try,catch 和 finally 关键字的形式内建于语言自身之中.Java 编程语言也允许你创建新的异常,并通过使用 throw 和 throws关键字抛出它们.事实上,在Java编程中,Java的异常处理不单单是知道语法这么简单,它必须遵循标准的JDK库,和几个处理错误和异常的开源代码.这里我们将讨论一些关于异常处

java开发中基于JDBC连接数据库实例总结_java

本文实例讲述了java开发中基于JDBC连接数据库的方法.分享给大家供大家参考,具体如下: 创建一个以JDBC连接数据库的程序,包含7个步骤:   1.加载JDBC驱动程序: 在连接数据库之前,首先要加载想要连接的数据库的驱动到JVM(Java虚拟机),这通过java.lang.Class类的静态方法forName(String  className)实现. 例如: try{ //加载MySql的驱动类 Class.forName("com.mysql.jdbc.Driver") ;