暫無名待修改
前言
一六年,我參與了一個我有生以來的最高大尚的專案,我在專案中負責某功能計費模組的開發,儘管總個專案投入了很多人力,還有引進了很多先進的工具和管理方式,但那還是我有生以來見過最糟的專案之一
一七年,我去朋友的創業公司,幫他組建團隊,一九年至今年,我於一家物流公司擔任架構方面的工作,在開發中,我常見那種寫業務能力很強,但程式碼細節慘不忍睹的開發者,於是我想整理一些我見過的程式碼片斷,並提供一些改進意見
提示:以下是本篇文章正文內容,下面案例可供參考
一.error日誌
對異常catch後的處理已經是老生常談的話題了,不能什麼都不做,也不能用e.printStackTrace()直接列印。下面的程式碼是用slf4j列印日誌:
try{
.....
}catch(XXException e){
logger.error("send data to XX failed: {}",e);
}
上面面程式碼看起來沒問題吧,但其實上面的日誌在很多情況下並不能列印出正常的堆疊資訊,在日誌中用到佔位符是受到了其它級別日誌的影響。 我們可以看一下文件中error幾個方法的定義:
/**
* Log a message at the ERROR level.
*
* @param msg the message string to be logged
*/
public void error(String msg);
/**
* Log a message at the ERROR level according to the specified format
* and argument.
* <p/>
* <p>This form avoids superfluous object creation when the logger
* is disabled for the ERROR level. </p>
*
* @param format the format string
* @param arg the argument
*/
public void error(String format, Object arg);
/**
* Log a message at the ERROR level according to the specified format
* and arguments.
* <p/>
* <p>This form avoids superfluous object creation when the logger
* is disabled for the ERROR level. </p>
*
* @param format the format string
* @param arg1 the first argument
* @param arg2 the second argument
*/
public void error(String format, Object arg1, Object arg2);
/**
* Log a message at the ERROR level according to the specified format
* and arguments.
* <p/>
* <p>This form avoids superfluous string concatenation when the logger
* is disabled for the ERROR level. However, this variant incurs the hidden
* (and relatively small) cost of creating an <code>Object[]</code> before invoking the method,
* even if this logger is disabled for ERROR. The variants taking
* {@link #error(String, Object) one} and {@link #error(String, Object, Object) two}
* arguments exist solely in order to avoid this hidden cost.</p>
*
* @param format the format string
* @param arguments a list of 3 or more arguments
*/
public void error(String format, Object... arguments); //(1)適用於佔位符,
/**
* Log an exception (throwable) at the ERROR level with an
* accompanying message.
*
* @param msg the message accompanying the exception
* @param t the exception (throwable) to log
*/
public void error(String msg, Throwable t); //(2)天生就有列印異常資訊的本事
可以看到error也有多參呼叫(1),所以用佔位符也沒問題,但在呼叫時,會呼叫引數的toString方法,我看到很多error日誌後面出現了一串莫名奇妙的記憶體地址,為了正確列印堆疊資訊,竟然大動干戈,弄了一個工具類,返回error的堆疊資訊。
大可不必如此,eror有一個過載方法天生就是用來列印Exception資訊,見2。但呼叫時我們不應該在msg欄位中出現佔位符。
所以只需要:
logger.error("send data to XX failed: {}",e)
二 裝箱與拆箱
void funa(Boolean flag){
if(flag){
....
}
}
上面的程式碼看上去非常ok。當flag為null時,會報空指標異常。
某天夜裡,我接到公司的緊急電話,公司裡的pda掃碼上傳出現異常,而PDA(Android)的程式碼也從沒有人動過。我追蹤程式碼到錯誤處,以下是程式碼片斷。
List<Scan> list = .... //這是呼叫服務得到的資料
for(Scan s : list){
SaveEndtity e = new SaveEntity();
e.setCout(s.getCount); //這裡出現了異常
....
}
這是一個最簡單的拷貝操作,如果不支看兩個類的程式碼,你怎麼也想不到問題會出在哪裡。
class SaveEndtity{
int count;
}
class Scan{
Integer count;
}
僅僅是兩個類,一個用了原始型別,一個用了包裝型別,就在特定條件下,產生了一場災難。
提示:上面的兩個示例是我在上家工作時發現很多程式碼中存在的bug.以下我所舉的示例不能算是bug,但它確實讓我不爽
三 沒完沒了的字串
interface UserService{
.....
List<User> queryByAge(String age);
String getTotalCount(...)
List<> query(String pageSize,String pageNo)
}
我曾在一家名企見過很多類似的程式碼,當我在上家公司見到更多的是這樣
String userStr = restTemplate.get("http://XXX/getUser?id="+id,String.class)
User u = JsonObject.readValue(userString.User.class);
....
andoid端可能有如下東西
inteface UserService{
@Get("/getUser")
Observerable<String> find(String userName); //宣告式遠端介面呼叫
}
//使用時
userService.get("wzp").subscribe((userStr)->{
User u = Json.readObject(userStr,User.class);
...
})
很多高階語言的程式設計師都非常迷戀字串,覺得字串就是erverything。恨不得把類裡的所有欄位型別都設成字串,或資料庫裡的所有欄位都統一用varchar。或者有些程式設計師,必需通過字串才能找到安全感。這真悲哀。
四 過多的巢狀
if(a==b){
if(check()){
doThings();
}
}else{
if(c == d){
doThings();//與上面的doThings是同一個方法
}
}
以上程式碼包括括號總共8行,但它其實與下面三行程式碼等價:
if((a==b && check()) || c==d){
doThings();
}
離散數學中的什麼條件推導的我幾乎忘了,但我想有時候我們可以不用高深的理論我們也可以寫出比較優雅的程式碼。只要我們留心就行了。
以下程式碼片斷我在code review時經常見到
Result login(String userName,String password){
if(StringUtils.isBlank(userName)){
throw new LongException();
}else if(StringUtils.isBlank(password)){
throw new LongException();;
}else{
User user= dao.finduser(username)
if(user == null){
return new result(false);
}else{
....
}
}
}
以上程式碼還是比較簡單的,還有比這更復雜的。我曾見過有人在方法裡面拋了個異常,然又在同一個方法裡try這個異常,我看的是一頭霧水,都不知道目的何在。其實寫程式就像是寫作文,要掌握主幹,如果我們圍繞主幹去寫,然後再添枝加葉或許會更好一些,比如一寫程式碼主要功能是查詢使用者並檢查使用者名稱密碼是正正確然後返回結果。
第一步:
Result login(String userName,String password){
Result r =new Result();
return r;
}
第二 步:
Result login(String userName,String password){
Result r =new Result();
User user = dao.queryUser(userName)
if(user== null){
r.setRsult(fale)
}else if(!checkpassword)){
r.setResult(false)
}else (r.setResult(true))
return r;
}
第三 步:
Result login(String userName,String password){
if(StringUtils.isBlank(userName)){
throw new LongException();
}
if(StringUtils.isBlank(password)){
throw new LongException();;
}
Result r =new Result();
User user = dao.queryUser(userName)
if(user== null){
r.setRsult(fale)
}else if(!checkpassword)){
r.setResult(false)
}else {
r.setResult(true)
}
return r;
}
我並沒有用線性的方式增編寫程式碼,我更喜歡首先關注業務主體,然後再去新增細節。而且我也少用了很多巢狀。在if中已丟擲了異常,else 就沒有必要使用了。我們的主要業務邏輯沒有巢狀在某個if中,這樣我們可以把驗證移到其它方法中了。
五 亂拷貝
六 引用了不該引用的jar包。
相關文章
- 簽名修改
- centOS修改主機名,重啟後無效解決CentOS
- 檔名提取、修改
- 批次修改副檔名
- java批量修改檔名Java
- Git修改檔名稱Git
- Android Studio修改專案名和包名Android
- MAC修改主機名、計算機名Mac計算機
- win10怎麼修改待機背景_win10更改待機桌布的方法Win10
- Windows修改新建.txt檔名Windows
- ubuntu修改主機名稱Ubuntu
- sqlserver修改例項名稱SQLServer
- linux主機名的修改Linux
- [20220329]批量修改檔名.txt
- Fedora網路介面名稱如何修改?Fedora修改網路介面名稱的方法
- Springboot 修改包名之後,報"錯誤: 找不到或無法載入主類"Spring Boot
- TP-Link AC1900系列無線路由器修改無線名稱、密碼的方法教程路由器密碼
- linux 檢視修改主機名Linux
- ***批次修改資料夾名稱
- webpack打包時如何修改檔名Web
- CentOS 7 下修改主機名CentOS
- 簡單修改Linux主機名Linux
- WPS 修改新建office 檔名稱
- 如何用Python批次修改檔名?Python
- CentOS 8 修改網路卡名稱CentOS
- Linux rename命令批量修改檔名Linux
- nid修改資料庫名稱資料庫
- git修改檔案的名稱Git
- 潤物無聲因摯愛,育人無痕待花開
- Linux修改主機名(靜態主機名、臨時主機名)Linux
- win10如何修改檔案字尾名 win10修改字尾名的方法Win10
- 無名前端庫前端
- 7628 EDCCA認證暫存器修改(認證自適應)
- 如何修改mac 下主機名、電腦名、區域網主機名Mac
- 208道面試題(JVM部分暫無答案)面試題JVM
- win10批量修改檔名的步驟_win10如何批量修改檔名Win10
- win10批次修改檔名的步驟_win10如何批次修改檔名Win10
- 修改labelme標註的標籤名