尚學堂【官網】-西安Java培訓|c++培訓|Android培訓|安卓培訓|java視頻教程|軟件工程師|-西安雁塔尚學堂計算機學校
常見問題  尚學堂首頁新聞公告常見問題

如何寫出更好的Java代碼

www.zyopwk.live 發布人:java  |  來自:本站  |  發布時間:2019-06-01 17:44:00  |  點擊次數:653
 編碼風格

傳統的Java編碼方式是非常啰嗦的企業級JavaBean的風格。新的風格更簡潔準確,對眼睛也更好。

 

 

結構體

我們這些碼農干的最簡單的事情就是傳遞數據了。傳統的方式就是定義一個JavaBean:


 

public class DataHolder {
   public final String data;

   public DataHolder(String data) {
       this.data = data;
   }
}  

 

這不僅拖沓而且浪費。盡管你的IDE可以自動地生成這個,但這還是浪費。因此,不要這么寫。

 

相反的,我更喜歡C的結構體的風格,寫出來的類只是包裝數據:

 

public class DataHolder {
    public final String data;
    public DataHolder(String data) {
        this.data = data;
    }
}


這樣寫減少了一半的代碼。不僅如此,除非你繼承它,不然這個類是不可變的,由于它是不可變的,因此推斷它的值就簡單多了。

 

如果你存儲的是Map或者List這些可以容易被修改的數據,你可以使用ImmutableMap或者ImmutableList,這個在不可變性這節中會有討論。

 

Builder模式

如果你有一個相對復雜的對象,可以考慮下Builder模式。

 

你在對象里邊創建一個子類,用來構造你的這個對象。它使用的是可修改的狀態,但一旦你調用了build方法,它會生成一個不可變對象。

 

想象一下我們有一個非常復雜的對象DataHolder。它的構造器看起來應該是這樣的:

 

public class ComplicatedDataHolder {
    public final String data;
    public final int num;
    // lots more fields and a constructor

    public class Builder {
        private String data;
        private int num;

    public Builder data(String data) {
        this.data = data;
        return this;
    }
    public Builder num(int num) {
        this.num = num;
        return this;
    }
    public ComplicatedDataHolder build() {
        return new ComplicatedDataHolder(data, num);
        // etc
    }
    }
}


現在你可以使用它了:


 

final ComplicatedDataHolder 
cdh = new ComplicatedDataHolder.Builder()
    .data("set this")
    .num(523)
    .build();


關于Builder的使用這里還有些更好的例子,我這里舉的例子只是想讓你大概感受一下。當然這會產生許多我們希望避免的樣板代碼,不過好處就是你有了一個不可變對象以及一個連貫接口。

 

依賴注入

這更像是一個軟件工程的章節而不是Java的,寫出可測的軟件的一個最佳方式就是使用依賴注入(Dependency injection,DI)。由于Java強烈鼓勵使用面向對象設計 ,因此想寫出可測性強的軟件,你需要使用DI。

 

在Java中,這個通常都是用Spring框架來完成的。它有一個基于XML配置的綁定方式,并且仍然相當流行。


重要的一點是你不要因為它的基于XML的配置格式而過度使用它了。在XML中應該沒有任何的邏輯和控制結構。它只應該是依賴注入。

 

還有一個不錯的方式是使用Dagger庫以及Google的Guice。它們并沒有使用Spring的XML配置文件的格式,而是將注入的邏輯放到了注解和代碼里。

 

避免null值

如果有可能的話盡量避免使用null值。你可以返回一個空的集合,但不要返回null集合。如果你準備使用null的話,考慮一下@Nullable注解。IntelliJ IDEA對于@Nullable注解有內建的支持。

 

如果你使用的是Java 8的話,可以考慮下新的Optional類型。如果一個值可能存在也可能不存在,把它封裝到Optional類里面,就像這樣:

 

public class FooWidget {
    private final String data;
    private final Optional<Bar> bar;
   
        public FooWidget(String data) {
            this(data, Optional.empty());
        }
        public FooWidget(String data, Optional<Bar> bar) {
            this.data = data;
            this.bar = bar;
        }
        public Optional<Bar> getBar() {
            return bar;
        }
}


現在問題就清楚了,data是不會為null的,而bar可能為空。Optional類有一些像isPresent這樣的方法,這讓它感覺跟檢查null沒什么區別。

 

不過有了它你可以寫出這樣的語句:

final Optional<FooWidget> 
fooWidget = maybeGetFooWidget();
final Baz baz = fooWidget.flatMap(FooWidget::getBar)
                         .flatMap(BarWidget::getBaz)
                         .orElse(defaultBaz);  

 

這比使用if來檢查null好多了。唯一的缺點就是標準類庫中對Optional的支持并不是很好,因此你還是需要對null進行檢查的。

 

不可變

變量,類,集合,這些都應該是不可變的,除非你有更好的理由它們的確需要進行修改。

 

變量可以通過final來設置成不可變的:

 

final FooWidget fooWidget;
   if (condition()) {
       fooWidget = getWidget();
   } else {
       try {
           fooWidget = cachedFooWidget.get();
       } catch (CachingException e) {
           log.error("Couldn't get cached value", e);
           throw e;  
       }
    } // fooWidget is guaranteed to be set here  

 

現在你可以確認fooWidget不會不小心被重新賦值了。final關鍵字可以和if/else塊以及try/catch塊配合使用。當然了,如果fooWidget對象不是不可變的,你也可以很容易地對它進行修改。

 

有可能的話,集合都應該盡量使用Guava的ImmutableMap, ImmutableList, or ImmutableSet類。這些類都有自己的構造器,你可以動態的創建它們,然后將它們設置成不可變的。

 

要使一個類不可變,你可以將它的字段聲明成不可變的(設置成final)。你也可以把類自身也設置成final的這樣它就不能被擴展并且修改了,當然這是可選的。

 

避免大量的工具類

如果你發現自己添加了許多方法到一個Util類里,你要注意了。

 

public class MiscUtil {
    public static String frobnicateString(String base, int times) {
    // ... etc
    }
    public static void throwIfCondition(boolean condition, String msg) {
    // ... etc
    }
}


這些類乍一看挺吸引人的,因為它們里面的這些方法不屬于任何一個地方。因此你以代碼重用之名將它們全都扔到這里了。

 

這么解決問題結果更糟。把它們放回它們原本屬于的地方吧,如果你確實有一些類似的常用方法,考慮下Java 8里接口的默認方法。并且由于它們是接口,你可以實現多個方法。

 

public interface Thrower {
    public void throwIfCondition(boolean condition, String msg) {
    // ...    
    }    
    public void throwAorB(Throwable a, Throwable b, boolean throwA) {  
    // ...  
    }
}

 

這樣需要使用它的類只需簡單的實現下這個接口就可以了。

 

格式

格式遠比許多程序員相像的要重要的多。一致的格式說明你關注自己的代碼或者對別人有所幫助?


是的。不過你先不要著急為了讓代碼整齊點而浪費一整天的時間在那給if塊加空格了。

 

如果你確實需要一份代碼格式規范,我強烈推薦Google的Java風格指南。這份指南最精彩的部分就是編程實踐這節了。非常值得一讀。

 

文檔

面向用戶的代碼編寫下文檔還是很重要的。這意味著你需要提供一些使用的示例,同時你的變量方法和類名都應該有適當的描述信息。


結論就是不要給不需要文檔的地方添加文檔。如果對于某個參數你沒什么可說的,或者它已經非常明顯了,別寫文檔了。模板化的文檔比沒有文檔更糟糕,因為它欺騙了你的用戶,讓他覺得這里有文檔。

 

Java 8有一個漂亮的流和lambda表達式的語法。你的代碼可以這么寫:

 

final List<String> 
filtered = list.stream()
    .filter(s -> s.startsWith("s"))    
    .map(s -> s.toUpperCase());


而不是這樣:

 

final List<String> 
filtered = Lists.newArrayList();
for (String str : list) {
    if (str.startsWith("s") {
        filtered.add(str.toUpperCase());
    }
}


這樣你能寫出更連貫的代碼,可讀性也更強。

 

 

部署

正確地部署Java程序還是需要點技巧的。現在部署Java代碼的主流方式有兩種 :使用框架或者使用自家摸索出來的解決方案,當然那樣更靈活。

 

框架

由于部署Java程序并不容易,因此才有了各種框架來用于部署。最好的兩個是Dropwizard以及Spring Boot。Play Framework也可以算是一個部署框架。


這些框架都試圖降低部署程序的門檻。如果你是一個Java的新手或者你需要快速把事情搞定的話,那么框架就派上用場了。單個jar的部署當然會比復雜的WAR或者EAR部署要更容易一些。

 

然而,這些框架的靈活性不夠,并且相當頑固,因此如果這些框架的開發人員給出的方式不太適合你的項目的話,你只能自己進行配置了。

當前文:如何寫出更好的Java代碼
上一頁:Java值類型和引用類型的區別
下一頁:程序員如何去快速定位bug
在線報名(*為必填項)
云南快乐十分开奖走势图