改善java程式的151個建議--筆記一,java151--筆記
一、Java開發中通用的方法和準則
1、不要在常量和變數中出現易混淆的字母
eg:long i=1l,這是11還是lL呢,建議使用大寫的方式;
2、不要讓常量變成變數
eg:public static final int RAND_CONST=new Random().nextInt();
3、三元符的類型務必一致
eg:String s=String.valueOf(i<100?90:100.0);
4、避免帶有變長參數的方法重載
eg:calPrice(int price,int discount);
calPrice(int price,int... discounts);
5、別讓null值和空值威脅到變長方法
eg:methodA(String str,Integer... is);
methodA(String str,String... strs);
Test:methodA(“China”);
methodA(“China”,null);
6、覆寫變長方法也要循規蹈矩
Eg:parent:method(int price,int... discounts);
Children:method(int price,int[] discounts);
7、警惕自增陷阱
Eg:int count=0;for(int i=0;i<10;i++){count=count++};輸出結果為0
8、不要讓就文法困擾
Eg:goto語句、methodA:methodB();
9、少用靜態匯入
10、不要在本類中覆蓋靜態匯入的變數和方法
11、養成良好的習慣,顯式聲明UID
Eg:序列化
12、避免用序列化類別在建構函式中為不變數(final修飾)賦值
13、避免為final變數複雜賦值
Eg:public final String name=initName();
Public String initName(){return “hello”;}
還原序列化時final變數在以下情況下不會被重新賦值:
1)通過建構函式為final變數賦值;
2)通過方法返回值為final變數賦值;
3)Final修飾的屬性不是基本類型;
14、使用序列化類別的私人方法巧妙解決部分屬性持久化問題
15、Break萬萬不可忘
Switch....case中....
16、易變業務使用指令碼語言編寫
例如通過java中的ScriptEngine調用javascript中的方法
17、慎用動態編譯:在運行期間直接編譯.java檔案,執行.class,將java類資訊寫入到字串中,然後運行期間調用相關介面執行字串中的java資訊
18、避免instanceOf非預期結果,注意instanceOf成立的條件是運算元左右兩邊要有繼承或者實現關係,否則編譯會報錯
Eg:’A’ instanceOf Character、new Date() instanceOf String,
19、斷言絕對不是雞肋:assert<布林運算式>:<錯誤資訊>
20、不要只替換一個類:發布應用系統時禁止使用類檔案替換方式,整體war包發布才是完全之策
二、基本類型
21、判斷奇偶數,用偶判斷,不用奇判斷
Eg:i%2==1?”奇數”:”偶數”;-1的時候會出現問題
正確使用:i%2==0?”偶數”:”奇數”;
22、用BigDecimal和整形類型處理貨幣
23、不要讓類型默默轉換
Eg:public static final int LENGTH_SPEED=30*10000*1000;
long v=LENGTH_SPEED*60*8會出現負數,因為計算的時候,先是運算再進行類型轉換的,三個數都是int類型,計算後超出int的最大值,所以計算結果為負值;
正確的方式:long v=1L*LENGTH_SPEED*60*8;計算的時候後面的都會轉換成為長整形
24、整形類型的邊界值需要考慮,在進行單元測試的時候,進行int型別參數測試的時候,需要考慮下面幾個值:0、正最大、負最小
25、不要讓四捨五入虧了一方,例如在做銀行、金融相關行業的系統的時候,要根據不同的情境,謹慎選擇不同的舍入模式,以提高項目的精準度,減少演算法損失
26、提防封裝類型的null值:封裝類型參與運算時候,要做null值校正
27、謹慎封裝類型的大小比較
Eg:Integer a=new Integer(100);
Integer b=new Integer(100);
a==b:false,a>b:false,a<b:false;
28、優先使用整形池:基本類型的裝箱動作是通過valueOf方法來實現的,對於int類型轉換為Integer對象,如果int值的範圍在-128到127之間,那麼它直接從cache數組中獲得,不在該範圍的int類型則通過new產生封裝對象,通過封裝類的valueOf產生封裝執行個體可以顯著提高空間和時間效能
29、優先選擇基本類型
Eg:int i=0;
test(i);
test(Integer.valueOf(i));
public static void test(long i){}
public static void test(Long i){}
在進行自動裝箱的過程中,基本類型可以先加寬,再轉變成寬類型的封裝類型,但是不能直接轉變成寬類型的封裝類型。Int可以加寬變成long類型,然後再轉變成為Long對象,但是不能直接變成Long對象。
30、不要隨便設定隨機數的種子
Eg:Random random=new Random(1000);產生隨機數的種子被固定了
Java中產生隨機數:Random、Math.random();
三、類對象及方法
31、在介面中不要存在實現的代碼
Interface S{public void doSomething();} interface B{S s=new S(){public void dos{}}}
32、靜態變數一定要先聲明後賦值
Eg:static{i=100;} public static int i=1;
33、不要覆寫靜態方法,當父類中定義了一個靜態方法後,子類中應不要覆寫靜態方法,類中定義靜態方法或屬性應該通過類名直接存取,通過執行個體對象訪問靜態方法或屬性不是好的習慣;
34、建構函式應盡量簡化,否則可能會出現因初始化的時候變數的不一致;
35、避免在建構函式中初始化其他類
36、使用構造代碼塊精鍊程式:構造代碼塊會在每個不同參數的構造方法中都執行一次,先執行構造代碼塊,再執行構造方法,它並不是在建構函式之前啟動並執行,它依託於建構函式執行,我們可以把構造代碼塊運用到如下情境中:1:初始化執行個體變數2:初始化執行個體環境;
37、構造代碼塊會想你所想,在構造方法中使用this和super的時候,構造代碼塊已經替你考慮好了,如果在有參數構造方法中使用this(),是不會再重新執行構造代碼塊的,所以要靈活運用;
38、使用靜態內部類加強類的封裝性和提高了代碼的可讀性
39、使用匿名類的建構函式:new ArrayList(){};new ArrayList(){{}{}{}}
40、匿名類的建構函式很特殊;
41、靈活使用內部類,讓多重繼承成為現實;
42、讓工具類不可執行個體化最好的方式是:這樣也不能利用反射機制進行執行個體化
public class Utils{
private Utils(){
Throws new Error(“don’t intstance me!!”);
}}
43、避免對象的淺拷貝:
Object提供的clone方法只是一種淺拷貝方式,也就是說它並不會把對象的所有屬性全部拷貝一份,而是有選擇性的拷貝,其拷貝規則如下:
基本類型:則拷貝其值
對象:拷貝地址引用,也就是說新拷貝出的對象與原有對象共用該執行個體變數,不受存取權限的限制。
String字串:拷貝的也是一個地址,是個引用,但是在修改時,它會從字串池(String pool)中重建新的字串,原有的字串對象保持不變,在此我們可以認為String是一個基本類型。
44、推薦使用序列化的方式實現對象的拷貝:參考我的博文,java對象的淺拷貝和深拷貝:http://blog.csdn.net/harderxin/article/details/41694747
45、覆寫equals方法時不要識別不出自己(見代碼)
//public boolean equals(Object obj) {//if(obj instanceof Person3){//Person3 p=(Person3)obj;//去掉trim()//return name.equalsIgnoreCase(p.getName().trim());//}//return false;//}
46、equals應該考慮null值情景
// 第二個版本----建議46//@Override//public boolean equals(Object obj) {//if(obj instanceof Person3){//Person3 p=(Person3)obj;//if(p.getName()==null||name==null){//return false;//}else{//return name.equalsIgnoreCase(p.getName().trim());//}//}//return false;//}
47、為了防止繼承,建議在equals中使用getClass進行類型判斷,而不是使用instanceOf
// 第三個版本----建議47@Overridepublic boolean equals(Object obj) {if(obj!=null&&obj.getClass()==this.getClass()){Person3 p=(Person3)obj;if(p.getName()==null||name==null){return false;}else{return name.equalsIgnoreCase(p.getName().trim());}}return false;}
48、覆寫equals方法必須覆寫hashcode方法:
HashMap:底層處理機制是以數組的方式儲存Map條目(Map Entry)的,這其中的關鍵是這個數組下標的處理機制:依據傳入元素hashCode方法的返回值決定其數組的下標,如果該數組位置上已經有了Map條目,且與傳入的鍵值相等則不處理,若不相等則覆蓋;如果數組位置沒有條目,則插入,並加入到Map條目的鏈表中,同理,檢查鍵是否存在也是根據雜湊碼確定位置,然後遍曆尋找鍵值的。HashCode的值我們可以使用org.apache.commons.lang.builder包下的一個雜湊碼產生工具HashCodeBuilder產生
49、推薦覆寫toString方法:java提供預設的toString方法不友好,列印出來只有機器能看懂,其格式為類名+@+hashCode,所以建議覆寫toString方法,便於我們進行調試
50、使用package-info類為包服務:在需要用到包的地方,可以考慮一下這個特殊的類
51、不要主動進行記憶體回收:在應用程式中不要直接使用System.gc()來主動對垃圾進行回收,即使經常出現記憶體溢出也不要調用