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

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

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

一 条件控制语句中的优化

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

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

forint i=0;i<list.size();i++)
可以改为
//我的电脑上,测试数量级在10^7,速度提升一倍。
forint 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,  0 100 );
}
}

  

 

五、让访问实例内变量的 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 ( 5 5 );
}
更正:
不要在循环体中调用同步方法,如果必须同步的话,推荐以下方式:
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 ( 5 5 );
}

  

 

十三、将 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()));
}
}
}

  


目录
相关文章
|
1天前
|
Java
Java程序的流程控制
程序执行顺序经典三类: 1. **顺序结构**: 代码从上至下依次执行。 2. **分支结构**: - `if/else`: 根据条件选择执行路径。 - `switch`: 通过匹配值选择分支,需注意`break`避免穿透现象。 3. **循环结构**: 控制代码重复执行,如`for`, `while`, `do...while`等。 (注:markdown格式不支持内嵌卡片或图片,故摘要中未包含原文中的图片信息。)
51 7
|
2天前
|
Java 程序员 调度
Java中的多线程编程:概念、实现及性能优化
【5月更文挑战第85天】本文主要探讨了Java中的多线程编程,包括其基本概念、实现方式以及如何进行性能优化。首先,我们将介绍多线程的基本概念,然后详细讨论如何在Java中实现多线程,包括继承Thread类和实现Runnable接口两种方式。最后,我们将探讨一些提高多线程程序性能的策略,如使用线程池和减少同步开销等。
|
16小时前
|
安全 Java 开发者
掌握Java并发编程:线程安全与性能优化之道
在多核处理器普及的今天,充分利用并发编程技术是提升应用性能的关键。本文将深入探讨Java中的并发编程,从基本概念到高级技巧,揭示如何通过正确的同步机制和锁策略来确保线程安全,同时避免常见的并发陷阱。我们将一起探索高效利用线程池、减少锁竞争、以及使用现代并发工具类等方法,以达到性能的最优化。
|
11天前
|
缓存 监控 Java
Java虚拟机(JVM)性能调优实战指南
在追求软件开发卓越的征途中,Java虚拟机(JVM)性能调优是一个不可或缺的环节。本文将通过具体的数据和案例,深入探讨JVM性能调优的理论基础与实践技巧,旨在为广大Java开发者提供一套系统化的性能优化方案。文章首先剖析了JVM内存管理机制的工作原理,然后通过对比分析不同垃圾收集器的适用场景及性能表现,为读者揭示了选择合适垃圾回收策略的数据支持。接下来,结合线程管理和JIT编译优化等高级话题,文章详细阐述了如何利用现代JVM提供的丰富工具进行问题诊断和性能监控。最后,通过实际案例分析,展示了性能调优过程中可能遇到的挑战及应对策略,确保读者能够将理论运用于实践,有效提升Java应用的性能。 【
54 10
|
6天前
|
JSON Java BI
一次Java性能调优实践【代码+JVM 性能提升70%】
这是我第一次对系统进行调优,涉及代码和JVM层面的调优。如果你能看到最后的话,或许会对你日常的开发有帮助,可以避免像我一样,犯一些低级别的错误。本次调优的代码是埋点系统中的报表分析功能,小公司,开发结束后,没有Code Review环节,所以下面某些问题,也许在Code Review环节就可以避免。
61 0
一次Java性能调优实践【代码+JVM 性能提升70%】
|
9天前
|
存储 缓存 JavaScript
|
9天前
|
存储 Java 开发者
探索Java内存管理:从垃圾收集到性能优化
本文深入探讨了Java的内存管理机制,重点分析了垃圾收集(GC)的工作原理及其对应用程序性能的影响。通过对比不同的垃圾收集器,并结合具体的性能优化案例,文章为Java开发者提供了一套实用的内存管理和优化策略。旨在帮助读者更好地理解如何通过调优JVM来提升应用的性能和稳定性。
|
13天前
|
安全 Java 开发者
Java并发编程中的线程安全性与性能优化
在Java编程中,处理并发问题是至关重要的。本文探讨了Java中线程安全性的概念及其在性能优化中的重要性。通过深入分析多线程环境下的共享资源访问问题,结合常见的并发控制手段和性能优化技巧,帮助开发者更好地理解和应对Java程序中的并发挑战。 【7月更文挑战第14天】
|
14天前
|
存储 监控 Java
揭秘Java虚拟机:探索JVM的工作原理与性能优化
本文深入探讨了Java虚拟机(JVM)的核心机制,从类加载到垃圾回收,再到即时编译技术,揭示了这些复杂过程如何共同作用于Java程序的性能表现。通过分析现代JVM的内存管理策略和性能监控工具,文章提供了实用的调优建议,帮助开发者有效提升Java应用的性能。
32 3
|
15天前
|
存储 安全 算法
深入理解Java并发编程:线程安全与性能优化
【5月更文挑战第72天】 在现代软件开发中,尤其是Java应用开发领域,并发编程是一个无法回避的重要话题。随着多核处理器的普及,合理利用并发机制对于提高软件性能、响应速度和资源利用率具有重要意义。本文旨在探讨Java并发编程的核心概念、线程安全的策略以及性能优化技巧,帮助开发者构建高效且可靠的并发应用。通过实例分析和理论阐述,我们将揭示在高并发环境下如何平衡线程安全与系统性能之间的关系,并提出一系列最佳实践方法。