Java:異常作為控制流?大佬:避免!避免!避免
Java是一種通用的編程語言,它有著許多不同的方案去解決一些特殊的問題。然而,其中既有需要被遵循的最佳做法,也有一些仍被普遍使用的不良做法。
用異常(Exceptions)作為控制流就是這些常見的不良做法中的一種。要避免使用這一方法的原因有二:首先,這會降低單位時間內代碼的響應性能,同時代碼也會非常難讀懂。
通過下面這個例子來看看異常是怎樣被用作控制流的。這個代碼的商業用例是:
- publicstaticintfindAge(String name) {
- try {
- String ageAsString = findUser(name);
- return ageAsString.length();
- } catch (NameNotFoundException e) {
- return0;
- }
- }
- privatestaticStringfindUser(String name) {
- if(name==null) {
- thrownew NameNotFoundException();
- }
- return name;
- }
如果用戶提供了一個非空的名字給findAge 方法,那它會返回這個名字的長度。但是如果用戶名為空,那么findUser方法會拋出NameNotFoundException,而在這種情況下,findAge 方法會返回0。
那該如何重構這個代碼來避免異常呢?
老實說,有很多種辦法可以實現,這里就只介紹一種。
- publicstaticintfindAgeNoEx(String name) {
- String ageAsString = findUserNoEx(name);
- return ageAsString.length();
- }
- privatestaticStringfindUserNoEx(String name) {
- if(name==null) {
- return"";
- }
- return name;
- }
為了找到異常對代碼性能的影響,準備下面這個代碼,其中兩種實現都被執行了1000萬次,Intel Core i7-3630QM CPU上運行時,異常花費了上千毫秒。
- publicclassControlFlowWithExceptionOrNot {
- publicstaticclassNameNotFoundExceptionextends RuntimeException {
- privatestaticfinallong serialVersionUID = 3L;
- }
- privatestaticfinalint TRIAL = 10000000;
- publicstaticvoid main(String[] args) throws InterruptedException {
- long start = System.currentTimeMillis();
- for (int i = 0; i < TRIAL; i++) {
- findAgeNoEx(null);
- }
- System.out.println("Duration :" + (System.currentTimeMillis() - start));
- long start2 = System.currentTimeMillis();
- for (int i = 0; i < TRIAL; i++) {
- findAge(null);
- }
- System.out.println("Duration :" + (System.currentTimeMillis() - start2));
- };
- publicstaticint findAge(String name) {
- try {
- String ageAsString = findUser(name);
- return ageAsString.length();
- } catch (NameNotFoundException e) {
- return0;
- }
- }
- privatestaticString findUser(String name) {
- if (name == null) {
- thrownew NameNotFoundException();
- }
- return name;
- }
- publicstaticint findAgeNoEx(String name) {
- String ageAsString = findUserNoEx(name);
- return ageAsString.length();
- }
- privatestaticString findUserNoEx(String name) {
- if (name == null) {
- return"";
- }
- return name;
- }
- }
輸出:
- Duration :16
- Duration :6212
如果來比較一下這兩種findAge方法的可讀性,可以發現沒有異常的那個十分清晰,不管findUser方法返回什么,findAge方法都會得到它的長度,我們也很確信findUser方法會返回一個字符串。但是那個有異常的就會令人困惑,findUser方法返回不清晰,它可能返回一個字符串也可能拋出一個異常,而且這在方法的簽名上是不可見的。正因為這樣,函數式編程范式中不歡迎異常。
最后,在碰到真正的異常情況時再使用異常會比較好。如果你將異常用作控制流,這可能會導致性能問題,代碼的可讀性也會減弱。