Java編程中的一些常見問題匯總
本文列舉了我在周圍同事的Java代碼中看到的一些比較典型的錯(cuò)誤。顯然,靜態(tài)代碼分析(我們團(tuán)隊(duì)用的是qulice)不可能發(fā)現(xiàn)所有的問題,這也是為什么我要在這里列出它們的原因。
如果你覺得少了什么,請(qǐng)不吝賜教,我會(huì)很樂意把它們加上。
下面列出的所有這些錯(cuò)誤基本都與面向?qū)ο缶幊逃嘘P(guān),尤其是Java的OOP。
類名
讀下這篇短文“什么是對(duì)象”。類應(yīng)該是真實(shí)生活中的一個(gè)抽象實(shí)體,而不是什么“validators”,“controller”, “managers”這些東西。如果你的類名以”er”結(jié)尾的話——那它就是個(gè)糟糕的設(shè)計(jì)。
當(dāng)然了,工具類也是反模式,比如說Apache的StringUtils, FileUtils, 以及IOUtils。上面這些都是糟糕設(shè)計(jì)的代表。延伸閱讀:OOP中工具類的替代方案。
當(dāng)然,不要使用前綴或者后綴來區(qū)分類和接口。比方說,這些名字就是錯(cuò)誤的:IRecord, IfaceEmployee, 或者RecordInterface。通常來說,接口名應(yīng)該是真實(shí)生活中的實(shí)體的名字,類名應(yīng)該可以說明它的實(shí)現(xiàn)細(xì)節(jié)。如果這個(gè)實(shí)現(xiàn)沒有什么特別可說明的,可以把它叫作Default, Simple或者類似的什么。比如說:
class SimpleUser implements User {};
class DefaultRecord implements Record {};
class Suffixed implements Name {};
class Validated implements Content {};
方法名
方法可以返回值也可以返回void。如果方法返回值的話,它的名字應(yīng)該能說明它返回了什么,比如說(永遠(yuǎn)也不要使用get前綴):
boolean isValid(String name);
String content();
int ageOf(File file);
如果它返回void,那么它的名字應(yīng)該要說明它做了什么。比如:
void save(File file);
void process(Work work);
void append(File file, String line);
剛才提到的這些規(guī)則只有一個(gè)例外——JUnit的test方法不算。下面將會(huì)說到這個(gè)。
test方法的名字
在JUnit的測(cè)試用例中,方法名應(yīng)該是沒有空格的英文語句。用一個(gè)例子來說明會(huì)更清楚一些:
/**
* HttpRequest can return its content in Unicode.
* @throws Exception If test fails
*/
public void returnsItsContentInUnicode() throws Exception {
}
你的JavaDoc里的第一句話的開頭應(yīng)該是你要測(cè)試的那個(gè)類的名字,然后是一個(gè)can。因此,你的第一句話應(yīng)該是類似于“somebody can do something”。
方法名也是一樣的,只是沒有主題而已。如果我在方法名中間加一個(gè)主題的話,我就能得到一個(gè)完整的句子,正如上面那個(gè)例子中那樣:“HttpRequest returns its content in unicode”。
請(qǐng)注意test方法的名字是不以can開頭的。只有JavaDoc里的的注釋會(huì)以can開頭。除此之外,方法名不應(yīng)該以動(dòng)詞開頭。
實(shí)踐中最好將測(cè)試方法聲明為拋出Exception的。
變量名
避免組合的變量名,比如說timeOfDay, firstItem,或者h(yuǎn)ttpRequest。類變量及方法內(nèi)的變量都是如此。變量名應(yīng)該足夠長,避免在它的可見作用域內(nèi)產(chǎn)生歧義,但是如果可以的話也不要太長。名字應(yīng)該是單數(shù)或復(fù)數(shù)形式的名詞,或者是一個(gè)適當(dāng)?shù)目s寫。比如:
List<String> names;
void sendThroughProxy(File file, Protocol proto);
private File content;
public HttpRequest request;
有的時(shí)候,如果構(gòu)造方法要將入?yún)⒈4娴揭粋€(gè)新初始化的對(duì)象中的時(shí)候,它的參數(shù)和類屬性的名字可能會(huì)沖突。這種情況,我建議是去掉元音,使用縮寫。
示例:
public class Message {
private String recipient;
public Message(String rcpt) {
this.recipient = rcpt;
}
}
很多時(shí)候,看一下變量的類名就知道變量該取什么名字了。就用它的小寫形式就好了,像這樣就很靠譜:
File file;
User user;
Branch branch;
然而,基礎(chǔ)類型的話,永遠(yuǎn)不要這么做,比如Integer number或者String string。
如果存在多個(gè)不同性質(zhì)的變量的話,可以考慮下使用形容詞。比如:
String contact(String left, String right);
構(gòu)造方法
不考慮異常的話,應(yīng)該只有一個(gè)構(gòu)造方法用來將數(shù)據(jù)存儲(chǔ)到對(duì)象變量中。其它構(gòu)造方法則使用不同的參數(shù)來調(diào)用這個(gè)構(gòu)造方法。比如說:
public class Server {
private String address;
public Server(String uri) {
this.address = uri;
}
public Server(URI uri) {
this(uri.toString());
}
}
一次性變量
無論如何都應(yīng)該避免使用一次性變量。這里我所說的“一次性“指的是只使用一次的變量。比如下面這個(gè):
String name = "data.txt";
return new File(name);
上述的變量只會(huì)使用一次,因此這段代碼可以重構(gòu)成這樣:
return new File("data.txt");
有的時(shí)候,比較罕見的情況中——主要是為了格式更好看些——可能會(huì)用到一次性變量。然而,還是應(yīng)當(dāng)盡量避免這種情況。
異常
毋庸贅言,永遠(yuǎn)不要自己吞掉異常,而是應(yīng)該當(dāng)它盡量往上傳遞。私有方法應(yīng)該始終把受檢查異常往外面拋。
不要使用異常來進(jìn)行流程控制。比方說下面這段代碼就是錯(cuò)誤的:
int size;
try {
size = this.fileSize();
} catch (IOException ex) {
size = 0;
}
那如果IOException提示“磁盤已滿”的話該怎么辦?你還會(huì)認(rèn)為這個(gè)文件大小為0,然后繼續(xù)往下處理?
縮進(jìn)
關(guān)于縮進(jìn),主要的規(guī)則就是左括號(hào)要么在該行的末尾,要么就在同一行上閉合(對(duì)于右括號(hào)來說則相反)。比如說,下面這個(gè)就不正確,因?yàn)榈谝粋€(gè)左括號(hào)沒有在同一行上閉合,而它后面還有別的字符。第二個(gè)括號(hào)也有問題,因?yàn)樗懊嬗凶址?,但?duì)應(yīng)的開括號(hào)又沒在同一行上:
final File file = new File(directory,
"file.txt");
正確的縮進(jìn)應(yīng)該是這樣的:
StringUtils.join(
Arrays.asList(
"first line",
"second line",
StringUtils.join(
Arrays.asList("a", "b")
)
),
"separator"
);
關(guān)于縮進(jìn),第二條重要的規(guī)則就是同時(shí)一行中應(yīng)該盡量多寫一些——上限是80個(gè)字符。上面的那個(gè)例子并不滿足這點(diǎn),它還可以收縮一下:
StringUtils.join(
Arrays.asList(
"first line", "second line",
StringUtils.join(Arrays.asList("a", "b"))
),
"separator"
);
多余的常量
當(dāng)你希望在類的方法中共享信息的時(shí)候,應(yīng)當(dāng)使用類常量,這些信息應(yīng)該是你這個(gè)類所特有的。不要把常量當(dāng)作字符串或數(shù)值字面量的替代品來使用——這是非常糟糕的實(shí)踐方式,它會(huì)對(duì)代碼造成污染。常量(正如OOP中的任何對(duì)象一樣)應(yīng)當(dāng)在真實(shí)世界中有它自己的含義??聪逻@些常量在真實(shí)生活中的意思是什么:
class Document {
private static final String D_LETTER = "D"; // bad practice
private static final String EXTENSION = ".doc"; // good practice
}
另一個(gè)常見的錯(cuò)誤就是在單元測(cè)試中使用常量來避免測(cè)試方法中出現(xiàn)冗余的字符串或者數(shù)值的字面量。不要這么做!每個(gè)測(cè)試方法都應(yīng)該有自己專屬的輸入值。
在每個(gè)新的測(cè)試方法中使用新的文本或者數(shù)值。它們是相互獨(dú)立的。那么為什么它們還要共享同樣的輸入常量呢?
測(cè)試數(shù)據(jù)耦合
下面是測(cè)試方法中數(shù)據(jù)耦合的一個(gè)例子:
User user = new User("Jeff");
// maybe some other code here
MatcherAssert.assertThat(user.name(), Matchers.equalTo("Jeff"));
最后一行中,”Jeff”和第一行中的同一個(gè)字符串字面值發(fā)生了耦合。如果過了幾個(gè)月,有人想把第三行這個(gè)值換一下,那么他還得花時(shí)間找出同一個(gè)方法中哪里也使用了這個(gè)”Jeff”。
為了避免這種情況,你最好還是引入一個(gè)變量。
相關(guān)文章
Spring中數(shù)據(jù)訪問對(duì)象Data Access Object的介紹
今天小編就為大家分享一篇關(guān)于Spring中數(shù)據(jù)訪問對(duì)象Data Access Object的介紹,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-01-01SpringBoot配置Actuator組件,實(shí)現(xiàn)系統(tǒng)監(jiān)控
在生產(chǎn)環(huán)境中,需要實(shí)時(shí)或定期監(jiān)控服務(wù)的可用性。Spring Boot的actuator(健康監(jiān)控)功能提供了很多監(jiān)控所需的接口,可以對(duì)應(yīng)用系統(tǒng)進(jìn)行配置查看、相關(guān)功能統(tǒng)計(jì)等。2021-06-06SwiftUI中級(jí)List如何添加新內(nèi)容(2020年教程)
這篇文章主要介紹了SwiftUI中級(jí)List如何添加新內(nèi)容,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01Feign如何實(shí)現(xiàn)第三方的HTTP請(qǐng)求
這篇文章主要介紹了Feign如何實(shí)現(xiàn)第三方的HTTP請(qǐng)求,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10