您的位置:首页 > 编程语言 > Java开发

浅谈Java程序优化

2013-04-10 17:59 197 查看

颜色说明

颜色

说明

*****橙色*****

重点优化对象,必须遵守。

*****黑色*****

能够提高性能,建议使用,但是不强制要求。

一、 避免在循环条件中使用复杂表达式

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

例子:

import java.util.Vector;

class CEL
{
void method(Vector vector)
{
for (int i = 0; i <
vector.size(); i++)
// Violation
; // ...
}
}

更正:

class CEL_fixed
{
void method (Vectorvector)
{
int size = vector.size();
for (int i = 0; i < size; i++) ;
// ...
}
}

二、 为'Vectors' 和 'Hashtables'定义初始大小

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

例子:

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块中关闭数据库连接、I/O流操作,以释放资源

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

例子:

import java.io.*;
public
class
CS
{
public
static void
main(String args[])

{
CS cs = new CS();
cs.method();
}

public
void
method()
{
FileInputStream fis = null;
try
{
fis = new FileInputStream("CS.java");
int count = 0;
while (fis.read() != -1)
{
count++;
}
System.out.println(count);
}
catch (FileNotFoundException e1)

{
}
catch (IOException e2)

{
}
finally
{
fis.close();
}
}
}

四、 使用'System.arraycopy ()'代替通过来循环复制数组

'System.arraycopy ()' 要比通过循环来复制数组快的多。

例子:

public
class
IRB
{
void method()
{
int[] array1 =
new int[100];
for (int i = 0; i < array1.length; i++)
{
array1 = i;
}
int[] array2 =
new int[100];
for (int i = 0; i < array2.length; i++)
{
array2 = array1; // Violation
}
}
}

更正:

public
class
IRB
{
void method()
{
int[] array1 =
new int[100];
for (int i = 0; i < array1.length; i++)
{
array1 = i;
}
int[] array2 =
new int[100];
System.arraycopy(array1, 0, array2, 0,100);
}
}

五、 让访问实例内变量的getter/setter方法变成”final”

简单的getter/setter方法应该被置成final,这会告诉编译器,这个方法不会被重载,所以,可以变成”inlined”

例子:

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。

例子:

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 aUISO");

UISO uiso = u;

if (uiso instanceof Object) // alwaystrue.

System.out.println("uiso is anObject");

}

}

更正:

删掉不需要的instanceof操作。

class Dog extends UISO {

void method () {

Dog d;

System.out.println ("Dog is anUISO");

System.out.println ("UISO is anUISO");

}

}

七、 避免不需要的造型操作

所有的类都是直接或者间接继承自Object。同样,所有的子类也都隐含的"等于"其父类。那么,由子类造型至父类的操作就是不必要的了。

例子:

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;
}
}

八、 如果只是查找单个字符的话,用charAt()代替startsWith()

用一个字符作为参数调用startsWith()也会工作的很好,但从性能角度上来看,调用用String API无疑是错误的!

例子:

public
class
PCTS
{
private
void
method(String s)
{
if (s.startsWith("a"))
{ // violation
// ...
}
}
}

更正

将'startsWith()' 替换成'charAt()'.

public
class
PCTS
{
private
void
method(String s)
{
if ('a' ==
s.charAt(0))
{
// ...
}
}
}

九、 使用移位操作来代替'a / b'操作

"/"是一个很"昂贵"的操作,使用移位操作将会更快更有效。

例子:

public
class
SDIV
{
public
static final
int
NUM = 16;

public
void
calculate(int a)
{
int div = a / 4;
// should be replaced with "a >> 2".
int div2 = a / 8;
// should be replaced with "a >> 3".
int temp = a / 3;
}
}

更正:

public
class
SDIV
{
public
static final
int
NUM = 16;

public
void
calculate(int a)
{
int div = a >> 2;
int div2 = a >> 3;
int temp = a / 3;
// 不能转换成位移操作
}
}

十、 使用移位操作代替'a * b'

同上。

但我个人认为,除非是在一个非常大的循环内,性能非常重要,而且你很清楚你自己在做什么,方可使用这种方法。否则提高性能所带来的程序晚读性的降低将是不合算的。

例子:

public
class
SMUL
{
public
void
calculate(int a)
{
int mul = a * 4;
// should be replaced with "a << 2".
int mul2 = 8 * a;
// should be replaced with "a << 3".
int temp = a * 3;
}
}

更正:

package OPT;

public
class
SMUL
{
public
void
calculate(int a)
{
int mul = a << 2;
int mul2 = a << 3;
int temp = a * 3;
// 不能转换
}
}

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

例子:

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(同步)方法

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

例子:

import java.util.Vector;

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%之多!

例子:

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)代码也会更加干净 。

例子:

public class UEQ

{

booleanmethod (String string)

{

return string.endsWith ("a")== true; // Violation

}

}

更正:

class UEQ_fixed

{

booleanmethod (String string)

{

return string.endsWith("a");

}

}

十五、 对于常量字符串,用'String'
代替 'StringBuffer'

常量字符串并不需要动态改变长度。

例子:

public class USC

{

Stringmethod ()

{

StringBuffer s = new StringBuffer("Hello");

String t = s + "World!";

return t;

}

}

更正:

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

十六、 用'StringTokenizer'
代替'indexOf()'
和'substring()'

字符串的分析在很多应用中都是常见的。使用indexOf()和substring()来分析字符串容易导致StringIndexOutOfBoundsException。而使用StringTokenizer类来分析字符串则会容易一些,效率也会高一些。

例子:

public
class
UST
{
void parseString(String string)
{
int index = 0;
while ((index = string.indexOf(".", index)) !=-1)
{
System.out.println(string.substring(index,string.length()));
}
}
}

十七、 不要在循环体中实例化变量

在循环体中实例化临时变量将会增加内存消耗

例子:

import java.util.Vector;

public
class
LOOP
{
void method(Vector v)
{
for (int i = 0; i < v.size(); i++)
{
Object o = new Object();
o = v.elementAt(i);
}
}
}

更正:

在循环体外定义变量,并反复使用

import java.util.Vector;

public
class
LOOP
{
void method(Vector v)
{
Object o;
for (int i = 0; i < v.size(); i++)
{
o = v.elementAt(i);
}
}
}

十八、 确定 StringBuffer的容量

StringBuffer的构造器会创建一个默认大小(通常是16)的字符数组。在使用中,如果超出这个大小,就会重新分配内存,创建一个更大的数组,并将原先的数组复制过来,再丢弃旧的数组。在大多数情况下,你可以在创建StringBuffer的时候指定大小,这样就避免了在容量不够的时候自动增长,以提高性能。

例子:

public
class
RSBC
{
void method()
{
StringBuffer buffer = new StringBuffer();
// violation
buffer.append("hello");
}
}

更正:

为StringBuffer提供大小。

public
class
RSBC
{
void method()
{
StringBuffer buffer = new StringBuffer(MAX);
buffer.append("hello");
}

private
final int

MAX = 100;
}

十九、 尽可能的使用栈变量

如果一个变量需要经常访问,那么你就需要考虑这个变量的作用域了。static? local?还是实例变量?访问静态变量和实例变量将会比访问局部变量多耗费2-3个时钟周期。

例子:

public
class
USV
{
void getSum(int[] values)
{
for (int i = 0; i <
value.length; i++)
{
_sum += value;
// violation.
}
}

void getSum2(int[] values)
{
for (int i = 0; i <
value.length; i++)
{
_staticSum +=
value;
}
}

private
int
_sum;
private
static int
_staticSum;
}

更正:

如果可能,请使用局部变量作为你经常访问的变量。

你可以按下面的方法来修改getSum()方法:

void getSum (int[] values)

{
int sum = _sum; // temporary local variable.
for (int i=0; i < value.length; i++)

{
sum += value;
}
_sum = sum;
}

二十、 不要总是使用取反操作符(!)

取反操作符(!)降低程序的可读性,所以不要总是使用。

例子:

public
class
test
{
boolean method(boolean a,
boolean b)
{
if (!a)
{
return !a;
}
else
{
return !b;
}
}
}

更正:

如果可能不要使用取反操作符(!)

二十一、 与一个接口进行instanceof操作

基于接口的设计通常是件好事,因为它允许有不同的实现,而又保持灵活。只要可能,对一个对象进行instanceof操作,以判断它是否某一接口要比是否某一个类要快。

例子:

public class INSOF {

private void method (Object o) {

if (o instanceof InterfaceBase) {} // better

if (o instanceof ClassBase) { } // worse.

}

}

class ClassBase {}

interface InterfaceBase {}

二十二、 异常对性能不利

抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本地(Native)方法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,VM就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。

异常只能用于错误处理,不应该用来控制程序流程。

二十三、 不用new关键词创建类的实例

用new关键词创建类的实例时,构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口,我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。

在使用设计模式(Design Pattern)的场合,如果用Factory模式创建对象,则改用clone()方法创建新的对象实例非常简单。例如,下面是Factory模式的一个典型实现:

public
static
Credit getNewCredit()
{
return
new
Credit();
}

改进后的代码使用clone()方法,如下所示:

private static Credit BaseCredit =new Credit();

public static Credit getNewCredit() {

return (Credit) BaseCredit.clone();

}

二十四、 尽量使用局部变量

调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快。其他变量,如静态变量、实例变量等,都在堆(Heap)中创建,速度较慢。

二十五、 避免对象创建和GC

只要有可能,应该避免创建对象,防止调用构造函数带来的相关性能成本,以及在对象结束其生命周期时进行垃圾收集所带来的成本。考虑以下这些准则:
· 只要有可能,就使用基本变量类型,而不使用对象类型。例如,使用 int,而不使用
Integer;
· 缓存那些频繁使用的寿命短的对象,避免一遍又一遍地重复创建相同的对象,并因此加重垃圾收集的负担;
· 在处理字符串时,使用 StringBuffer
而不使用字符串String进行连接操作,因为字符串对象具有不可变的特性,并且需要创建额外的字符串对象以完成相应的操作,而这些对象最终必须经历 GC;
· 避免过度地进行 Java
控制台的写操作,降低字符串对象处理、文本格式化和输出带来的成本;
· 实现数据库连接池,重用连接对象,而不是重复地打开和关闭连接;
· 使用线程池(thread pooling),避免不停地创建和删除线程对象,特别是在大量使用线程的时候;
· 避免在代码中调用GC。GC是一个“停止所有处理(stop
the world)”的事件,它意味着除了 GC
线程自身外,其他所有执行线程都将处于挂起状态。如果必须调用 GC,那么可以在非紧急阶段或空闲阶段实现它;
· 避免在循环内分配对象。
· 尽早释放无用对象的引用。大多数程序员在使用临时变量的时候,都是让引用变量在退出活动域(scope)后,自动设置为null。我们在使用这种方式时候,必须特别注意一些复杂的对象,例如数组,队列,树,图等,这些对象之间的相互引用关系较为复杂。对于这类对象,GC回收它们一般效率较低。如果程序允许,尽早将不再使用的引用对象赋为null。这样可以加速GC的工作。
· 如果有经常使用的图片,可以使用soft引用类型。它可以尽可能将图片保存在内存中,供程序调用,而不引起Out Of Memory。
· 注意一些全局的变量,以及一些静态变量。这些变量往往容易引起悬挂对象(dangling reference),造成内存浪费。
· 使用String
a ="a";定义字符串,而不是使用Stringa =
new String("a");

二十六、 避免非常大的分配

有时候问题不是由当时的堆状态造成的,而是因为分配失败造成的。分配的内存块都必须是连续的,而随着堆越来越满,找到较大的连续块越来越困难。这不仅仅是 Java
的问题,使用 C
中的 malloc 也会遇到这个问题。JVM
在压缩阶段通过重新分配引用来减少碎片,但其代价是要冻结应用程序较长的时间。

二十七、 SQL语句大写

在JAVA + ORACLE 的应用系统开发中,java中内嵌的SQL语句尽量使用大写的形式,以减轻ORACLE解析器的解析负担。

二十八、 尽量重用对象,特别是String
对象的使用中,出现字符串连接情况时应用StringBuffer代替

由于系统不仅要花时间生成对象,以后可能还需花时间对这些对象进行垃圾回收和处理。因此,生成过多的对象将会给程序的性能带来很大的影响;

例子:

StringBuffer sqlbuff = newStringBuffer();

sqlbuff.append("select to_char(decode(parent_item_id ,(select parent_item_id froms_finance_item_2009 where item_id=10),null,parent_item_id)) up_id, "

+

" to_char(item_id)id,item_name name ");

sqlbuff.append(" froms_finance_item_2009 start with item_id=10 connect by parent_item_id= prioritem_id ");

二十九、 由于JVM的有其自身的GC机制,不需要程序开发者的过多考虑,从一定程度上减轻了开发者负担,但同时也遗漏了隐患,过分的创建对象会消耗系统的大量内存,严重时会导致内存泄露;

JVM回收垃圾的条件是:对象不在被引用;然而,JVM的GC并非十分的机智,即使对象满足了垃圾回收的条件也不一定会被立即回收。所以,建议我们在对象使用完毕,应手动置成null;

示例代码:

Object[] rvo = new Object[2];
ArrayList tlist = new ArrayList();
tlist = BOQueryResult.getInstence().getQueryResult(sql,BOQueryResult.integrateSqlAgent);
if (tlist.size() == 1)
{
rvo = (Object[]) tlist.get(0);
//对象使用完毕,手工释放对象
tlist = null;
}
return rvo;

三十、 尽量使用HashMap
和ArrayList ,除非必要,否则不推荐使用HashTable和Vector
,后者由于使用同步机制,而导致了性能的开销;

三十一、 array(数组)和 ArryList的使用

array([]):最高效;但是其容量固定且无法动态改变;

ArrayList:容量可动态增长;但牺牲效率;

基于效率和类型检验,应尽可能使用array,无法确定数组大小时才使用ArrayList!

ArrayList是Array的复杂版本

ArrayList内部封装了一个Object类型的数组,从一般的意义来说,它和数组没有本质的差别,甚至于ArrayList的许多方法,如Index、IndexOf、Contains、Sort等都是在内部数组的基础上直接调用Array的对应方法。

ArrayList存入对象时,抛弃类型信息,所有对象屏蔽为Object,编译时不检查类型,但是运行时会报错。

注:jdk5中加入了对泛型的支持,已经可以在使用ArrayList时进行类型检查。

从这一点上看来,ArrayList与数组的区别主要就是由于动态增容的效率问题了;

三十二、 在进行金额计算时使用BigDecimal

对于帐务系统中金额处理的运行,一律采用BigDecimal。

转换元为分

BigDecimal money = new BigDecimal(100);
money = money.movePointLeft(2);

处理结果为:1.00

转换分为元

BigDecimal money = new BigDecimal(100);
money = money.movePointRight(2);

处理结果为:10000
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: