不要在常量和變量中出現(xiàn)易混淆的字母
包名全小寫,類名首字母全大寫佩谷,常量全部大寫并用下劃線分割辞做,變量采用駝峰(Camel Case)命名。在變量的聲明中不要引入容易混淆的字母:
long i=1l;
System.out.println("i的兩倍是:"+(i+i));
如果字母和數(shù)字混合使用,字母“l(fā)”務(wù)必大寫框喳,字母“O”則增加注釋五垮。
注意杜秸,字母“l(fā)”作為長整型標(biāo)志時(shí)務(wù)必大寫撬碟。
莫讓常量蛻變成變量
加了final和static的常量可能會(huì)變嗎莉撇?
interface ConstDemo {
public static final int RAND_CONST = new Random().nextInt();
}
System.out.println("常量會(huì)變哦:" + ConstDemo.RAND_CONST);
常量就是常量棍郎,在編譯器就必須確定其值银室,不應(yīng)該在運(yùn)行期更改,負(fù)責(zé)程序的可讀性會(huì)非常差蜈敢,甚至連作者自己都不能確定在運(yùn)行 期發(fā)生了何種神奇的事情辜荠。
三元操作符的類型務(wù)必一致
int i = 80;
String s = String.valueOf(i < 100 ? 90 : 100);
String s1 = String.valueOf(i < 100 ? 90 : 100.0);
System.out.println("兩者是否相等:" + s.equals(s1));
三元操作符類型的轉(zhuǎn)換規(guī)則:
若兩個(gè)操作數(shù)不可轉(zhuǎn)換,則不做鉆換抓狭,返回值為Object類型
若干兩個(gè)操作數(shù)是明確類型額表達(dá)式(比如變量)侨拦,則按正常的二進(jìn)制數(shù)字來轉(zhuǎn)換,int類型轉(zhuǎn)換為long類型辐宾,long類型轉(zhuǎn)換為float類型等狱从。
若兩個(gè)操作數(shù)中有一個(gè)是數(shù)字S,另外一個(gè)是表達(dá)式,且其類型標(biāo)識(shí)為T,那么叠纹,若數(shù)字S在T的范圍內(nèi),則轉(zhuǎn)換為T類型誉察;若S超出T類型的范圍与涡,則T轉(zhuǎn)換為S類型
若兩個(gè)操作數(shù)都是直接量數(shù)字,則返回值類型為范圍較大者持偏。
注意保證三元操作符中兩個(gè)操作數(shù)類型一致驼卖,即可減少錯(cuò)誤的發(fā)生。
避免帶有變長參數(shù)的方法重載
public void calPrice(int price, int discount) {
float knockdownPrice = price * discount / 100.0F;
System.out.println("簡單折扣后的價(jià)格是:" + formateCurrency(knockdownPrice));
}
public void calPrice(int price, int... discounts) {
float knockdownPrice = price;
for (int discount : discounts) {
knockdownPrice = knockdownPrice * discount / 100;
}
System.out.println("復(fù)雜折扣后的價(jià)格是:" + formateCurrency(knockdownPrice));
}
private String formateCurrency(float price) {
return NumberFormat.getCurrencyInstance().format(price / 100);
}
Java在編譯時(shí)鸿秆,首先會(huì)根據(jù)實(shí)參的數(shù)量和類型來進(jìn)行處理酌畜,也就是查到calPrice(int price,int discount)方法,而且確認(rèn)它是否符合方法簽名條件卿叽。
int是一個(gè)原生數(shù)據(jù)類型桥胞,而數(shù)組本身是一個(gè)對象,編譯器想要“偷懶”考婴,所以會(huì)這樣選擇贩虾。
慎用變長參數(shù)的方法重載,讓人傷腦經(jīng)不說沥阱,蘇紅補(bǔ)丁哪天就陷入這類小陷阱里了缎罢。
別讓null值和空值威脅到變長方法
public class Client {
public void methodA(String str, Integer... is) {
}
public void methodA(String str, String... strs) {
}
public static void main(String[] args) {
Client client = new Client();
client.methodA("China", 0);
client.methodA("China", "People");
client.methodA("China", null);
}
}
public static void main(String[] args){
Client client = new Client();
String[] strs = null;
client.methodA("China",strs);
}
讓編譯器知道這個(gè)null值是String類型的,編譯即可順利通過,也就減少了錯(cuò)誤的發(fā)生策精。
覆寫變長方法也循規(guī)蹈矩
在Java中舰始,子類覆寫符類的方法很常見,這樣做既可以修正Bug也可以提供擴(kuò)展的業(yè)務(wù)功能支持蛮寂,同時(shí)還符合開閉原則(Open-Closed Principle)蔽午。覆寫必須滿足的條件:
重寫方法不能縮小訪問權(quán)限。
參數(shù)列表必須與被重寫方法相同酬蹋。
返回類型必須與重寫方法的相同或者是其子類及老。
重寫方法不能拋出新的異常,或者超出符類范圍的異常范抓,但是可以拋出更少骄恶,更有限的異常,或者不拋出異常匕垫。
public class Client {
public static void main(String[] args) {
Base base = new Sub();
base.fun(100, 50);
Sub sub = new Sub();
sub.fun(100, 50);
}
}
class Base {
void fun(int price, int... discounts) {
System.out.println("Base.....fun");
}
}
class Sub extends Base {
@Override
void fun(int price, int[] discounts) {
System.out.println("Sub.....fun");
}
}
警惕自增的陷阱
int count = 0;
for (int i = 0; i < 10; i++) {
count = count++;
}
System.out.println("count=" + count);
結(jié)果是0
count++是一個(gè)表達(dá)式僧鲁,是由返回值的,它的返回值就是count自加前的值象泵,Java對自加是這樣處理的:首先把count的值(注意是值寞秃,不是引用)拷貝到一個(gè)臨時(shí)變量區(qū),然后對count變量加1偶惠,最后返回臨時(shí)變量區(qū)的值春寿。程序第一次玄幻時(shí)的詳細(xì)處理步驟如下:
1.JVM把count的值(其值是0)拷貝到臨時(shí)變量區(qū)
2.count值加1,這時(shí)候count的值是1
3.返回臨時(shí)變量區(qū)的值忽孽,注意這個(gè)值是0绑改,沒修改過。
4.返回值賦值給count兄一,此時(shí)count值被重置成0.
不要讓舊語法困擾你
Java中沒有了goto關(guān)鍵字厘线,但是擴(kuò)展了break和continue關(guān)鍵字。
少用靜態(tài)導(dǎo)入
靜態(tài)導(dǎo)入語法(import static)其目的是為了減少字符串輸入量出革,提高代碼的可閱讀性造壮,以便更好的理解程序。但是蹋盆,濫用靜態(tài)導(dǎo)入回是程序更難于都费薄,更難維護(hù)。會(huì)讓閱讀者很難弄清除其屬性或方法代表何意栖雾,甚至是哪一個(gè)類的屬性(方法)都要思考一番。(IDE友好提示是另說)
對于靜態(tài)導(dǎo)入伟众,一定要遵循兩個(gè)原則:
1.不使用*(星號)通配符析藕,除非是導(dǎo)入靜態(tài)常量類(只包含常量的類或接口)。
2.方法名是具有明確凳厢,清晰表象意義的工具類
不要在本類中覆蓋靜態(tài)導(dǎo)入的變量和方法
編譯器有一個(gè)“最短路徑”原則:如果能在奔雷中找到的變量账胧,常量竞慢,方法,就不會(huì)到其他包或者符類治泥,接口中查找筹煮,以確保奔雷中的屬性,方法優(yōu)先居夹。
如果要變更一個(gè)被導(dǎo)入的方法败潦,最好的辦法是在原始類中重構(gòu),而不是在本類中覆蓋准脂。
養(yǎng)成良好習(xí)慣劫扒,顯式聲明UID
類實(shí)現(xiàn)Serializable接口目的是為了可持久化,比如網(wǎng)絡(luò)傳輸或本地存儲(chǔ)狸膏,為系統(tǒng)的分布和異構(gòu)部署提供先決支持條件沟饥。若沒有序列化,現(xiàn)在我們熟悉的遠(yuǎn)程調(diào)用湾戳,對象數(shù)據(jù)庫都不可能存在贤旷。
通過SerialVersionUID,也叫做流標(biāo)識(shí)符,即類的版本定義的砾脑,它可以顯式聲明也可以隱式聲明幼驶。顯式聲明格式如下:
private static final long serialVersionUID = XXXXXL;
而隱式聲明是我不聲明,你編譯器在編譯的時(shí)候幫我生成拦止。
JVM在反序列化時(shí)县遣,會(huì)比較數(shù)據(jù)流中的serialVersionUID與類的serialVersionUID是否相同,如果相同汹族,則認(rèn)為類沒有發(fā)生改變萧求;如果不相同,JVM不干了顶瞒,拋個(gè)異常InvalidClassException夸政。
顯式聲明serialVersionUID可以提高代碼的健壯性。
~~## 避免用序列化類在構(gòu)造函數(shù)中為不變量賦值
避免為final變量復(fù)雜賦值
使用序列化類的私有方法巧妙解決部分屬性持久化問題~~
break萬萬不可忘
case后缺少break往往會(huì)稱為bug所在榴徐。
可以再IDE中做相應(yīng)的設(shè)置守问,將switch語句中,每個(gè)case不以break結(jié)尾的情況設(shè)置為編譯錯(cuò)誤坑资。
易變業(yè)務(wù)使用腳本語言編寫
例如我們項(xiàng)目中的公式耗帕。
慎用動(dòng)態(tài)編譯
動(dòng)態(tài)編譯一致是java的夢想,從Java6版本它開始支持動(dòng)態(tài)編譯了袱贮,可以在運(yùn)行期直接編譯.java文件仿便,執(zhí)行.class,并且能夠獲得相關(guān)的輸入輸出,甚至還能監(jiān)聽相關(guān)的事件嗽仪。
使用動(dòng)態(tài)編譯時(shí)需要注意以下幾點(diǎn):
1.在框架中謹(jǐn)慎使用
2.不要再要求高性能的項(xiàng)目使用
3.動(dòng)態(tài)編譯要考慮安全性問題
4.記錄動(dòng)態(tài)編譯過程荒勇。(空中編譯和運(yùn)行是很讓人不放心的,留下些一句可以更好地優(yōu)化程序)
避免instanceof非預(yù)期結(jié)果
public class Client {
public static void main(String[] args) {
//String對象是否是Object的實(shí)例
boolean b1 = "Sting" instanceof Object;
//String對象是否是String的實(shí)例
boolean b2 = new String() instanceof String;
//Object對象是否是String的實(shí)例
boolean b3 = new Object() instanceof String;
//拆箱類型是否是裝箱類型的實(shí)例
boolean b4 = 'A' instanceof Character;
//空對象是否是String的實(shí)例
boolean b5 = null instanceof String;
// 類型轉(zhuǎn)換后的空對象是否是String的實(shí)例
boolean b6 = (String) null instanceof String;
// Date對象是否是String的實(shí)例
boolean b7 = new Date() instanceof String;
// 在泛型類中判斷String對象是否是Date的實(shí)例
boolean b8 = new GenericClass<String>().isDateInstance("");
}
}
class GenericClass<T> {
//判斷是否是Date類型
public boolean isDateInstance(T t) {
return t instanceof Date;
}
}
'A' instanceof String無法編譯通過闻坚,因?yàn)?A'是一個(gè)char類型沽翔,也就是一個(gè)基本類型仅偎,不是一個(gè)對象,instanceof只能用于對象的判斷威恼,不能用于基本類型的判斷箫措。
不要只替換一個(gè)類
對于final修飾的基本類型和String類型,編譯器會(huì)認(rèn)為它是穩(wěn)定態(tài)(Immutable Status),所以在編譯時(shí)就直接把值變異到字節(jié)碼中了,編碼了在運(yùn)行期引用驾锰,以提高代碼的執(zhí)行效率。
對于final修飾的類(即非基本類型赏酥,編譯器認(rèn)為它是不穩(wěn)定態(tài)(Mutable Status))搬素,在編譯時(shí)簡歷的則是引用關(guān)系蔗蹋,如果Client類引入的常量是一個(gè)類或?qū)嵗词共恢匦戮幾g也會(huì)輸出最新值戒傻。
注意:發(fā)布應(yīng)用系統(tǒng)時(shí)禁止使用類文件替換方式艺挪,整個(gè)WAR包或者JAR包發(fā)布才是萬全之策口蝠。