浅谈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
相关文章推荐
- Java 程序优化:字符串操作、基本运算方法等优化策略(一)
- android之java程序性能优化(不断补充)
- JAVA程序优化
- Java程序性能优化
- 如何优化JAVA程序开发,提高JAVA性能
- JAVA程序性能优化
- Java程序性能优化
- 提高性能---java中优化程序应尽量要做到的
- 3.Java程序优化-NIO
- java程序性能优化
- Java 程序性能优化《第一章》Java性能调优概述 1.4小结
- Java程序性能优化 让你的Java程序更快、更稳定
- java 程序性能优化《第二章》设计优化 2.1善用设计模式 1 单例模式
- Java程序性能优化Tip
- java程序性能优化
- Java程序性能优化之找出内存溢出元凶
- java程序代码的优化
- Android性能优化第三篇--java程序优化
- Java程序优化的一些最佳实践
- Java程序性能优化 !