1. 程式人生 > 其它 >JAVA新手入門01

JAVA新手入門01

技術標籤:Javajava

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/

全文完。