Java性能優化

一、避免在循環條件中使用復雜表達式

在不做編譯優化的情況下,在循環中,循環條件會被反復計算,如果不使用復雜表達式,而使循環條件值不變的話,程序將會運行的更快。

例子:
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個元素大小是不夠的。你最好能準確的估計你所需要的最佳大小。

例子:
import java.util.vector;
public class dic {public void addobjects (object[] o) {// if length > 10, vector needs to expandfor (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);


參考資料:
dov bulka, "java performance and scalability volume 1: server-side programming
techniques" addison wesley, isbn: 0-201-70429-3 pp.55 – 57

三、在finally塊中關閉stream

程序中使用到的資源應當被釋放,以避免資源泄漏。這最好在finally塊中去做。不管程序執行的結果如何,finally塊總是會執行的,以確保資源的正確關閉。
????????
例子:
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塊

參考資料:
peter haggar: "practical java - programming language guide".
addison wesley, 2000, pp.77-79
四、使用'system.arraycopy ()'代替通過來循環復制數組

'system.arraycopy ()' 要比通過循環來復制數組快的多。
????????
例子:
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);}
}

????????
參考資料:
http://www.cs.cmu.edu/~jch/java/speed.html

五、讓訪問實例內變量的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;
}


參考資料:
warren n. and bishop p. (1999), "java in practice", p. 4-5
addison-wesley, isbn 0-201-36065-9

六、避免不需要的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 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。同樣,所有的子類也都隱含的“等于”其父類。那么,由子類造型至父類的操作就是不必要的了。
例子:
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;}
}
?????
參考資料:
nigel warren, philip bishop: "java in practice - design styles and idioms
for effective java".? addison-wesley, 1999. pp.22-23
八、如果只是查找單個字符的話,用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)) {// ...}}
}

????????
參考資料:
dov bulka, "java performance and scalability volume 1: server-side programming
techniques"? addison wesley, isbn: 0-201-70429-3
九、使用移位操作來代替'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'

同上。
[i]但我個人認為,除非是在一個非常大的循環內,性能非常重要,而且你很清楚你自己在做什么,方可使用這種方法。否則提高性能所帶來的程序晚讀性的降低將是不合算的。

例子:
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) {}}

????????
參考資料:
peter haggar: "practical java - programming language guide".
addison wesley, 2000, pp.81 – 83

十四、對于boolean值,避免不必要的等式判斷

將一個boolean值與一個true比較是一個恒等操作(直接返回該boolean變量的值). 移走對于boolean的不必要操作至少會帶來2個好處:
1)代碼執行的更快 (生成的字節碼少了5個字節);
2)代碼也會更加干凈 。

例子:
public class ueq
{boolean method (string string) {return string.endswith ("a") == true;   // violation}
}


更正:
class ueq_fixed
{boolean method (string string) {return string.endswith ("a");}
}


十五、對于常量字符串,用'string' 代替 'stringbuffer'


常量字符串并不需要動態改變長度。
例子:
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類來分析字符串則會容易一些,效率也會高一些。

例子:
public class ust {void parsestring(string string) {int index = 0;while ((index = string.indexof(".", index)) != -1) {system.out.println (string.substring(index, string.length()));}}
}

參考資料:
graig larman, rhett guthrie: "java 2 performance and idiom guide"
prentice hall ptr, isbn: 0-13-014260-3 pp. 282 – 283

十七、使用條件操作符替代"if (cond) return; else return;" 結構

條件操作符更加的簡捷
例子:
public class if {public int method(boolean isdone) {if (isdone) {return 0;} else {return 10;}}
}


更正:
public class if {public int method(boolean isdone) {return (isdone ? 0 : 10);}
}

十八、使用條件操作符代替"if (cond) a = b; else a = c;" 結構

例子:
public class ifas {void method(boolean istrue) {if (istrue) {_value = 0;} else {_value = 1;}}private int _value = 0;
}


更正:
public class ifas {void method(boolean istrue) {_value = (istrue ? 0 : 1);       // compact expression.}private int _value = 0;
}

十九、不要在循環體中實例化變量

在循環體中實例化臨時變量將會增加內存消耗

例子:????????
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(); // violationbuffer.append ("hello");}
}

????????
更正:????????
為stringbuffer提供寢大小。????????
public class rsbc {void method () {stringbuffer buffer = new stringbuffer(max);buffer.append ("hello");}private final int max = 100;
}

????????
參考資料:
dov bulka, "java performance and scalability volume 1: server-side programming
techniques" addison wesley, isbn: 0-201-70429-3 p.30 – 31

二十一、盡可能的使用棧變量

如果一個變量需要經常訪問,那么你就需要考慮這個變量的作用域了。static? local?還是實例變量?訪問靜態變量和實例變量將會比訪問局部變量多耗費2-3個時鐘周期。
????????
例子:
public class usv {void getsum (int[] values) {for (int i=0; i < value.length; i++) {_sum += value[i];           // violation.}}void getsum2 (int[] values) {for (int i=0; i < value.length; i++) {_staticsum += value[i];}}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[i];}_sum = sum;
}

????????
參考資料:????????
peter haggar: "practical java - programming language guide".
addison wesley, 2000, pp.122 – 125

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

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

例子:
public class dun {boolean method (boolean a, boolean b) {if (!a)return !a;elsereturn !b;}
}


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

二十三、與一個接口 進行instanceof操作


基于接口的設計通常是件好事,因為它允許有不同的實現,而又保持靈活。只要可能,對一個對象進行instanceof操作,以判斷它是否某一接口要比是否某一個類要快。

例子:
public class insof {private void method (object o) {if (o instanceof interfacebase) { }  // betterif (o instanceof classbase) { }   // worse.}
}class classbase {}
interface interfacebase {}


參考資料:
graig larman, rhett guthrie: "java 2 performance and idiom guide"
prentice hall ptr, 2000.? pp.207
  • 17:26
  • 瀏覽 (26)
  • 評論 (0)
  • 分類: java
2010-05-12
縮略顯示

Java性能優化技巧

文章分類:Java編程
轉載:http://blog.csdn.net/kome2000/archive/2010/04/28/5537591.aspx


[size=small]在JAVA程序中,性能問題的大部分原因并不在于JAVA語言,而是程序本身。養成良好的編碼習慣非常重要,能夠顯著地提升程序性能。

1. 盡量使用final修飾符。
帶有final修飾符的類是不可派生的。在JAVA核心API中,有許多應用final的例子,例如 java.lang.String。為String類指定final防止了使用者覆蓋length()方法。另外,如果一個類是final的,則該類所有方法都是final的。java編譯器會尋找機會內聯(inline)所有的final方法(這和具體的編譯器實現有關)。此舉能夠使性能平均提高 50%。

2.盡量重用對象。
特別是String對象的使用中,出現字符串連接情況時應使用StringBuffer代替,由于系統不僅要花時間生成對象,以后可能還需要花時間對這些對象進行垃圾回收和處理。因此生成過多的對象將會給程序的性能帶來很大的影響。

3. 盡量使用局部變量。
調用方法時傳遞的參數以及在調用中創建的臨時變量都保存在棧(Stack)中,速度較快。其他變量,如靜態變量,實例變量等,都在堆(Heap)中創建,速度較慢。

4.不要重復初始化變量。
默認情況下,調用類的構造函數時,java會把變量初始化成確定的值,所有的對象被設置成null,整數變量設置成0,float和double變量設置成0.0,邏輯值設置成false。當一個類從另一個類派生時,這一點尤其應該注意,因為用new關鍵字創建一個對象時,構造函數鏈中的所有構造函數都會被自動調用。
這里有個注意,給成員變量設置初始值但需要調用其他方法的時候,最好放在一個方法比如initXXX()中,因為直接調用某方法賦值可能會因為類尚未初始化而拋空指針異常,public int state = this.getState();

5.在java+Oracle的應用系統開發中,java中內嵌的SQL語言應盡量使用大寫形式,以減少Oracle解析器的解析負擔。

6.java編程過程中,進行數據庫連接,I/O流操作,在使用完畢后,及時關閉以釋放資源。因為對這些大對象的操作會造成系統大的開銷。

7.
過分的創建對象會消耗系統的大量內存,嚴重時,會導致內存泄漏,因此,保證過期的對象的及時回收具有重要意義。
JVM的GC并非十分智能,因此建議在對象使用完畢后,手動設置成null。

8.在使用同步機制時,應盡量使用方法同步代替代碼塊同步。

9.盡量減少對變量的重復計算。

比如
for(int i=0;i<list.size();i++) 

應修改為
for(int i=0,len=list.size();i<len;i++)


10. 采用在需要的時候才開始創建的策略。

例如:
String str="abc";
if(i==1){ list.add(str);}

應修改為:
if(i==1){String str="abc"; list.add(str);}


11.慎用異常,異常對性能不利。

拋出異常首先要創建一個新的對象。Throwable接口的構造函數調用名為fillInStackTrace()的本地方法,fillInStackTrace()方法檢查棧,收集調用跟蹤信息。只要有異常被拋出,VM就必須調整調用棧,因為在處理過程中創建了一個新的對象。
異常只能用于錯誤處理,不應該用來控制程序流程。

12.不要在循環中使用Try/Catch語句,應把Try/Catch放在循環最外層。

Error是獲取系統錯誤的類,或者說是虛擬機錯誤的類。不是所有的錯誤Exception都能獲取到的,虛擬機報錯 Exception就獲取不到,必須用Error獲取。

13.通過StringBuffer的構造函數來設定他的初始化容量,可以明顯提升性能。
StringBuffer的默認容量為16,當StringBuffer的容量達到最大容量時,她會將自身容量增加到當前的2倍+2,也就是2*n+2。無論何時,只要StringBuffer到達她的最大容量,她就不得不創建一個新的對象數組,然后復制舊的對象數組,這會浪費很多時間。所以給StringBuffer設置一個合理的初始化容量值,是很有必要的!

14.合理使用java.util.Vector。

Vector 與StringBuffer類似,每次擴展容量時,所有現有元素都要賦值到新的存儲空間中。Vector的默認存儲能力為10個元素,擴容加倍。
vector.add(index,obj) 這個方法可以將元素obj插入到index位置,但index以及之后的元素依次都要向下移動一個位置(將其索引加 1)。 除非必要,否則對性能不利。
同樣規則適用于remove(int index)方法,移除此向量中指定位置的元素。將所有后續元素左移(將其索引減 1)。返回此向量中移除的元素。所以刪除vector最后一個元素要比刪除第1個元素開銷低很多。刪除所有元素最好用 removeAllElements()方法。
如果要刪除vector里的一個元素可以使用 vector.remove(obj);而不必自己檢索元素位置,再刪除,如int index = indexOf(obj);vector.remove(index);

15.當復制大量數據時,使用 System.arraycopy();


16.代碼重構,增加代碼的可讀性。

17.不用new關鍵字創建對象的實例。
用 new關鍵詞創建類的實例時,構造函數鏈中的所有構造函數都會被自動調用。但如果一個對象實現了Cloneable接口,我們可以調用她的clone() 方法。clone()方法不會調用任何類構造函數。
下面是Factory模式的一個典型實現。
public static Credit getNewCredit()
{return new Credit();
}

改進后的代碼使用clone() 方法,
private static Credit BaseCredit = new Credit();
public static Credit getNewCredit()
{return (Credit)BaseCredit.clone();
}

18. 乘除法如果可以使用位移,應盡量使用位移,但最好加上注釋,因為位移操作不直觀,難于理解。

19.不要將數組聲明為:public static final。


20.HaspMap的遍歷。

Map<String, String[]> paraMap = new HashMap<String, String[]>();
for( Entry<String, String[]> entry : paraMap.entrySet() )
{String appFieldDefId = entry.getKey();String[] values = entry.getValue();
}

利用散列值取出相應的Entry做比較得到結果,取得entry的值之后直接取key和 value。

21.array(數組)和ArrayList的使用。
array 數組效率最高,但容量固定,無法動態改變,ArrayList容量可以動態增長,但犧牲了效率。

22.單線程應盡量使用 HashMap, ArrayList,
除非必要,否則不推薦使用HashTable,Vector,她們使用了同步機制,而降低了性能。

23
.StringBuffer,StringBuilder 的區別在于:java.lang.StringBuffer 線程安全的可變字符序列。一個類似于String的字符串緩沖區,但不能修改。StringBuilder與該類相比,通常應該優先使用 StringBuilder類,因為她支持所有相同的操作,但由于她不執行同步,所以速度更快。為了獲得更好的性能,在構造StringBuffer或 StringBuilder時應盡量指定她的容量。當然如果不超過16個字符時就不用了。
相同情況下,使用StringBuilder比使用 StringBuffer僅能獲得10%~15%的性能提升,但卻要冒多線程不安全的風險。綜合考慮還是建議使用StringBuffer。

24. 盡量使用基本數據類型代替對象。

25.用簡單的數值計算代替復雜的函數計算,比如查表方式解決三角函數問題。


26.使用具體類比使用接口效率高,但結構彈性降低了,但現代IDE都可以解決這個問題。

27.考慮使用靜態方法,

如果你沒有必要去訪問對象的外部,那么就使你的方法成為靜態方法。她會被更快地調用,因為她不需要一個虛擬函數導向表。這同事也是一個很好的實踐,因為她告訴你如何區分方法的性質,調用這個方法不會改變對象的狀態。

28.應盡可能避免使用內在的GET,SET方法。
android編程中,虛方法的調用會產生很多代價,比實例屬性查詢的代價還要多。我們應該在外包調用的時候才使用get,set方法,但在內部調用的時候,應該直接調用。

29. 避免枚舉,浮點數的使用。

30.二維數組比一維數組占用更多的內存空間,大概是10倍計算。

31.SQLite數據庫讀取整張表的全部數據很快,但有條件的查詢就要耗時30-50MS,大家做這方面的時候要注意,盡量少用,尤其是嵌套查找! [/size][align=left][/align]
  • 16:39
  • 瀏覽 (27)
  • 評論 (0)
  • 分類: java
2010-05-12
縮略顯示

《java解惑》轉

文章分類:Java編程
轉載于:http://jiangzhengjun.javaeye.com/blog/652623
數值表達式
1. 奇偶判斷

不要使用 i % 2 == 1 來判斷是否是奇數,因為i為負奇數時不成立,請使用 i % 2 != 0 來判斷是否是奇數,或使用

高效式 (i & 1) != 0來判斷。


2. 小數精確計算


System.out.println(2.00 -1.10);//0.8999999999999999



上面的計算出的結果不是 0.9,而是一連串的小數。問題在于1.1這個數字不能被精確表示為一個double,因此它被表

示為最接近它的double值,該程序從2中減去的就是這個值,但這個計算的結果并不是最接近0.9的double值。


一般地說,問題在于并不是所有的小數都可以用二進制浮點數精確表示。


二進制浮點對于貨幣計算是非常不適合的,因為它不可能將1.0表示成10的其他任何負次冪。


解決問題的第一種方式是使用貨幣的最小單位(分)來表示:

System.out.println(200-110);//90



第二種方式是使用BigDecimal,但一定要用BigDecimal(String)構造器,而千萬不要用 BigDecimal(double)來構造(也不能將float或double型轉換成String再來使用BigDecimal(String)來構造,因為在將float或double轉換成String時精度已丟失)。
例如new BigDecimal(0.1),
它將返回一個BigDecimal,
也即0.1000000000000000055511151231257827021181583404541015625,
正確使用BigDecimal,程序就可以打印出我們所期

望的結果0.9:

System.out.println(new BigDecimal("2.0").subtract(new BigDecimal("1.10")));// 0.9


另外,如果要比較兩個浮點數的大小,要使用BigDecimal的compareTo方法。

3. int整數相乘溢出

我們計算一天中的微秒數:

long microsPerDay = 24 * 60 * 60 * 1000 * 1000;// 正確結果應為:86400000000
System.out.println(microsPerDay);// 實際上為:500654080


? 問題在于計算過程中溢出了。這個計算式完全是以int運算來執行的,并且只有在運算完成之后,其結果才被提升為long,而此時已經太遲:計算已經溢出。
? 解決方法使計算表達式的第一個因子明確為long型,這樣可以強制表達式中所有的后續計算都用long運算來完成,這樣結果就不會溢出:

long microsPerDay = 24L * 60 * 60 * 1000 * 1000;



4. 負的十六進制與八進制字面常量


“數字字面常量”的類型都是int型,而不管他們是幾進制,所以“2147483648”、“0x180000000(十六進制,共33位,所以超過了整數的取值范圍)”字面常量是錯誤的,編譯時會報超過int的取值范圍了,所以要確定以long來表示“2147483648L”“0x180000000L”。


十進制字面常量只有一個特性,即所有的十進制字面常量都是正數,如果想寫一個負的十進制,則需要在正的十進制

字面常量前加上“-”即可。


十六進制或八進制字面常量可就不一定是正數或負數,是正還是負,則要根據當前情況看:如果十六進制和八進制字

面常量的最高位被設置成了1,那么它們就是負數:

System.out.println(0x80);//128 
//0x81看作是int型,最高位(第32位)為0,所以是正數
System.out.println(0x81);//129 
System.out.println(0x8001);//32769
System.out.println(0x70000001);//1879048193 
//字面量0x80000001為int型,最高位(第32位)為1,所以是負數
System.out.println(0x80000001);//-2147483647
//字面量0x80000001L強制轉為long型,最高位(第64位)為0,所以是正數
System.out.println(0x80000001L);//2147483649
//最小int型
System.out.println(0x80000000);//-2147483648
//只要超過32位,就需要在字面常量后加L強轉long,否則編譯時出錯
System.out.println(0x8000000000000000L);//-9223372036854775808


從上面可以看出,十六進制的字面常量表示的是int型,如果超過32位,則需要在后面加“L”,否則編譯過不過。如果為32,則為負int正數,超過32位,則為long型,但需明確指定為long。


System.out.println(Long.toHexString(0x100000000L + 0xcafebabe));// cafebabe


結果為什么不是0x1cafebabe?該程序執行的加法是一個混合類型的計算:左操作數是long型,而右操作數是int類型。為了執行該計算,Java將int類型的數值用拓寬原生類型轉換提升為long類型,然后對兩個long類型數值相加。因為int是有符號的整數類型,所以這個轉換執行的是符號擴展。
? 這個加法的右操作數0xcafebabe為32位,將被提升為long類型的數值0xffffffffcafebabeL,之后這個數值加上了左操

作0x100000000L。當視為int類型時,經過符號擴展之后的右操作數的高32位是-1,而左操作數的第32位是1,兩個數

值相加得到了0:
? 0x 0xffffffffcafebabeL
+0x 0000000100000000L
-----------------------------
0x 00000000cafebabeL

如果要得到正確的結果0x1cafebabe,則需在第二個操作數組后加上“L”明確看作是正的long型即可,此時相加時拓

展符號位就為0:
System.out.println(Long.toHexString(0x100000000L + 0xcafebabeL));// 1cafebabe


5. 窄數字類型提升至寬類型時使用符號位擴展還是零擴展


System.out.println((int)(char)(byte)-1);// 65535


結果為什么是65535而不是-1?


窄的整型轉換成較寬的整型時符號擴展規則:如果最初的數值類型是有符號的,那么就執行符號擴展(即如果符號位

為1,則擴展為1,如果為零,則擴展為0);如果它是char,那么不管它將要被提升成什么類型,都執行零擴展。


了解上面的規則后,我們再來看看迷題:因為byte是有符號的類型,所以在將byte數值-1(二進制為:11111111)提

升到char時,會發生符號位擴展,又符號位為1,所以就補8個1,最后為16個1;然后從char到int的提升時,由于是

char型提升到其他類型,所以采用零擴展而不是符號擴展,結果int數值就成了65535。


如果將一個char數值c轉型為一個寬度更寬的類型時,只是以零來擴展,但如果清晰表達以零擴展的意圖,則可以考慮

使用一個位掩碼:

int i = c & 0xffff;//實質上等同于:int i = c ;



如果將一個char數值c轉型為一個寬度更寬的整型,并且希望有符號擴展,那么就先將char轉型為一個short,它與

char上個具有同樣的寬度,但是它是有符號的:

int i = (short)c;



如果將一個byte數值b轉型為一個char,并且不希望有符號擴展,那么必須使用一個位掩碼來限制它:

char c = (char)(b & 0xff);// char c = (char) b;為有符號擴展


[size=medium]
6. ((byte)0x90 == 0x90)?

[/size]
答案是不等的,盡管外表看起來是成立的,但是它卻等于false。為了比較byte數值(byte)0x90和int數值0x90,Java

通過拓寬原生類型將byte提升為int,然后比較這兩個int數值。因為byte是一個有符號類型,所以這個轉換執行的是

符號擴展,將負的byte數值提升為了在數字上相等的int值(10010000?111111111111111111111111 10010000)。在本例中,該轉換將(byte)0x90提升為int數值-112,它不等于int數值的0x90,即+144。


解決辦法:使用一個屏蔽碼來消除符號擴展的影響,從而將byte轉型為int。

((byte)0x90 & 0xff)== 0x90



7. 三元表達式(?:)

char x = 'X';
int i = 0;
System.out.println(true ? x : 0);// X
System.out.println(false ? i : x);// 88


條件表達式結果類型的規則:
(1) 如果第二個和第三個操作數具有相同的類型,那么它就是條件表達式的類型。
(2) 如果一個操作的類型是T,T表示byte、short或char,而另一個操作數是一個int類型的“字面常量”,并且

它的值可以用類型T表示,那條件表達式的類型就是T。
(3) 否則,將對操作數類型進行提升,而條件表達式的類型就是第二個和第三個操作被提升之后的類型。


現來使用以上規則解上面的迷題,第一個表達式符合第二條規則:一個操作數的類型是char,另一個的類型是字面常

量為0的int型,但0可以表示成char,所以最終返回類型以char類型為準;第二個表達式符合第三條規則:因為i為int

型變量,而x又為char型變量,所以會先將x提升至int型,所以最后的結果類型為int型,但如果將i定義成final時,

則返回結果類型為char,則此時符合第二條規則,因為final類型的變量在編譯時就使用“字面常量0”來替換三元表

達式了:

final int i = 0;
System.out.println(false ? i : x);// X


在JDK1.4版本或之前,條件操作符 ?: 中,當第二個和延續三個操作數是引用類型時,條件操作符要求它們其中一個

必須是另一個的子類型,那怕它們有同一個父類也不行:

public class T {public static void main(String[] args) {System.out.println(f());}public static T f() {// !!1.4不能編譯,但1.5可以// !!return true?new T1():new T2();return true ? (T) new T1() : new T2();// T1}
}class T1 extends T {public String toString() {return "T1";}
}class T2 extends T {public String toString() {return "T2";}
}



在5.0或以上版本中,條件操作符在延續二個和第三個操作數是引用類型時總是合法的。其結果類型是這兩種類型的最

小公共超類。公共超類總是存在的,因為Object是每一個對象類型的超類型,上面的最小公共超類是T,所以能編譯。

?

在JAVA程序中,性能問題的大部分原因并不在于JAVA語言,而是程序本身。養成良好的編碼習慣非常重要,能夠顯著地提升程序性能。

1. 盡量使用final修飾符。
帶有final修飾符的類是不可派生的。在JAVA核心API中,有許多應用final的例子,例如 java.lang.String。為String類指定final防止了使用者覆蓋length()方法。另外,如果一個類是final的,則該類所有方法都是final的。java編譯器會尋找機會內聯(inline)所有的final方法(這和具體的編譯器實現有關)。此舉能夠使性能平均提高 50%。

2.盡量重用對象。
特別是String對象的使用中,出現字符串連接情況時應使用StringBuffer代替,由于系統不僅要花時間生成對象,以后可能還需要花時間對這些對象進行垃圾回收和處理。因此生成過多的對象將會給程序的性能帶來很大的影響。

3. 盡量使用局部變量。
調用方法時傳遞的參數以及在調用中創建的臨時變量都保存在棧(Stack)中,速度較快。其他變量,如靜態變量,實例變量等,都在堆(Heap)中創建,速度較慢。

4.不要重復初始化變量。
默認情況下,調用類的構造函數時,java會把變量初始化成確定的值,所有的對象被設置成null,整數變量設置成0,float和double變量設置成0.0,邏輯值設置成false。當一個類從另一個類派生時,這一點尤其應該注意,因為用new關鍵字創建一個對象時,構造函數鏈中的所有構造函數都會被自動調用。
這里有個注意,給成員變量設置初始值但需要調用其他方法的時候,最好放在一個方法比如initXXX()中,因為直接調用某方法賦值可能會因為類尚未初始化而拋空指針異常,public int state = this.getState();

5.在java+Oracle的應用系統開發中,java中內嵌的SQL語言應盡量使用大寫形式,以減少Oracle解析器的解析負擔。

6.java編程過程中,進行數據庫連接,I/O流操作,在使用完畢后,及時關閉以釋放資源。因為對這些大對象的操作會造成系統大的開銷。

7.
過分的創建對象會消耗系統的大量內存,嚴重時,會導致內存泄漏,因此,保證過期的對象的及時回收具有重要意義。
JVM的GC并非十分智能,因此建議在對象使用完畢后,手動設置成null。

8.在使用同步機制時,應盡量使用方法同步代替代碼塊同步。

9.盡量減少對變量的重復計算。

比如
Java代碼 復制代碼
  1. for(int?i=0;i<list.size();i++)???
for(int i=0;i<list.size();i++) 

應修改為
Java代碼 復制代碼
  1. for(int?i=0,len=list.size();i<len;i++)??
for(int i=0,len=list.size();i<len;i++)


10. 采用在需要的時候才開始創建的策略。

例如:
Java代碼 復制代碼
  1. String?str="abc"; ??
  2. if(i==1){?list.add(str);}??
String str="abc";
if(i==1){ list.add(str);}

應修改為:
Java代碼 復制代碼
  1. if(i==1){String?str="abc";?list.add(str);}??
if(i==1){String str="abc"; list.add(str);}


11.慎用異常,異常對性能不利。

拋出異常首先要創建一個新的對象。Throwable接口的構造函數調用名為fillInStackTrace()的本地方法,fillInStackTrace()方法檢查棧,收集調用跟蹤信息。只要有異常被拋出,VM就必須調整調用棧,因為在處理過程中創建了一個新的對象。
異常只能用于錯誤處理,不應該用來控制程序流程。

12.不要在循環中使用Try/Catch語句,應把Try/Catch放在循環最外層。

Error是獲取系統錯誤的類,或者說是虛擬機錯誤的類。不是所有的錯誤Exception都能獲取到的,虛擬機報錯 Exception就獲取不到,必須用Error獲取。

13.通過StringBuffer的構造函數來設定他的初始化容量,可以明顯提升性能。
StringBuffer的默認容量為16,當StringBuffer的容量達到最大容量時,她會將自身容量增加到當前的2倍+2,也就是2*n+2。無論何時,只要StringBuffer到達她的最大容量,她就不得不創建一個新的對象數組,然后復制舊的對象數組,這會浪費很多時間。所以給StringBuffer設置一個合理的初始化容量值,是很有必要的!

14.合理使用java.util.Vector。

Vector 與StringBuffer類似,每次擴展容量時,所有現有元素都要賦值到新的存儲空間中。Vector的默認存儲能力為10個元素,擴容加倍。
vector.add(index,obj) 這個方法可以將元素obj插入到index位置,但index以及之后的元素依次都要向下移動一個位置(將其索引加 1)。 除非必要,否則對性能不利。
同樣規則適用于remove(int index)方法,移除此向量中指定位置的元素。將所有后續元素左移(將其索引減 1)。返回此向量中移除的元素。所以刪除vector最后一個元素要比刪除第1個元素開銷低很多。刪除所有元素最好用 removeAllElements()方法。
如果要刪除vector里的一個元素可以使用 vector.remove(obj);而不必自己檢索元素位置,再刪除,如int index = indexOf(obj);vector.remove(index);

15.當復制大量數據時,使用 System.arraycopy();


16.代碼重構,增加代碼的可讀性。

17.不用new關鍵字創建對象的實例。
用 new關鍵詞創建類的實例時,構造函數鏈中的所有構造函數都會被自動調用。但如果一個對象實現了Cloneable接口,我們可以調用她的clone() 方法。clone()方法不會調用任何類構造函數。
下面是Factory模式的一個典型實現。
Java代碼 復制代碼
  1. public?static?Credit?getNewCredit() ??
  2. { ??
  3. ????return?new?Credit(); ??
  4. }??
public static Credit getNewCredit()
{return new Credit();
}

改進后的代碼使用clone() 方法,
Java代碼 復制代碼
  1. private?static?Credit?BaseCredit?=?new?Credit(); ??
  2. public?static?Credit?getNewCredit() ??
  3. { ??
  4. ????return?(Credit)BaseCredit.clone(); ??
  5. }??
private static Credit BaseCredit = new Credit();
public static Credit getNewCredit()
{return (Credit)BaseCredit.clone();
}

18. 乘除法如果可以使用位移,應盡量使用位移,但最好加上注釋,因為位移操作不直觀,難于理解。

19.不要將數組聲明為:public static final。


20.HaspMap的遍歷。

Java代碼 復制代碼
  1. Map<String,?String[]>?paraMap?=?new?HashMap<String,?String[]>(); ??
  2. for(?Entry<String,?String[]>?entry?:?paraMap.entrySet()?) ??
  3. { ??
  4. ????String?appFieldDefId?=?entry.getKey(); ??
  5. ????String[]?values?=?entry.getValue(); ??
  6. }??
Map<String, String[]> paraMap = new HashMap<String, String[]>();
for( Entry<String, String[]> entry : paraMap.entrySet() )
{String appFieldDefId = entry.getKey();String[] values = entry.getValue();
}

利用散列值取出相應的Entry做比較得到結果,取得entry的值之后直接取key和 value。

21.array(數組)和ArrayList的使用。
array 數組效率最高,但容量固定,無法動態改變,ArrayList容量可以動態增長,但犧牲了效率。

22.單線程應盡量使用 HashMap, ArrayList,
除非必要,否則不推薦使用HashTable,Vector,她們使用了同步機制,而降低了性能。

23
.StringBuffer,StringBuilder 的區別在于:java.lang.StringBuffer 線程安全的可變字符序列。一個類似于String的字符串緩沖區,但不能修改。StringBuilder與該類相比,通常應該優先使用 StringBuilder類,因為她支持所有相同的操作,但由于她不執行同步,所以速度更快。為了獲得更好的性能,在構造StringBuffer或 StringBuilder時應盡量指定她的容量。當然如果不超過16個字符時就不用了。
相同情況下,使用StringBuilder比使用 StringBuffer僅能獲得10%~15%的性能提升,但卻要冒多線程不安全的風險。綜合考慮還是建議使用StringBuffer。

24. 盡量使用基本數據類型代替對象。

25.用簡單的數值計算代替復雜的函數計算,比如查表方式解決三角函數問題。


26.使用具體類比使用接口效率高,但結構彈性降低了,但現代IDE都可以解決這個問題。

27.考慮使用靜態方法,

如果你沒有必要去訪問對象的外部,那么就使你的方法成為靜態方法。她會被更快地調用,因為她不需要一個虛擬函數導向表。這同事也是一個很好的實踐,因為她告訴你如何區分方法的性質,調用這個方法不會改變對象的狀態。

28.應盡可能避免使用內在的GET,SET方法。
android編程中,虛方法的調用會產生很多代價,比實例屬性查詢的代價還要多。我們應該在外包調用的時候才使用get,set方法,但在內部調用的時候,應該直接調用。

29. 避免枚舉,浮點數的使用。

30.二維數組比一維數組占用更多的內存空間,大概是10倍計算。

31.SQLite數據庫讀取整張表的全部數據很快,但有條件的查詢就要耗時30-50MS,大家做這方面的時候要注意,盡量少用,尤其是嵌套查找!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/388092.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/388092.shtml
英文地址,請注明出處:http://en.pswp.cn/news/388092.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

對Faster R-CNN的理解(1)

目標檢測是一種基于目標幾何和統計特征的圖像分割&#xff0c;最新的進展一般是通過R-CNN&#xff08;基于區域的卷積神經網絡&#xff09;來實現的&#xff0c;其中最重要的方法之一是Faster R-CNN。 1. 總體結構 Faster R-CNN的基本結構如下圖所示&#xff0c;其基礎是深度全…

大數據業務學習筆記_學習業務成為一名出色的數據科學家

大數據業務學習筆記意見 (Opinion) A lot of aspiring Data Scientists think what they need to become a Data Scientist is :許多有抱負的數據科學家認為&#xff0c;成為一名數據科學家需要具備以下條件&#xff1a; Coding 編碼 Statistic 統計 Math 數學 Machine Learni…

postman 請求參數為數組及JsonObject

2019獨角獸企業重金招聘Python工程師標準>>> 1. (1)數組的請求方式(post) https://blog.csdn.net/qq_21205435/article/details/81909184 (2)數組的請求方式&#xff08;get&#xff09; http://localhost:port/list?ages10,20,30 后端接收方式&#xff1a; PostMa…

領扣(LeetCode)對稱二叉樹 個人題解

給定一個二叉樹&#xff0c;檢查它是否是鏡像對稱的。 例如&#xff0c;二叉樹 [1,2,2,3,4,4,3] 是對稱的。 1/ \2 2/ \ / \ 3 4 4 3但是下面這個 [1,2,2,null,3,null,3] 則不是鏡像對稱的: 1/ \2 2\ \3 3說明: 如果你可以運用遞歸和迭代兩種方法解決這個問題&#…

python 開發api_使用FastAPI和Python快速開發高性能API

python 開發apiIf you have read some of my previous Python articles, you know I’m a Flask fan. It is my go-to for building APIs in Python. However, recently I started to hear a lot about a new API framework for Python called FastAPI. After building some AP…

Purley平臺Linpak測試,從踏坑開始一步步優化

Purley平臺Linpak測試&#xff0c;從踏坑開始一步步優化 #記2017年11月第一次踏坑事件 測試平臺配置&#xff1a; 6nodes CPU: Intel Gold 6132 2.6GHz 14C RAM: 8G *12 2666MHz NET: Infiband FDR OS: centos7.2 mpi: Intel-mpi hpl: xhpl.intel 開始踏第一坑 現象&#xff1a…

基于easyui開發Web版Activiti流程定制器詳解(一)——目錄結構

&#xfeff;&#xfeff;題外話&#xff08;可略過&#xff09;&#xff1a; 前一段時間&#xff08;要是沒記錯的話應該是3個月以前&#xff09;發布了一個更新版本&#xff0c;很多人說沒有文檔看著比較困難&#xff0c;所以打算拿點時間出來詳細給大家講解一下&#xff0c;…

HDOJ 2037:今年暑假不AC_大二寫

AC代碼&#xff1a; #include <iostream> #include <cstdio> #include <algorithm> #define Max 105 using namespace std;struct TimeList {int start;int end; }timelist[Max]; bool compare(TimeList a, TimeList b) {if(a.end b.end)return a.start &l…

基于easyui開發Web版Activiti流程定制器詳解(二)——文件列表

&#xfeff;&#xfeff;上一篇我們介紹了目錄結構&#xff0c;這篇給大家整理一個文件列表以及詳細說明&#xff0c;方便大家查找文件。 由于設計器文件主要保存在wf/designer和js/designer目錄下&#xff0c;所以主要針對這兩個目錄進行詳細說明。 wf/designer目錄文件詳解…

杭電oj2047-2049、2051-2053、2056、2058

2047 阿牛的EOF牛肉串 1 #include<stdio.h>2 3 int main(){4 int n,i;5 _int64 s[51];6 while(~scanf("%d",&n)){7 s[1]3;s[2]8;8 for(i3;i<n;i){9 s[i] s[i-1]*2 s[i-2]*2; 10 } 11 print…

Power BI:M與DAX以及度量與計算列

When I embarked on my Power BI journey I was almost immediately slapped with an onslaught of foreign and perplexing terms that all seemed to do similar, but somehow different, things.當我開始Power BI之旅時&#xff0c;我幾乎立刻受到了外國和困惑術語的沖擊&am…

git 基本命令和操作

設置全局用戶名密碼 $ git config --global user.name runoob $ git config --global user.email testrunoob.comgit init:初始化倉庫 創建新的 Git 倉庫 git clone: 拷貝一個 Git 倉庫到本地 : git clone [url]git add:將新增的文件添加到緩存 : git add test.htmlgit status …

基于easyui開發Web版Activiti流程定制器詳解(三)——頁面結構(上)

&#xfeff;&#xfeff;上一篇介紹了定制器相關的文件&#xff0c;這篇我們來看看整個定制器的界面部分&#xff0c;了解了頁面結構有助于更好的理解定制器的實現&#xff0c;那么現在開始吧&#xff01; 首先&#xff0c;我們來看看整體的結構&#xff1a; 整體結構比較簡單…

bzoj 4300 絕世好題 —— 思路

題目&#xff1a;https://www.lydsy.com/JudgeOnline/problem.php?id4300 記錄一下 mx[j] 表示以第 j 位上是1的元素結尾的子序列長度最大值&#xff0c;轉移即可。 代碼如下&#xff1a; #include<iostream> #include<cstdio> #include<cstring> #include&…

基于easyui開發Web版Activiti流程定制器詳解(四)——頁面結構(下)

&#xfeff;&#xfeff;題外話&#xff1a; 這兩天周末在家陪老婆和兒子沒上來更新請大家見諒&#xff01;上一篇介紹了調色板和畫布區的頁面結構&#xff0c;這篇講解一下屬性區的結構也是定制器最重要的一個頁面。 屬性區整體頁面結構如圖&#xff1a; 在這個區域可以定義工…

梯度下降法優化目標函數_如何通過3個簡單的步驟區分梯度下降目標函數

梯度下降法優化目標函數Nowadays we can learn about domains that were usually reserved for academic communities. From Artificial Intelligence to Quantum Physics, we can browse an enormous amount of information available on the Internet and benefit from it.如…

FFmpeg 是如何實現多態的?

2019獨角獸企業重金招聘Python工程師標準>>> 前言 眾所周知&#xff0c;FFmpeg 在解碼的時候&#xff0c;無論輸入文件是 MP4 文件還是 FLV 文件&#xff0c;或者其它文件格式&#xff0c;都能正確解封裝、解碼&#xff0c;而代碼不需要針對不同的格式做出任何改變&…

基于easyui開發Web版Activiti流程定制器詳解(五)——Draw2d詳解(一)

&#xfeff;&#xfeff;背景&#xff1a; 小弟工作已有十年有余&#xff0c;期間接觸了不少工作流產品&#xff0c;個人比較喜歡的還是JBPM&#xff0c;因為出自名門Jboss所以備受推崇&#xff0c;但是現在JBPM版本已經與自己當年使用的版本&#xff08;3.X&#xff09;大相徑…

Asp.net MVC模型數據驗證擴展ValidationAttribute

在Asp.Mvc項目中有自帶的一套完整的數據驗證功能&#xff0c;客戶端可以用HtmlHelper工具類&#xff0c;服務端可以用ModelState進行驗證。而他們都需要System.ComponentModel.DataAnnotations類庫中的特性功能&#xff0c;通過在屬性上方添加特性就可以達到驗證前后端驗證數據…

seaborn 子圖_Seaborn FacetGrid:進一步完善子圖

seaborn 子圖Data visualizations are essential in data analysis. The famous saying “one picture is worth a thousand words” holds true in the scope of data visualizations as well. In this post, I will explain a well-structured, very informative collection …