Java隨談(三)如何創建好一個對象?
本文推薦閱讀時間30分鐘
大家都知道,在編寫Java程式里,一般就是處理各種各樣的對象,那麼,你知道一共有多少種創建對象的方式嗎?
希望大家能稍微思考一下再往下翻。
答案是4種
- new 一個對象
- 反射一個類實例化一個對象(反射類和反射構造方法)
- clone 一個對象
- 反序列化一個對象
前兩者調用了構造方法,後兩者沒有調用。
其中,日常使用中,最為常見的是使用new關鍵字創建對象;而在框架之中,最常用的是反射來控制對象生成。
下面詳細地梳理常見的new對象的邏輯
- 若編寫一個新的類,此時沒有任何額外的需求時,採用最簡單的構造方法即可。
- 若對象屬性很多,但其中必傳屬性不多,可以使用set設置屬性,或者重疊構造器模式。
- 若對象的屬性和方法的傳參類似,可以使用org.springframework.beans.BeanUtils.copyProperties()方法搭配set方法來設置參數。
- 若對象需要有一些特殊功能,比如單例,能夠快取等,可以使用靜態工廠方法。
- 若對象需要一次性構建(創建不可變對象),使用建造者模式。
- 若對象為底層資源,會被各種方法依賴,使用依賴注入。
1.1 Java原生構造方法,和類同名即可
public class Order { private String code; private List<String> offers; private Map<String, Object> features; public Order() { } public Order(String code, List<String> offers, Map<String, Object> features) { this.code = code; this.offers = offers; this.features = features; } }
2.1 重疊構造器模式
public class Order { // not null! private String code; private List<String> offers; private Map<String, Object> features; //傳入code, offers設為空集合, features設為空表 public Order(String code) { this(code, new ArrayList<>(), new HashMap<>()); } //傳入code, offers, features設為空表 public Order(String code, List<String> offers) { this(code, offers, new HashMap<>()); } //傳入code,features,offers設為空集合 public Order(String code, Map<String, Object> features) { this(code, new ArrayList<>(), features); } //傳入code, offers, features public Order(String code, List<String> offers, Map<String, Object> features) { this.code = code; this.offers = offers; this.features = features; } }
2.2 set設置屬性
public class Order { private String code; private List<String> offers; private Map<String, Object> features; public String getCode() { return code; } public void setCode(String code) { this.code = code; } public List<String> getOffers() { return offers; } public void setOffers(List<String> offers) { this.offers = offers; } public Map<String, Object> getFeatures() { return features; } public void setFeatures(Map<String, Object> features) { this.features = features; } }
3.1 從傳參中獲取屬性, 使用 org.springframework.beans.BeanUtils
//為省略程式碼量,使用lombok的@Data //Order.java @Data public class Order { private String code; private List<String> offers; private Map<String, Object> features; } //FooModel.java //和Order.java的屬性一致 @Data public class FooModel { private String code; private List<String> offers; private Map<String, Object> features; } //FooTest.class import org.springframework.beans.BeanUtils; public class FooTest { public void handleFoo(FooModel model) { Order order = new Order(); BeanUtils.copyProperties(model, order); //處理order對象 } }
4.1 靜態工廠方法單例對象
/** * 枚舉來創建單例對象有以下優勢 * 1.構造方法已私有化 * 2.是否多執行緒安全 * 3.支援序列化機制,防止多次序列化創建對象 */ public enum Singleton { INSTANCE; public void method() { //函數處理 } }
4.2 靜態工廠方法快取對象
/** * 下面以經典的Boolean.java來舉例 * Boolean類創建了兩個常量屬性,TRUE和FALSE * 在調用valueOf時使用這個快取 */ public final class Boolean implements java.io.Serializable, Comparable<Boolean> { //快取 真 public static final Boolean TRUE = new Boolean(true); //快取 假 public static final Boolean FALSE = new Boolean(false); private final boolean value; public Boolean(boolean value) { this.value = value; } //返回快取值,不新創建對象 public static Boolean valueOf(boolean b) { return (b ? TRUE : FALSE); } }
5.1 Java8之前建造者模式(不依賴第三方庫)
public class Order { private String code; private List<String> offers; private Map<String, Object> features; public static Builder builder() { return new Builder(); } //私有化構造方法,只提供給Builder.build()使用 private Order(String code, List<String> offers, Map<String, Object> features) { this.code = code; this.offers = offers; this.features = features; } public String toString() { return "Order(code=" + this.code + ", offers=" + this.offers + ", features=" + this.features + ")"; } public static class Builder { private String code; private List<String> offers; private Map<String, Object> features; public Builder code(String code) { this.code = code; return this; } public Builder offers(List<String> offers) { this.offers = offers; return this; } public Builder offer(String offer) { if (null == this.offers) { this.offers = new ArrayList<>(); } this.offers.add(offer); return this; } public Builder features(Map<String, Object> features) { this.features = features; return this; } public Builder feature(String key, Object value) { if (null == this.features) { this.features = new HashMap<>(); } this.features.put(key, value); return this; } public Order build() { return new Order(this.code, this.offers, this.features); } } public static void main(String[] args) { Order order = Order.builder() .code("1234") .offer("滿100減5") .offer("滿200減15") .feature("color", "white") .feature("category", "shirt") .build(); System.out.println(order); } } /** 輸出 Order(code=1234, offers=[滿100減5, 滿200減15], features={color=white, category=shirt}) Process finished with exit code 0 */
5.2 Java8建造者模式(不依賴第三方庫)
函數式介面和泛型構造器
@FunctionalInterface public interface KeyValueConsumer<T, K, V> { void accept(T t, K k, V v); default KeyValueConsumer<T, K, V> andThen(KeyValueConsumer<? super T, ? super K, ? super V> after) { Objects.requireNonNull(after); return (t, k, v) -> { accept(t, k, v); after.accept(t, k, v); }; } } public class GenericBuilder<T> { private final Supplier<T> instantiator; private List<Consumer<T>> instantiatorModifiers = new ArrayList<>(); private List<Consumer<T>> keyValueModifiers = new ArrayList<>(); public GenericBuilder(Supplier<T> instantiator) { this.instantiator = instantiator; } public static <T> GenericBuilder<T> of(Supplier<T> instantiator) { return new GenericBuilder<T>(instantiator); } public <U> GenericBuilder<T> with(BiConsumer<T, U> consumer, U value) { Consumer<T> c = instance -> consumer.accept(instance, value); instantiatorModifiers.add(c); return this; } public <K, V> GenericBuilder<T> with(KeyValueConsumer<T, K, V> consumer, K key, V value) { Consumer<T> c = instance -> consumer.accept(instance, key, value); keyValueModifiers.add(c); return this; } public T build() { T value = instantiator.get(); instantiatorModifiers.forEach(modifier -> modifier.accept(value)); keyValueModifiers.forEach(keyValueModifier -> keyValueModifier.accept(value)); instantiatorModifiers.clear(); keyValueModifiers.clear(); return value; } }
實際操作類
package com.example.demo.bean; import lombok.Builder; import lombok.Singular; import lombok.ToString; import java.util.*; public class Order { private String code; private List<String> offers; private Map<String, Object> features; //省略無參構造方法,set、get,toString方法 public void addOffer(String offer) { offers = Optional.ofNullable(offers) .orElseGet(ArrayList::new); offers.add(offer); } public <T> void addFeature(String key, T value) { features = Optional.ofNullable(features) .orElseGet(HashMap::new); features.put(key, value); } public static void main(String[] args) { Order order = GenericBuilder.of(Order::new) .with(Order::setCode, "1234") .with(Order::addOffer, "滿200減15") .with(Order::addOffer, "滿200減15") .with(Order::addFeature, "color", "white") .with(Order::addFeature, "category", "shirt").build(); System.out.println(order); } } /** 輸出 Order(code=1234, offers=[滿200減15, 滿200減15], features={color=white, category=shirt}) Process finished with exit code 0 */
5.3 lombok第三方庫的建造者模式
@ToString @Builder public class Order { private String code; @Singular private List<String> offers; @Singular private Map<String, Object> features; public static void main(String[] args) { Order order = Order.builder() .code("1234") .offer("滿100減5") .offer("滿200減15") .feature("color", "white") .feature("category", "shirt") .build(); System.out.println(order); } } /** 輸出 Order(code=1234, offers=[滿200減15, 滿200減15], features={color=white, category=shirt}) Process finished with exit code 0 */
6.1 依賴注入
這裡移除了Spring的配置文件,只展示實際程式碼
@RestController public class DemoController { //自動注入demo的服務 @Autowired private DemoService demoService; @GetMapping public String getDemo() { return demoService.get(); }
建造者模式來源於 如何實現Builder模式
單例模式來源於 Effective Java