給程式設計師的幾點程式設計經驗----《編寫高質量程式碼》
一.在非idea編譯的情況下,不要只替換一個類
我們經常在系統中定義一個常量介面(或常量類),以囊括系統中涉及的常量,從而簡化程式碼,方便開發,在很多的開源專案中採用了類似的方法,比如在Struts2中,org.apache.struts2.StrutsConstants就是一個常量類,它定義了一個Status框架中配置的有關的常量,而org.apache.status2.StatusStatics則是一個常量介面,其中定義了一個OGNL訪問的關鍵字。
關於常量介面(類)我們來看一個例子,首先定義一個常量類:
public class Constant{
//定義人類壽命的極限
public final static int MAX_AGE = 150;
}
這是一個非常簡單的常量類,定義了一個人類的最大年齡,我們引用這個常量,程式碼如下:
public class Client {
public static void main(String[] args){
System.out.println("人類的極限壽命:"+Constant.MAX_AGE);
}
}
執行的結果非常簡單。目前的程式碼編寫都是在“智慧型”IDE工具中完成的,下面我們暫時回溯到原始時代,也就是迴歸到記事本編寫程式碼的時代,然後看看會發生什麼奇妙的事情把。
修改常量類Constant類,人類的壽命增加了,最大能活到180歲,程式碼如下:
public class Constant {
//定義人類的極限壽命
public final static int MAX_AGE = 180;
}
然後重新編譯:javac Constant, 編寫完成後執行:java Client,大家想看看輸出的極限年齡是多少碼?
輸出的結果是:“人類壽命極限是:150”,竟然沒有改變為180,太奇怪了,這是為何?原因是:對於final修飾的基本資料型別和String型別,編譯器會認為它是穩定態(Immutable Status),所以在編譯時就直接把值編譯到位元組碼中了,避免了在執行期引用(Run-time Referece),以提高程式碼的執行效率。針對我們的雷子來說,Client類在編譯時,位元組碼中就寫不上“150”這個常量,而不是一個地址的引用,因此無論你後續怎麼修改常量類,只要不重新編譯Client類,輸出還是照舊。
而對於final修飾的類(即非基本型別),編譯器認為它是不穩定態(Mutable Status),在編譯時建立的則是引用關係(該型別也叫做Soft Final),如果Client類引入的常量是一個類或者例項,即使不重新編譯也會輸出最新值。
呃,我們例子中為什麼不在IDE工具中執行呢?那是因為在IDE中不能重現該問題,若修改了Constant類,IDE工具會自動編譯多有的引用類,“智慧”化遮蔽了該問題,但潛在的風險其實仍然存在。
注意: 釋出應用系統時禁止使用類檔案替換的方式,整體war包釋出才是萬全之策。
二.用偶判斷,不用奇判斷
判斷一個數是奇數還是小學裡學的基本知識,能夠被2整除的整數是偶數,不能被2整除的是奇數,這規則簡單又明瞭,還有什麼好考慮?好,我們來看一個例子,程式碼如下:
public class Client {
public static void main(String[] args){
//接受鍵盤輸入引數
Scanner input = new Scanner(System.in);
System.out.println("請輸入多個數字判斷奇偶:");
Where(input.hasNextInt()){
int i = input.nextInt();
String str = i+ "->" + (i%2 == 1?"奇數":"偶數");
Syetem.out.println(str);
}
}
}
輸入多個數字,然後判斷每個數字的奇偶性,不能被2整除就是奇數,其他的都是偶數,完全是根據奇偶數的定義編寫的程式,我們看看列印的結果:
前三個還是很靠譜,第四個引數-1怎麼可能會是偶數呢,這Java也太差勁了,如此簡單的計算也會出錯!我們先了解一下Java中的取餘(%標示符)演算法,模擬程式碼如下:
//模擬取餘計算,dividend被除數,divisor除數
public static int remainder(int dividend,int divisor){
return dividend - dividend / divsor * divsor;
}
看到這段程式碼,相信大家都會心地笑了,原來Java是這麼處理取餘計算的呀。根據上面的模擬取餘可知,當輸入-1的時候,運算結果是-1,當然不等於1了,所以它就是被判斷為偶數了,也就是說我們的判斷失誤了。問題明白了,修正也是很簡單,改為判斷是否是偶數即可,程式碼如下:
注意: 對於基礎知識,我們應該“知其然,並知其所以然”。
三.邊界,邊界,還是邊界
某商家生產的電子產品非常暢銷,需要提前30天預定才能搶到手,同時它還規定了一個會員可擁有的最多產品數量,目的是防止囤積壓貨肆意加價。會員的預定過程是這樣的:先登入官方網站,選擇產品型號,然後設定需要預定的數量,提交,符合規則即下單成功,不符合規則提示下單失敗。後臺的處理邏輯模擬如下:
public class Client {
//一個會員擁有產品的最多數量
public final static int LIMIT = 2000;
public static void main(String[] args){
//會員當前擁有的產品數量
int cur = 1000;
Scanner input = new Scanner(System.in);
System.out.println("請輸入需要預定的數量:");
Where(input.hasNext()){
int order = input.nextInt();
//當前擁有的與準備預定的產品數量之和
if(order>0 && order+cur<=LIMIT){
System.out.println("你已經成功預定的"+order+"個產品!");
}else{
System.out.println("超過限額,預定失敗!");
}
}
}
}
這是一個簡易的訂單處理程式,其中cur代表的是會員已經擁有的產品數量,LIMIT是一個會員最多擁有的產品數量,如果當前預定數量與擁有數量之和超過了最大數量,則預定失敗,否則下單成功。業務邏輯很簡單,同時在web介面上對訂單數量做了嚴格的校驗,比如不能是負值、不能超過最大數量等,但是人算不如天算,執行不大倆小時資料庫中就出現異常:某會員擁有的產品數量與預定數量之和遠遠大於限額。我們模擬一下:
看到沒,這個數字遠遠超過2000的限額,但是竟然預定成功了,真實神奇!
看著2147483647這個數字很眼熟?那就對了,它是int型別的最大值,沒錯,有人輸入一個最大值,是校驗條件失效了,why?我們來看程式,order的值是2147483647,那在加上1000就超出int的範圍了,其結果是-2147482649,那當然是小於正數2000了!一句話可歸原因:數字超界使校驗條件失效。
在單元測試中,有一項測試叫做邊界測試(也有交臨界值測試),如果一個方法接受的int型別的引數,那麼以下三個值是必測的:0、最大值、最小值,其中正最大和負最小是邊界值,如果這三個值都沒有問題,方法才是比較安全可靠的。我們的例子就是因為缺少邊界測試,導致生產系統產生了嚴重的偏差。
四.優先使用整形池
看程式碼我們解決問題,
public static void main(String[] args){
Scanner in = new Scanner(System.in);
while(in.hasNextInt()){
int input = in.nextInt();
System.out.println("\n-----"+ii+"的相等判斷-----");
//倆個通過new 產生的物件
Integer i = new Integer(input);
Integer j = new Integer(input);
System.out.println("new產生的物件:"+(i==j));
//基本型別轉換成包裝型別後比較
i=input;
j=input;
System.out.println("基本型別轉換的物件:"+(i==j));
//通過靜態方法生成一個例項
i=Integer.valueOf(input);
j=Integer.valueOf(input);
System.out.println("valueOf產生的物件:"+(i==j));
}
}
分別輸入三個值,127,128,555產生的結果圖:
很不可思議,對吧。那這是為什麼呢?
(1)new產生的Integer物件
new宣告就是要生成一個新的物件,沒二話,這是倆個物件,地址肯定不相等。false
(2)裝箱生成的物件
對於這一點,首先要說明的是裝箱動作是通過valueOf方法實現的,也就是說後倆個演算法是相同的,那結果肯定也是一樣。那現在的問題是:valueOf是如何生成物件的呢?我們閱讀一下Integer.valueOf的實現程式碼。
public static Integer valueOf(int i){
final int offset = 128;
if(i>=-128 && i<=127){
return IntegerCache.cache(i);
}
return new Integer(i);
}
顯而易見,如果是-128到127之前的int型別轉換成Integer物件,則直接從cache陣列中取,那這個cache資料是作甚麼?
static final Integer cache[] = new Integer[-(-128)+127+1];
static{
for(int i=0; i<cache.length; i++){
cache[i] = new Integer(i-128);
}
}
cache是IntegerCache內部類的一個靜態陣列,容納的是-127到128之間的Integer物件。通過valueOf產生包裝物件時,如果int引數在-128到127之間,則直接在從整型池中獲取物件,不再該範圍內的int 型別則通過new 生成包裝物件。
總結:通過包裝類的valueOf生成包裝例項可以顯著提高空間和時間效能。
相關文章
- 老程式設計師的10個程式設計小技巧,教你寫出高質量程式碼!程式設計師
- 高質量C++/C程式設計指南(第11章 其它程式設計經驗) (轉)C++C程式程式設計
- 如何招聘到高質量的程式設計師?程式設計師
- 這些資深程式設計師的程式設計小技巧,教你寫出高質量程式碼!程式設計師
- 給新手程式設計師的幾點建議程式設計師
- 一個老程式設計師的程式設計之路,寫給年輕的程式設計師們程式設計師
- 給 Python程式設計師的函數語言程式設計實踐經驗Python程式設計師函數
- 書寫高質量jQuery程式碼的12條經驗jQuery
- 程式設計師如何保證我們的程式碼質量程式設計師
- 程式設計師面試經驗程式設計師面試
- 程式設計師的管理經驗程式設計師
- 程式設計師程式設計知識經驗總結程式設計師
- 高階程式設計師考試經驗 (轉)程式設計師
- 一個十幾年程式設計師給所有新老程式設計師的忠告程式設計師
- 程式設計師:增加程式設計經驗的3種途徑程式設計師
- 高階程式設計師的程式碼質量應當達到什麼水準程式設計師
- 中國程式設計師與美國程式設計師寫程式碼的區別分析程式設計師
- 送給程式設計師:最好的程式設計名言程式設計師
- 程式設計師如何寫出好程式碼?程式設計師
- 好程式設計師不寫程式碼程式設計師
- 程式設計師這樣寫程式碼程式設計師
- 《程式設計師健康指南》:給程式設計師的健康書程式設計師
- 寫給程式設計師:當自己的上帝程式設計師
- VB程式設計經驗點滴程式設計
- 怎樣編寫高質量的java程式碼Java
- 編寫高質量的程式碼,從命名入手
- 程式設計師自我修養之程式設計經驗總結程式設計師
- 為什麼程式設計師要儘量少寫程式碼程式設計師
- 程式設計師如何寫出更好的程式碼程式設計師
- 程式設計師怎樣才能達到程式設計的最高境界?(送給喜歡寫程式碼的朋友)程式設計師
- 美女程式設計師觀點:程式設計師最重要的非程式設計技巧程式設計師
- 程式設計師妻子自述:那些程式設計師教給我的程式設計師
- 程式設計師妻子自述: 那些程式設計師教給我的程式設計師
- 在程式設計師眼中,工作/生活平衡和高質量程式碼幾乎同等重要程式設計師
- 當程式設計師寫不出程式碼了……程式設計師
- 程式設計師應該每天寫程式碼程式設計師
- 程式設計師,千萬不要重寫程式碼程式設計師
- 三個程式設計師在寫程式碼程式設計師