【Java 程式設計】Java 開發實踐與經驗:BetterJava
文章目錄
原文: https://www.seancassidy.me/better-java.html
Java 當然是一門很好的程式語言,它非常流行,但也不可否認比較難用,冗長、笨拙、刻板,這是用 Java 程式設計時常會有的體驗。Java 8 帶來了很多新特性,讓程式設計師可以寫出更簡潔、流暢的 Java 程式碼。
1. 風格
傳統的 JavaBean 風格的程式碼讓程式顯得拖沓、煩冗!
比如:
public class DataHolder {
private String data;
public DataHolder() {
}
public void setData(String data) {
this.data = data;
}
public String getData() {
return this.data;
}
}
這樣的程式碼好嗎?
好的程式碼應該是讓複雜的事情變簡單,而不是讓簡單的事情變複雜。儘管大部分程式碼都可以通過 IDE 自動生成,但這種程式碼資訊密度太低,極大的影響可讀性。So, Don’t do this!
這時,可以嘗試 C 結構體風格的 Java 類:
public class DataHolder {
public final String data;
public DataHolder(String data) {
this.data = data;
}
}
- 刪除掉多餘的 setter,getter 方法
- 預設為 immutable,簡化邏輯
對於複雜的物件,可以使用建造者模式
來構建,例如:
public class ComplicatedDataHolder {
public final String data;
public final int num;
// lots more fields and a constructor
public static 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
}
}
}
- Builder 是一個內部類,用於構建物件;
- Builder 是 mutable 的;
- 當呼叫 build() 方法時,會產生一個 immutable ComplicatedDataHolder 類例項。
使用示例:
final ComplicatedDataHolder cdh = new ComplicatedDataHolder.Builder()
.data("set this")
.num(523)
.build();
這是構建者模式的一種例項,通過它可以讓程式碼顯得更加流暢、可讀。
2. 依賴注入
依賴注入(Dependency injection)讓軟體具有更好的可測性,在面向物件的設計與開發中廣泛應用。
Java 可以使用 Spring 框架提供的 DI 功能。注入的方式有三種:xml 配置檔案,註解,編碼。
除了 Spring,也可以考慮使用 Dagger 或 Guice。
3. 避免使用 null
儘可能的避免使用 null,也不要在方法中返回 null。必須使用 null 的場景,需新增 @Nullable 註解。
Java 8 提供了 Optional 型別,當一個值可能為 null 時,用 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;
}
}
這樣的話,返回結果就永遠不會是 null 了,Optional 有類似 isPresent 的方法,可以編寫如下所示的程式碼:
final Optional<FooWidget> fooWidget = maybeGetFooWidget();
final Baz baz = fooWidget.flatMap(FooWidget::getBar)
.flatMap(BarWidget::getBaz)
.orElse(defaultBaz);
這樣就不用去寫一連串的 check null 程式碼了。
目前,標準庫對 Optional 的支援還不夠完善,可以考慮使用 guava 等三方庫提供的 Optional 特性。
4. Immutable-by-default
變數、類、集合預設應該為 immutable(不可編輯),除非有特殊需要。
變數和類物件可以通過 final 宣告為 immutable。
集合可以使用 Guava 提供的 ImmutableMap,ImmutableList,或者 ImmutableSet 類。它們都有自己的 Builder 類,用來動態構建,最後呼叫 builder() 方法返回構建的 Immutable 集合例項。
5. 介面預設方法代替各種 util 類
專案中常常會有各種各樣的 util 類,有時候用著挺方便,但這並不是一種良好的開發習慣。
Java 8 中的介面可以定義 default 方法,可以試著將通用方法放在介面中,這樣任何想使用這些方法的類都可以通過實現介面的方式獲得。
public interface Thrower {
default void throwIfCondition(boolean condition, String msg) {
// ...
}
default void throwAorB(Throwable a, Throwable b, boolean throwA) {
// ...
}
}
6. Streams
Java 8 提供了 stream 和 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());
}
}
是不是清爽了很多,又可以愉快的程式設計了!
7. 部署
框架(Framework)讓 Java 應用的部署變得簡單,比如 Dropwizard 和 Spring Boot,除此之外,還有 Play framework,也可以考慮。
這些框架就是為了降低部署程式的複雜度,讓程式變得開箱即用。對於新手來說,使用框架能讓其專注於程式設計本身;對於專案來說,使用框架可以實現快速構建!
Jar 的部署方式,比 War 或 Ear 更簡單。
當然,框架只能保證通用性需求,如果專案需要有特殊的部署需求的話,則需要進行定製。
8. Maven
https://books.sonatype.com/mvnex-book/reference/index.html
Maven 依然是構建、打包、測試 Java 程式的標準工具!如果你是新手,你應該從 Maven 開始。同時 Gradle 作為後起之秀,也有它的優點。
Maven repository,可以使用 Artifactory 或 Nexus。
9. 持續整合
專案開發越來越敏捷化,因此持續整合也就變得越來越重要。
Jenkins 和 Travis-CI 是必然選擇。
程式碼覆蓋率很有用,可以考慮 Cobertura 提供的 Maven 外掛。
10. 自動化管理
Chef,Puppet,Ansible
11. 常用庫
Java 最大的優點就是擁有豐富的三方庫,下面列出的是一些比較常用的:
Apache Commons
- Commons Codec: 大量的 Base64/hex 字串的編解碼工具,不用自己去寫了
- Commons Lang: 字符集/字串的構建、操作,各種常用雜散工具
- Commons IO:FileUtils.copyDirectory, FileUtils.writeStringToFile, IOUtils.readLines 等等
Guava
guava 是 google 提供的 Java 通用庫,包含了很多重要但 JDK 沒有提供的特性。
- Cache:
- Immutable:
- Events
…
Gson
google 提供的 JSON 庫
final Gson gson = new Gson();
final String json = gson.toJson(fooWidget);
final FooWidget newFooWidget = gson.fromJson(json, FooWidget.class);
Java Tuples
Java 語言本身沒有 tuple 型別,可以使用 javatuples 庫,如下:
Pair<String, Integer> func(String input) {
// something...
return Pair.with(stringResult, intResult);
}
Joda-Time
最好的時間處理庫,Java 8 之前的專案可以考慮使用。
Lombok
一個有趣的庫,通過註解,可以編寫如下程式碼:
public class Foo {
@Getter @Setter private int var;
}
jOOQ
如果你不喜歡厚重的ORM框架,如Hibernate,可是試著用 jOOQ,使用它可以編寫如下風格的 SQL 程式碼:
// Typesafely execute the SQL statement directly with jOOQ
Result<Record3<String, String, String>> result =
create.select(BOOK.TITLE, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME)
.from(BOOK)
.join(AUTHOR)
.on(BOOK.AUTHOR_ID.equal(AUTHOR.ID))
.where(BOOK.PUBLISHED_IN.equal(1948))
.fetch();
既靈活、又簡潔!
Eclipse Memory Analyzer
記憶體分析工具,可以排查記憶體洩漏問題。
可以使用 jmap 來生成 heap dump 檔案:
$ jmap -dump:live,format=b,file=heapdump.hprof -F 8152
Attaching to process ID 8152, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 23.25-b01
Dumping heap to heapdump.hprof ...
... snip ...
Heap dump file created