JAVA新手入門01
JAVA新手入門01
這篇文章給新入門的程式設計師看的,高手就不要看了,對於剛開始參加工作,或者在大學學習了計算機的程式設計知識,不管是學的C語言,還是JAVA,還是C# 等等,都迫切的想要提高自己,這篇文章將結合本人多年的程式設計經驗,說一說新手在程式設計中,應當注意的一些事項。這些程式碼,可能工作了很多年的程式設計師還在寫,有可能你也在寫,有可能是你不得不這麼做,看完了這篇文章,相信你對程式 員為什麼寫出了屎山一樣的程式碼,就知道點原因了。這可能是一個沉重的話題,不該新手來承擔,每個程式設計師優不要隨意的寫if else,那麼程式碼就更好維護了。
你真的學會了if else 了嗎?
關注我的部落格,獲取更多Java程式設計知識: https://www.epoooll.com/
場景1,我們有1個變數的值type:
type =0 表示A種情況 type=1 表示另外一種情況,可能會有更多的情況,在不用switch的情況下,你會有以下2種寫法(java版本的,且省略了大括號,你們寫程式碼的時候,即使只有1行程式碼,也要寫{}):
推薦寫法:
if(type == 0)
do something;
else if(type == 1)
do something
throw new RuntimeException("型別錯誤,請檢查:" + type);
不推薦寫法:
if(type == 0)
do something
else
do something
這種寫法,在某一天,如果type的值增加了1個,測試的時候容易出現詭異的BUG,如果你不測試,直接上線了,那可能會更糟糕。
嚴格檢查型別的值,否則容易出錯,當type的型別是固定的幾個值的時候,即使是用switch case when實現,也要注意這種類似的錯誤,尤其是以後typ的值增加了,錯誤的寫法還不容易發現,儘量讓程式碼早報錯,可以避免不少BUG
核心思想:嚴格檢查型別的值,程式要早報錯
延伸 你對抽象的理解對嗎?
我們接著上文,假設有變數a b,a、b在不同的情況下,有不同的業務場景,例如:
a == 1 && b== 1 表示客戶現金支付
a == 1 && b == 2 表示客戶信用卡支付
推薦寫法:
if(a == 1 && b == 1) {
do something
}
if(a == 1 && b == 2) {
do something
}
不推薦寫法:
if(a == 1 && b == 1) {
do something
} else if( a == 1 && b == 2) {
do something
}
這2種寫法會有細微的區別,看起來功能是一樣的,在邏輯簡單的時候,幾乎一樣,當以後的邏輯更復雜的時候,會有不同,例如 增加了1個變數c,前面1種情況,可以更好的處理增加了c的這種情況, 後面的寫法,就需要慎重的考慮,c應該放在哪個位置,增加了else,測試的時候就複雜了很多。
一種更好的辦法是抽象邏輯
int type;
if(a == 1 && b == 1) {
type = 0; //客戶現金支付
}
if( a == 1 && b == 2) {
type = 1; //客戶信用卡支付
}
switch(type) {
case 0:
do something;
break;
case 1:
do something;
break;
default:
do somthing;
break;
}
這樣就能在眾多複雜的程式碼中,把業務發生程式碼的字首,和業務的實際處理邏輯,剝離開來,將程式碼抽象成2部分,1部分用來描述業務發生的條件,1部分用來描述業務條件觸發之後的邏輯。
上述的程式碼,只是為了演示使用,不能作為實際的支付邏輯的處理。
如果對上述程式碼,再進行修改的話:
if(a == 1 && b == 1) {
type = 0; //客戶現金支付
}
修改為:
if(cash(a,b)) {
type = 0; //客戶現金支付
}
這樣是不是程式碼更容易閱讀了呢? 我們是不是可以更進一步,把type的值用列舉來表示呢?
enum PAY_CHANNEL {
CASH,CREDIT_CARD;
}
PAY_CHANNEL channel = PAY_CHANNEL.CASH;
if(cash(a,b)) {
channel = PAY_CHANNEL.CASH;
}
if(credit_card(a,b)) {
channel = PAY_CHANNEL.CREDIT_CARD;
}
switch(channel) {
case PAY_CHANNEL.CASH:
do something;
break;
case PAY_CHANNEL.CREDIT_CARD:
do something;
break;
default:
do something;
break;
}
當然,要不要把一個if else寫到如此的複雜,就要看實際的需求的情況了。
還有一種比較容易出錯的地方是,只寫了if ,不寫else ,這種寫法,也會帶來一些隱蔽的錯誤,當然,並不是任何情況下,不寫else,都會出錯,只是寫程式碼的時候,要考慮下,到了else,我該怎麼辦? 所以:
if( cond ) {
do something
}
結果等同於
if( cond ) {
// cond == true
do something
} else {
// cond == false 時候,是不是真的什麼都不做
// do nothing
}
總是要確認,是不是真的在else中什麼都不做。
DEBUG 可以有一些技巧
在JAVA中,你可以定義一個測試類,叫DebugConstant,然後其中可以定義很多debug的常量資訊,例如:
public static boolean DEBUG = false;
public static int DEBUG_SKU = 12345; // 表示遇到該SKU時候,列印DEBUG日誌資訊
然後在業務程式碼中,寫:
if(DEBUG)
do something
這樣在單元測試中,就可以開啟DEBUG或者,你也可以設定Java的啟動引數 -DDEBUG=true 來啟動DEBUG資訊,當然,如果你學習的是C語言,那就可以使用條件編譯了,定義1個變數DEBUG,需要DEBUG的時候就開啟
需要debug的時候,直接
#define DEBUG 1
if(DEBUG) {
do something
}
核心思想: 善用條件編譯,來實現DEBUG
finally只用來釋放資源
看看這段程式碼會返回什麼?
try {
return true;
} finally {
return false;
}
實際會返回false,所以給我們的啟示就是finally中,不能返回值只可以用來做釋放資源(檔案流、鎖等)
謹慎修改常量的值
如果你修改了1個常量的值,一定要全域性搜尋所有使用這個常量的地方,仔細判斷這個修改,是否有副作用。需要注意的是,註解裡面不可以使用常量,一定要特別注意,尤其是mybatis的多資料來源配置。
思考下return的位置
在日常的開發中,經常會寫1個函式,返回1個值,在函式執行的過程中,會有各種各樣的判斷,各種情況,可能會中途跳出,如果是C語言,可以使用goto,如果是Java的話,就需要仔細思考下,把return的位置放在哪裡,接下來仔細研究,該怎麼做:
提前宣告返回值,並在最後返回。
這種方法是1種最常見的寫法,不容易出錯,而且程式碼容易看懂,也容易跟蹤變數的變化,如果業務十分複雜,難免會增加複雜度,但是不論如何增加複雜度,
在AB之間,都不要寫return就行了。
public int sum(int a,int b) {
int res = 0; // A
try {
res = a + b;
} catch(Exception e) {
res = -1; // 不要糾結這個-1,只是為了演示
}
return res; // B
}
上面的程式碼在穩定執行之後,需求有變化,需要判斷一些特殊值,而且這個業務,可能和這個方法沒有特別大的關係,當然,且不論是否應該修改sum方法,更好的辦法當然是在呼叫sum方法的地方,去處理特殊值,這裡我們假設,我們必須修改sum方法,可能是因為有很多的地方都呼叫了sum方法,導致如果不修改sum方法,而是去修改呼叫sum方法的地方,會修改很多處程式碼。
從現在開始,就有2種寫法了:
方法1(不推薦):
public int sum(int a,int b) {
int res = 0; // A
try {
if(a <= 0 ) {
return 2 * b;
}
res = a + b;
} catch(Exception e) {
res = -1;
}
return res; // B
}
方法2(推薦):
public int sum(int a,int b) {
int res = 0; // A
try {
if(a <= 0 ) {
res = 2 * b;
} else {
res = a + b;
}
} catch(Exception e) {
res = -1;
}
return res; // B
}
這2個方法是完全不同的編碼習慣,相信我,在你的程式設計生涯中,你會無時不刻的面臨這個選擇,所以需要慎重的考慮下,到底應該採用哪種方法來處理這種情況,接下來,我們稍微把剛才的例子,再增加一些變化,這裡我們選擇使用推薦的方法2來做變化,增加了1個需求,如果a = 15,那麼就固定的返回錯誤-1,該怎麼做呢?
方法1: 在方法開頭的地方直接判斷並返回
public int sum(int a,int b) {
// a == 15表示程式異常了,需要直接返回
if( a == 15 ) {
return -1;
}
int res = 0; // A
try {
if(a <= 0 ) {
res = 2 * b;
} else {
res = a + b;
}
} catch(Exception e) {
res = -1;
}
return res; // B
}
方法2: 在邏輯運算中增加1種情況
public int sum(int a,int b) {
int res = 0; // A
try {
if(a == 15 ) {
res = -1;
} else if( a <= 0 ) {
res = 2 * b;
} else {
res = a + b;
}
} catch(Exception e) {
res = -1;
}
return res; // B
}
現在,我們擁有了1個相對來說比較複雜的方法,有正常的業務邏輯,但是程式碼的量不多,有異常處理,有特殊業務的處理,這個方法已經比最開始的程式碼,複雜了不少,在實際的開發中,可能此時這個方法已經不是30行左右了,而是300行 600行了,恭喜你,你已經在製造所謂的“程式碼屎山”了,多年以後,當你再看到這段程式碼的時候,可能你也會在心裡狠狠的咒罵一句,那個XX寫的程式碼,然後看了程式碼的提交記錄,發現是自己寫的,默默的把它關閉了。也可能是某位已經離職/在職/升職的同事寫的,不過無論是誰寫的,這都是一個不美好的一天。
然後我們來思考以下對策,該怎麼處理,這裡我們仍然堅持最開始的初衷,AB之間不返回值(如果程式碼中途返回值會大大增加複雜度),此時需要處理的是,判斷哪些是異常情況,哪些是正常情況,也就是說,對引數進行檢查,是可以方法的開頭的,這並不影響:
if(a == 15) {
return -1;
}
或者是
if(checkParam(a,b) == false) {
return -1;
}
可以在方法的開頭,增加引數的有效性的檢查,同時,要把各種業務情況抽象出來,走不同的分支,具體的做法,可以參考本文開頭的if else的部分,總之,不能在方法的中間隨意的return,但這也僅僅是一家之言,如果不同意這個看法,也可以不考慮這個準則。
結論: 儘量不在方法體中間return
關注我的部落格,獲取更多Java程式設計知識: https://www.epoooll.com/
全文完。