文章目录

  • 一. 用静态工厂方法代替构造器
    • 优势:
    • 劣势:
    • 实例代码:
  • 二. 遇到多个构造器参数时要考虑使用构建器
    • ① 重叠构建器
    • ② JavaBeans模式
    • ③ Builder模式
  • 三. 用私有构造器或枚举类型强化Singleton属性
    • 方法一:公有静态成员是个final域
    • 方法二:公有的成员是个静态工厂
    • 方法三:一个包含单个元素的枚举类型
  • 四. 通过私有构造器强化不可实例化的能力

一. 用静态工厂方法代替构造器

优势:

  • 有名称,可以确切地描述正被返回的对象。
  • 不必在每次调用的时候都创建新对象
  • 可以返回原返回类型的任何子类型对象
  • 返回的对象的类可以随着每次调用而产生变化
  • 返回的对象所属的类,在编写静态工厂方法时可以不存在

劣势:

  • 类如果不含公有的或者受保护的构造器,就不能被子类化
  • 程序员很难发现他们,但是可以通过遵守标准的命名习惯来弥补。

实例代码:

这是第二版的代码,在第三版里已经删去了。不过可以用来便于理解。

// 英文部分可以不看,或者作为参考。/**
用于描述Provider所提供的服务
*/
public interface Service {... // Service-specific methods go here
}
/**
用于描述Provider,每个Provider都要有自己的newService
*/
public interface Provider {Service newService();
}
// Noninstantiable class for service registration and access
public class Services {// 私有化构造方法,不会把工厂实例化,直接用类名。
private Services() { } // Prevents instantiation (Item 4)// 使用Map来存储各provider及其name的映射。
// 使用final:只是providers一直指向当前HashMap,对HashMap里的内容变化没有影响。
// Maps service names to services
private static final Map<String, Provider> providers =
new ConcurrentHashMap<String, Provider>();
public static final String DEFAULT_PROVIDER_NAME = "<def>";
ITEM 1: CONSIDER STATIC FACTORY METHODS INSTEAD OF CONSTRUCTORS 9
// Provider registration API
public static void registerDefaultProvider(Provider p) {registerProvider(DEFAULT_PROVIDER_NAME, p);
}
// 注册Provider到工厂Services里。
public static void registerProvider(String name, Provider p){providers.put(name, p);
}
// Service access API
public static Service newInstance() {return newInstance(DEFAULT_PROVIDER_NAME);
}
// 根据name从工厂中取出对应Provider提供的服务。
public static Service newInstance(String name) {Provider p = providers.get(name);
if (p == null)
throw new IllegalArgumentException(
"No provider registered with name: " + name);
return p.newService();
}
}

二. 遇到多个构造器参数时要考虑使用构建器

① 重叠构建器

参考下面的代码,活用this,但是参数多的时候难写,并且用起来容易出错。

public class NutritionFacts {private final int servingSize; // (mL) required
private final int servings; // (per container) required
private final int calories; // optional
private final int fat; // (g) optional
private final int sodium; // (mg) optional
private final int carbohydrate; // (g) optional
public NutritionFacts(int servingSize, int servings) {this(servingSize, servings, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories) {this(servingSize, servings, calories, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories, int fat) {this(servingSize, servings, calories, fat, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories, int fat, int sodium) {this(servingSize, servings, calories, fat, sodium, 0);
}
12 CHAPTER 2 CREATING AND DESTROYING OBJECTS
public NutritionFacts(int servingSize, int servings,
int calories, int fat, int sodium, int carbohydrate) {this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
this.carbohydrate = carbohydrate;
}
}

② JavaBeans模式

  • 构造函数就是:ClassName(){}
  • 赋值交给Setters来实现
  • 弥补了重叠构造器的不足,容易创建实例,并且易读
  • 缺点1:构造过程中JavaBean可能处于不一致的状态(毕竟要多次赋值)
  • 缺点2:使得把类做成不可变的可能性不存在(不能final,毕竟之后还要set的嘛)
public class NutritionFacts {// Parameters initialized to default values (if any)
private int servingSize = -1; // Required; no default value
private int servings = -1; // " " " "
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public NutritionFacts() { }
ITEM 2: CONSIDER A BUILDER WHEN FACED WITH MANY CONSTRUCTOR PARAMETERS 13
// Setters
public void setServingSize(int val) { servingSize = val; }
public void setServings(int val) { servings = val; }
public void setCalories(int val) { calories = val; }
public void setFat(int val) { fat = val; }
public void setSodium(int val) { sodium = val; }
public void setCarbohydrate(int val) { carbohydrate = val; }
}

③ Builder模式

  • 既保证了像重叠构造器的安全性,也保证像JavaBeans的可读性
  • 描述:用类的Builder,来构建类的对象。
// Builder Pattern
public class NutritionFacts {private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
// 在构造函数前,先写好内部类Builder
public static class Builder {// Required parameters
private final int servingSize;
private final int servings;
// Optional parameters - initialized to default values
// 与类的final不同,此处因为要用setters,因此不设为final。
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0;
public Builder(int servingSize, int servings) {this.servingSize = servingSize;
this.servings = servings;
}
// 逐个属性设定值,每次都return this:和之后的具体调用相关
public Builder calories(int val)
{ calories = val; return this; }
public Builder fat(int val)
{ fat = val; return this; }
public Builder carbohydrate(int val)
{ carbohydrate = val; return this; }
public Builder sodium(int val)
{ sodium = val; return this; }
// build():使用this builder作为参数来构造类的对象。
public NutritionFacts build() {return new NutritionFacts(this);
}
}
// 私有的构造函数,使用已经完全初始化的builder来进行唯一的一次初始化。
private NutritionFacts(Builder builder) {servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
  • 还适用于类层次结构(这里会麻烦点,涉及到抽象类、范式等等)
// 先来个抽象的父类Pizza!
// Builder pattern for class hierarchies
public abstract class Pizza {public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE }
final Set<Topping> toppings;
// Builder也是抽象的,这里用到T进行一个约束:只能是Pizza.Builder的字类
abstract static class Builder<T extends Builder<T>> {EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
public T addTopping(Topping topping) {toppings.add(Objects.requireNonNull(topping));
// 这里不能return T,所以先自己写一个self()函数来代替(返回还未构成的类)
return self();
}
abstract Pizza build();
// Subclasses must override this method to return "this"
protected abstract T self();
}
// <?>:只要是继承了Builder的参数都可用
Pizza(Builder<?> builder) {toppings = builder.toppings.clone(); // See Item 50
}
}// 再来两个子类披萨
// 重写之前的抽象函数,并且构造函数中要有super(builder)
public class NyPizza extends Pizza {public enum Size { SMALL, MEDIUM, LARGE }
private final Size size;
public static class Builder extends Pizza.Builder<Builder> {private final Size size;
public Builder(Size size) {this.size = Objects.requireNonNull(size);
}
@Override public NyPizza build() {return new NyPizza(this);
}
@Override protected Builder self() { return this; }
}
private NyPizza(Builder builder) {super(builder);
size = builder.size;
}
}
public class Calzone extends Pizza {private final boolean sauceInside;
public static class Builder extends Pizza.Builder<Builder> {private boolean sauceInside = false; // Default
public Builder sauceInside() {sauceInside = true;
return this;
}
@Override public Calzone build() {return new Calzone(this);
}
@Override protected Builder self() { return this; }
}
private Calzone(Builder builder) {super(builder);
sauceInside = builder.sauceInside;
}
}// 然后实际使用的时候
// 使用类名建builder,一直builder下去,最后让builder来build对象出来。
NyPizza pizza = new NyPizza.Builder(SMALL)
.addTopping(SAUSAGE).addTopping(ONION).build();
Calzone calzone = new Calzone.Builder()
.addTopping(HAM).sauceInside().build();

三. 用私有构造器或枚举类型强化Singleton属性

  • 单例模式

方法一:公有静态成员是个final域

// Singleton with public final field
public class Elvis {// 公有 静态 final
public static final Elvis INSTANCE = new Elvis();
// 构造函数私有化
private Elvis() { ... }
public void leaveTheBuilding() { ... }
}

方法二:公有的成员是个静态工厂

相对于方法一,毕竟类的成员还是要用方法来访问比较好。

// Singleton with public final field
public class Elvis {// 把public 变成 private
private static final Elvis INSTANCE = new Elvis();
// 构造函数私有化
private Elvis() { ... }
// 公有静态方法,返回唯一的实例。
public static Elvis getInstance(){return INSTANCE}
public void leaveTheBuilding() { ... }
}

方法三:一个包含单个元素的枚举类型

  • Java的enum和C++的差别很大。
  • 这样子写会比较简便。
// Enum singleton - the preferred approach
public enum Elvis{// 只有一个“实例”INSTANCE;public void leaveTheBuilding(){...}
}

四. 通过私有构造器强化不可实例化的能力

  • 这块感觉没啥好讲的,就是给构造函数加个private。
  • 好坏处之类的可以参考书里的内容。

【Effective Java】第二章:静态工厂、构建器、强化Singleton属性、私有构造器、相关推荐

  1. [Effective Java]第二章 创建和销毁对象

    第一章      前言 略... 第二章      创建和销毁对象 1.            考虑用静态工厂方法代替构造器 创建对象方法:一是最常用的公有构造器,二是静态工厂方法.下面是一个Bool ...

  2. 【Effective Java】1.静态工厂方法来替换构造函数

    优点一:不像构造函数,它有具意的名称 因为可以取具意的名称,更容易明白如何使用,可读性也更好, 优点二:每次调用无需创建新的对象 优点三:允许返回子类对象 这些子类可以不为public ,这样可以打造 ...

  3. java第二章复习_JAVA第二章知识点

    JAVA第二章知识点 本章知识梳理 2.1 关键字 2.2 标识符 2.3 变 量 2.4运算符 2.5 程序流程控制 2.6 方法 2.1 关键字 关键字(keyword)的定义和特点 定义:被ja ...

  4. java第二章_JAVA第二章知识点

    JAVA第二章知识点 本章知识梳理 2.1 关键字 2.2 标识符 2.3 变 量 2.4运算符 2.5 程序流程控制 2.6 方法 2.1 关键字 关键字(keyword)的定义和特点 定义:被ja ...

  5. 第二章 定义和构建索引(二)

    文章目录 第二章 定义和构建索引(一) 定义索引 使用带有索引的Unique.PrimaryKey和IdKey关键字 定义SQL搜索索引 用索引存储数据 索引null 索引集合 使用(Elements ...

  6. 尚学堂 JAVA第二章作业

    文章目录 尚学堂JAVA 第二章作业 尚学堂JAVA 第二章作业 第二章比较简单写得很粗略 import java.util.Scanner; import java.math.*;/*** 作业2* ...

  7. Java 设计模式之静态工厂方法模式

    设计模式系列 创建型设计模式 Java 设计模式之单例模式 Java 设计模式之静态工厂方法模式 Java 设计模式之工厂方法模式 Java 设计模式之抽象工厂模式 Java 设计模式之Builder ...

  8. Effective Java第二版 读后感

    虽说是读后感,其实我并没有完全读完这本书,中间有些不懂的章节和最后的两章(并发和序列化)是没有看完的,以目前的实力来看,实在看的云里雾里,就决定先放着,看看编程思想后再回头看看.写这篇感想一是为了记录 ...

  9. java多个构造方法_Java构建器(多个构造器参数)

    今天看netty权威指南,第一次听说构建器,百度了几个博客,但是并没有通俗易懂一点儿的,综合别人的博客,总结如下: 1. 构建器是什么? 当创建对象需要传入多个参数的时候我们通常会根据参数的数量写不同 ...

最新文章

  1. C++ Primer 5th笔记(chap 16 模板和泛型编程)可变参数模板举例
  2. Java遍历完数的一些思考
  3. 5G大幕已启 将如何改变社会?
  4. 为什么大家都在抵制用定时任务实现「关闭超时订单」功能?
  5. 云计算概况及第一个Azure程序
  6. #openssl #爆重大漏洞heartbleed,危及两亿网民!!!
  7. python中sorted_关于python中sorted方法的key参数
  8. 【国内下载Android系统源码的方法】
  9. 计算机ps相框怎么做,如何在PS中制作相框?在PS中制作相框的具体方法
  10. 【智能门禁系统】——硬件设计
  11. 07 ,矩阵的转置,矩阵的行列式,方阵 ( 2阶行列式,3阶行列式,n 阶行列式 ) :
  12. 修改植物大战僵尸数据
  13. #树形dp#洛谷 2014 codevs 1378 jzoj 1486 选课
  14. 瑞士轮赛制模拟器_【入门必读】VGC综合介绍(下篇)【翻译】
  15. c语言之良好的编程习惯(四)
  16. NetSuite 合并报表之外币折算差异(CTA)
  17. 第二十九章 狼心狗肺
  18. [办公软件]怎么在WPS表格里设置完成率公式?
  19. 浅谈共线性的产生以及解决方法(上篇——前世)
  20. [Gitlab CI/CD] fatal: could not read Username for ‘xxx‘: No such device or address

热门文章

  1. linux安装DNS服务命令,Linux下的安装和配置DNS服务器
  2. python选择某一行_Python常用语法有哪些 如何快速入门Python开发
  3. js数组截取前5个_想用好 Node.js?这 5 个经典国产项目值得细品
  4. c语言编程求连续几日的温差最大 最小值,数控维修理论题库(含答案)X2份..doc
  5. java format用法_java学习常用函数之日期时间函数
  6. armv7 cortex a系列编程手册_STM32、Cortex-M3和ARMv8-M之间的关联
  7. OpenCV——读取视频文件并写入文件
  8. 利用flask写的接口(base64, 二进制, 上传视频流)+异步+gunicorn部署Flask服务+多gpu卡部署
  9. Android IDA 动态调试最完善攻略,跨过各种坑
  10. CompletableFuture详解~allOf