如何編寫高質量JAVA程式碼
阿新 • • 發佈:2019-01-25
如何編寫高質量程式碼,從而提升系統性能。想必是很多程式設計師都非常注意的地方,最近總結了一些要點,特此記錄在案。 所謂程式碼高可讀性和高可維護性,其實就是應該有著規範的Java程式設計風格,在開發程式過程中,從近期目標看是應該著眼於功能實現,但是隻能解一時之渴,而不思長遠之計,確不可取,一個雜亂無序的程式碼讓人看後有一種不解其意,心緒煩亂的感覺。所以,作為一名合格的程式設計師,一定要確定一個觀點就是你編寫出來的程式碼不只是給你一個人看的,還是給別人看的,所以在開發過程中檔案註釋頭,java原始檔編排格式,方法體的具體業務含義的註釋都是必須的。 如程式註釋就分為塊註釋與行註釋 。例如塊註釋為
/**
* @param
@return
*/
行註釋
/** **/或者 //
再者就是方法的命名也需要多加斟酌,一個業務方法,如果取最能體現體現該業務的名字,這樣讀者幾乎不需要看程式碼便可以知道該方法具體用途。 高質量的程式碼其實很多時候都在一些小細節中體現,對於每個程式設計師來說一個for迴圈都會寫,可是卻有很多人沒有能在程式碼中體現出高效性來,在這裡我用簡單的一個例子來說明:一個ArrayList需要遍歷。 一般人會寫成for(int i=0;i<list.size();i++) 這有問題嗎?沒有問題,能夠完成程式設計師的意圖的功能。
可是它高效嗎?你有注意到這點嗎?其實問題就出現在list.size()方法,這個方法是計算一個list的大小,本身它不會存在任何問題,可是將它放在了一個for()迴圈中的話,就很有問題了,因為如果一個N次的for迴圈,這個方法就需要被執行N次,這樣的程式碼就造成計算機花很多的時間去做沒有意義的事情,而本來這個list.size()方法只需要計算一次的就可以了,所以我們把計算list大小的方法放在for迴圈外面去定義的話,效率就可以得到提高
如: int size = list.size();
for(int i=0;i<size;i++)
關於For迴圈還有一個要注意的地方,就是在for迴圈裡面去New一個新物件。如:
for(int i=0;i<10;i++){
A a = new A();
}
是不是怎麼看都不會有問題,是的在語法上。或者是執行業務處理邏輯的時候,它都是沒有任何問題的,可是這是從語言級別去看待問題,沒有深入到它的實現機制上去看待問題。
在介紹這個問題的之前我想先簡單說下關於java記憶體的機制:java是如何在記憶體中儲存物件,我們回到A a=new A()在記憶體中是怎麼分配這個問題來,
首先在棧中開闢一段空間存放引用a,然後在堆中開闢一段空間存放新建的A物件,同時引用a指向堆中新建得A物件,a所指代的物件地址為A在堆中地址。根據javaGC機制,只有物件已經沒有引用指向它的時候,才有可能被GC給回收,所以基於這種機制的話,上述的一個For迴圈就會存在很大的效率問題了,如果迴圈有1000次,在記憶體中棧會有1000個引用,而堆中也會有1000個新生成的物件,同時1000個引用會相應指向1000個新生成的物件,即使這個for迴圈結束,也不會有任何改變。但是實際上1000個引用的生成完全是沒有必要的,如果有著編寫高效能程式碼的想法的話:像這樣的for迴圈完全可以這樣寫:
A a = null;
for(int i=0;i<1000;i++){
a = new A();
...
}
這樣的程式碼在記憶體中便只會在棧中生成一個指向,每當一個for迴圈結束後,這個指向會指向下一個新生成的物件,前面生成的物件就會失去指向,這樣GC就有可能更加快速的回收這些已經失去功能的物件。 在java中其實new 一個物件是非常耗費時間的,特別是重量級物件,所以每次在new 物件的時候一定需要考慮清楚是不是非的生成一個物件才能完成我的業務需求呢?總之能夠根據實際情況,然後舉一反三的話,我相信大家編寫出來的程式碼就會更加高效了。
其次是針對同步的慎重考慮,因為我們一旦用了synchronized這個關鍵字後,就很可能喪失了並行的功效,所以在開發的過程中需要注意到執行緒是不是會對共有的資源進行處理,然後在慎重選擇Synchronized 關鍵字,其實大家可以考慮用ThreadLocal這個類,它的優點是既保證同步的情況下仍然能保證並行,缺點是會佔用更多的空間去換取換取時間。
最後,便是在j2ee開發過程中對資料庫操作的優化,在這裡我只針對程式碼級別的優化,關於資料庫級別的我不涉及。大家編寫SQL的時候會不會注意到一些原則,在這裡我將羅列一些需要注意的要點。(總結肯定不會很全,我希望如果有大蝦能給予更多的經驗指導,在下將會感激萬分。)
(1)在搜尋子句的列名邊要避免函式、算術操作符和其它的表示式,除非建立基於函式的索引
(2)使用複合索引的第一個列名
(3)SELECT子句中避免使用 ‘*’
(4)如果可能儘量多用"Commit"
(5)避免在索引列上使用IS null和Is not null
(6)用Union -all替換Union(如果可能的話)
(7)Oracel 採用自下而上的順序解析WHERE子句,可以過濾掉最大數量記錄的條件寫在WHERE子句的末尾
(8)between謂詞可以轉化為>=and<=子句,比如:price between 10 and 20
可以轉化為 price>= 10 and <=20
(9)like子句中匹配值的第一個字元是常量,也可以進行轉換, 例如:
like “sm%”可以轉換成 >=“sm” and <“sn”
(10)在子查詢,exists和in要比not exists和not in執行得快,因為對於exists和in,優化器只要找到一條記錄就可以返回TRUE,而對於not exists和not in則必須檢查所有的值。
以上10條總結如果在編寫sql的時候能注意到的話,將會在一定程度上提高java跟資料互動的效能。
那麼除了在SQL上下功夫來提高效能之外,編寫合適的事務處理也將帶來一些效能提高。我們都知道事務具有:原子性,隔離性,一致性,永續性,所以在使用事務的時候肯定是犧牲併發效能為代價的。特別是一個涉及update的事務處理的時候,資料庫會在表上加上排他鎖,而一個數據資源只要被一個事務放置了排他鎖,其他事務將不能放上排他鎖,一定要一直等到事務結束後才釋放。所以在這種情況下的,併發性就會被抹殺掉了。我們不能改變這種加鎖的機制,但是我們可以用另外一種方式來達到一定程度的效能提升,那就是根據實際情況將一個大事務分解成小事務來處理。簡而言之就是減低事務放置排他鎖和釋放排他鎖的時間間隔,這樣可以讓其他的事務能更快的訪問到資料資源。而關於大事務分解一定要小心使用,如果使用不恰當的話很可能會產生意想不到的資料不一致錯誤。
/**
* @param
@return
*/
行註釋
/** **/或者 //
再者就是方法的命名也需要多加斟酌,一個業務方法,如果取最能體現體現該業務的名字,這樣讀者幾乎不需要看程式碼便可以知道該方法具體用途。 高質量的程式碼其實很多時候都在一些小細節中體現,對於每個程式設計師來說一個for迴圈都會寫,可是卻有很多人沒有能在程式碼中體現出高效性來,在這裡我用簡單的一個例子來說明:一個ArrayList需要遍歷。 一般人會寫成for(int i=0;i<list.size();i++) 這有問題嗎?沒有問題,能夠完成程式設計師的意圖的功能。
可是它高效嗎?你有注意到這點嗎?其實問題就出現在list.size()方法,這個方法是計算一個list的大小,本身它不會存在任何問題,可是將它放在了一個for()迴圈中的話,就很有問題了,因為如果一個N次的for迴圈,這個方法就需要被執行N次,這樣的程式碼就造成計算機花很多的時間去做沒有意義的事情,而本來這個list.size()方法只需要計算一次的就可以了,所以我們把計算list大小的方法放在for迴圈外面去定義的話,效率就可以得到提高
如: int size = list.size();
for(int i=0;i<size;i++)
關於For迴圈還有一個要注意的地方,就是在for迴圈裡面去New一個新物件。如:
for(int i=0;i<10;i++){
A a = new A();
}
是不是怎麼看都不會有問題,是的在語法上。或者是執行業務處理邏輯的時候,它都是沒有任何問題的,可是這是從語言級別去看待問題,沒有深入到它的實現機制上去看待問題。
在介紹這個問題的之前我想先簡單說下關於java記憶體的機制:java是如何在記憶體中儲存物件,我們回到A a=new A()在記憶體中是怎麼分配這個問題來,
首先在棧中開闢一段空間存放引用a,然後在堆中開闢一段空間存放新建的A物件,同時引用a指向堆中新建得A物件,a所指代的物件地址為A在堆中地址。根據javaGC機制,只有物件已經沒有引用指向它的時候,才有可能被GC給回收,所以基於這種機制的話,上述的一個For迴圈就會存在很大的效率問題了,如果迴圈有1000次,在記憶體中棧會有1000個引用,而堆中也會有1000個新生成的物件,同時1000個引用會相應指向1000個新生成的物件,即使這個for迴圈結束,也不會有任何改變。但是實際上1000個引用的生成完全是沒有必要的,如果有著編寫高效能程式碼的想法的話:像這樣的for迴圈完全可以這樣寫:
A a = null;
for(int i=0;i<1000;i++){
a = new A();
...
}
這樣的程式碼在記憶體中便只會在棧中生成一個指向,每當一個for迴圈結束後,這個指向會指向下一個新生成的物件,前面生成的物件就會失去指向,這樣GC就有可能更加快速的回收這些已經失去功能的物件。 在java中其實new 一個物件是非常耗費時間的,特別是重量級物件,所以每次在new 物件的時候一定需要考慮清楚是不是非的生成一個物件才能完成我的業務需求呢?總之能夠根據實際情況,然後舉一反三的話,我相信大家編寫出來的程式碼就會更加高效了。
其次是針對同步的慎重考慮,因為我們一旦用了synchronized這個關鍵字後,就很可能喪失了並行的功效,所以在開發的過程中需要注意到執行緒是不是會對共有的資源進行處理,然後在慎重選擇Synchronized 關鍵字,其實大家可以考慮用ThreadLocal這個類,它的優點是既保證同步的情況下仍然能保證並行,缺點是會佔用更多的空間去換取換取時間。
最後,便是在j2ee開發過程中對資料庫操作的優化,在這裡我只針對程式碼級別的優化,關於資料庫級別的我不涉及。大家編寫SQL的時候會不會注意到一些原則,在這裡我將羅列一些需要注意的要點。(總結肯定不會很全,我希望如果有大蝦能給予更多的經驗指導,在下將會感激萬分。)
(1)在搜尋子句的列名邊要避免函式、算術操作符和其它的表示式,除非建立基於函式的索引
(2)使用複合索引的第一個列名
(3)SELECT子句中避免使用 ‘*’
(4)如果可能儘量多用"Commit"
(5)避免在索引列上使用IS null和Is not null
(6)用Union -all替換Union(如果可能的話)
(7)Oracel 採用自下而上的順序解析WHERE子句,可以過濾掉最大數量記錄的條件寫在WHERE子句的末尾
(8)between謂詞可以轉化為>=and<=子句,比如:price between 10 and 20
可以轉化為 price>= 10 and <=20
(9)like子句中匹配值的第一個字元是常量,也可以進行轉換, 例如:
like “sm%”可以轉換成 >=“sm” and <“sn”
(10)在子查詢,exists和in要比not exists和not in執行得快,因為對於exists和in,優化器只要找到一條記錄就可以返回TRUE,而對於not exists和not in則必須檢查所有的值。
以上10條總結如果在編寫sql的時候能注意到的話,將會在一定程度上提高java跟資料互動的效能。
那麼除了在SQL上下功夫來提高效能之外,編寫合適的事務處理也將帶來一些效能提高。我們都知道事務具有:原子性,隔離性,一致性,永續性,所以在使用事務的時候肯定是犧牲併發效能為代價的。特別是一個涉及update的事務處理的時候,資料庫會在表上加上排他鎖,而一個數據資源只要被一個事務放置了排他鎖,其他事務將不能放上排他鎖,一定要一直等到事務結束後才釋放。所以在這種情況下的,併發性就會被抹殺掉了。我們不能改變這種加鎖的機制,但是我們可以用另外一種方式來達到一定程度的效能提升,那就是根據實際情況將一個大事務分解成小事務來處理。簡而言之就是減低事務放置排他鎖和釋放排他鎖的時間間隔,這樣可以讓其他的事務能更快的訪問到資料資源。而關於大事務分解一定要小心使用,如果使用不恰當的話很可能會產生意想不到的資料不一致錯誤。