Java随谈(三)如何创建好一个对象?
本文推荐阅读时间30分钟
(注意: 编写这篇文章的意义在于,希望自己能够在今后的编程生涯中,每一个实现,都是根据需求去思考,选择较优的实现方式(不是最优而是较优是考虑时间、实现成本等因素,具体见赫伯特西蒙的原则),而不是拿一把锤子,看什么都像钉子。)
Java语法中的创建对象方式
大家都知道,在编写Java程序里,一般就是处理各种各样的对象,那么,你知道一共有多少种创建对象的方式吗?
希望大家能稍微思考一下再往下翻。
答案是4种
- new 一个对象
- 反射一个类实例化一个对象(反射类和反射构造方法)
- clone 一个对象
- 反序列化一个对象
前两者调用了构造方法,后两者没有调用。
其中,日常使用中,最为常见的是使用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.1 静态工厂方法之单例对象(二)
/** * 静态内部类来创建单例模式有以下优势 * 1.代码实现简单 * 2.无需处理多线程问题(类初始化时会先进行静态代码的创建) * 3.属于懒汉式,不使用时不占用空间 */ public class Singleton { //私有化构造器 private Singleton() { } //静态内部类 private static class StaticInnerClass { private static final Singleton instance = new Singleton(); } //对外的静态工厂方法 public static SingletonPattern getInstance() { return StaticInnerClass.instance; } }
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); } }
4.2 静态工厂方法之缓存对象 (二)
/** * IntegerCache 缓存了-128到127的数字 */ public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
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 boot的配置文件,只展示实际的操作代码
@RestController public class DemoController { //自动注入demoService,程序员不用关心对象何时创建 @Autowired private DemoService demoService; @GetMapping public String getDemo() { return demoService.get(); }
建造者模式来源于 如何实现Builder模式
单例模式来源于 Effective Java