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

文章目录

  • 一. 用静态工厂方法代替构造器
      • 优势:
      • 劣势:
      • 实例代码:
  • 二. 遇到多个构造器参数时要考虑使用构建器
      • ① 重叠构建器
      • ② 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。
  • 好坏处之类的可以参考书里的内容。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/504621.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java bytearrayoutputstream 文件_Java ByteArrayInputStream和ByteArrayOutputStream示例

ByteArrayInputStream和ByteArrayOutputStream分别从内存中的字节数组读取数据并将数据写入内存中的字节数组。下面是一个演示ByteArrayInputStream的示例。文件&#xff1a;ByteArrayInputStreamExample.java -package com.yiibai.tutorial.io;import java.io.ByteArrayInputS…

【LeetCode笔记】2. 两数相加(JAVA、链表)

文章目录题目描述代码题目描述 解法&#xff1a;直接用两个链表构造出第三个链表即可注意点&#xff1a;进位carry。进位的情况有几种&#xff0c;都要考虑上。&#xff08;其实感觉这道题不是很难&#xff0c;挺直观的&#xff09; 代码 时间复杂度&#xff1a;O(max(m,n)…

java velocity是什么意思_基于 Java 的模板引擎Velocity快速入门

最近使用Velocity模板引擎&#xff0c;写一个maven项目Coding生成工具。对基于Java的模板引擎Velocity的demo总结如下&#xff1a;Step1. 创建Maven项目&#xff0c;添加如下velocity的dependency到pom.xml中org.apache.velocityvelocity1.7Step2. 创建模板文件HelloVelocity.v…

【LeetCode笔记】3. 无重复字符的最长子串(JAVA、滑动窗口、字符串)

文章目录题目描述思路 && 代码1. 之前的版本更新 2.0题目描述 子串&#xff1a;各字符间必须要相邻&#xff0c;而非子序列使用滑动窗口来做就行 思路 && 代码 1. 之前的版本 思路&#xff1a;维护一个滑动窗口&#xff0c;滑动窗口中容纳一个无重复字符的…

启动java服务时刷新缓存_Spring java项目对外提供服务和java进程启动时bean,内部缓存加载的先后关系?...

Spring java项目对外提供服务有这么几种&#xff0c;一种是web服务&#xff0c;譬如tomcat&#xff0c;一种是RPC服务&#xff0c;譬如dubbo&#xff0c;thrift。总的来说就是对外开放某个/些端口&#xff0c;接收请求。Spring工程项目启动时&#xff0c;bean会加载&#xff0c…

【LeetCode笔记】5.最长回文子串(Java、动态规划、字符串)

文章目录题目描述解法 & 代码&#xff1a;思路题目描述 回文&#xff1a;正着念和倒着念一样。 解法 & 代码&#xff1a; 一开始看到子串&#xff0c;想着可能no.3最长重复子串一样用滑动窗口。不过回文串的判断会很麻烦&#xff0c;于是舍弃。之后看题解&#xff…

java 拷贝替换文件夹_比较两个不同文件夹中的两个文件,并将其替换为较新的文件夹...

如果要根据上次修改日期覆盖&#xff0c;则 File 对象具有所需的属性&#xff1a; DateLastModified . (您可以检查 File 对象的所有属性here . )您已经可以访问源文件对象(代码的 Photo 变量)&#xff0c;因此您只需要获取目标的文件对象 .这样的事情应该有效&#xff1a;Dim …

【LeetCode笔记】6. Z字形变化(JAVA、思路)

文章目录题目描述解题 & 代码二维数组ArrayList && StringBuilder题目描述 这题目有点搞&#xff0c;理解题意得时候直接结合给的例子比较好。本质就是&#xff1a;从顶部开始&#xff0c;往下逐个走&#xff1b;到底了就反向&#xff0c;往上逐个走&#xff1b;以…

用java写四则混合运算,JAVA写的四则混合运算-JSP教程,Java技巧及代码

这是一个四则混合运算程序,没什么做优化,也没做什么注释,(人啊,总喜欢偷懒的.)这个版本我已经定为了2.21版本.呵呵.从最先的1.0到2.0的改动很大.除了运算思想没动处,其它的都在2.0做了重新设计.这种程序其实网上一大把(算法也好得多)。此仅为无聊找点事情做而已。/***四则混合运…

【LeetCode笔记】7.整数反转(Java、溢出判断、栈)

文章目录题目描述解法 & 代码① 字符串解法② 类栈做法题目描述 边界比较需要考虑&#xff0c;而且还有不允许64位整数的要求。 解法 & 代码 ① 字符串解法 起初想到的做法&#xff0c;不过缺点比较多首先用到了long&#xff0c;实际上不允许使用&#xff0c;修改…

java 接口中变量修饰符,Java的访问修饰符与变量的作用域讲解

Java访问修饰符(访问控制符)Java 通过修饰符来控制类、属性和方法的访问权限和其他功能&#xff0c;通常放在语句的最前端。例如&#xff1a;?Java 的修饰符很多&#xff0c;分为访问修饰符和非访问修饰符。本节仅介绍访问修饰符&#xff0c;非访问修饰符会在后续介绍。访问修…

【LeetCode笔记】11.盛最多水的容器(Java、双指针法)

文章目录题目描述代码 & 解题思路题目描述 无 代码 & 解题思路 思路&#xff1a;使用左右两个指针&#xff0c;不断缩小范围&#xff0c;并在每次缩小的过程对最大值进行更新。代码实现不难&#xff0c;主要是弄明白为啥这样做就能得到正确的值简单描述就是&#x…

php 类加载,关于PHP中类的加载

类的访问方式有两种&#xff1a;通过实例化对象访问类成员访问而访问的前提便是内存中有类的存在&#xff0c;所以需要提前将类加载至内存中。1.手动加载//类文件 Salary.phpclass Salary{public function Student(){echo "Salary下面的Student方法";}}?>应用文件…

【LeetCode笔记】15.三数之和(JAVA、双指针)

文章目录题目描述代码 & 解题思路二刷更新题目描述 主要是解决重复的问题&#xff1a;如何去除重复解、在有大量重复解的情况下如何让算法跑得更快 代码 & 解题思路 先排序&#xff0c;按照大小顺序来做。思路&#xff1a;固定第一个数&#xff0c;用双指针分别代表…

php 网站计数器,PHP实现网站访问量计数器

简单的网站访问量计数器实现&#xff0c;具体如下首先说明思路&#xff1a;1.用户向服务器发出访问请求2.服务器读取访问次数文件&#xff0c;1&#xff0c;向客户端返回3.服务器保存新的浏览次数4.新用户访问&#xff0c;重复123即可解决方案(主要算法)&#xff1a;1.数据文件…

【LeetCode笔记】17.电话号码的字母组合(Java、DFS)

文章目录题目描述代码 & 思路题目描述 得建立映射&#xff0c;其实用数组来建立也行&#xff0c;看起来还比较直观。 代码 & 思路 理好DFS的过程就行&#xff0c;整体思路不难当递归字符length 1时&#xff0c;递归结束 class Solution {public List<String&g…

php ajax download,通过Ajax和PHP强制下载

小编典典您无法使用Ajax下载文件。因此&#xff0c;如果您在ajax上发生了某些情况&#xff0c;则应返回url作为响应&#xff0c;并像document.location "url"开始下载过程一样应用它。这里有一个音符。我记得&#xff0c;如果不是用户单击启动浏览器&#xff0c;浏览…

【LeetCode笔记】19.删除链表的倒数第N个结点(Java、快慢指针)

文章目录题目描述思路 & 代码题目描述 重点在于一趟扫描实现简单的做法&#xff1a;一趟扫描长度&#xff0c;一趟根据长度找到结点删除 思路 & 代码 两种特例情况&#xff0c;见注释思路&#xff1a;根据N构造两个快慢指针&#xff0c;两指针直接差了N个结点。由此…

java xca碗组口径,公路车常见杂音检查清单和解决方法(图文)

爱车上出现杂音是件很不爽的事&#xff0c;我本人就很不能容忍任何异响。相信这也是困扰广大车友的问题之一&#xff0c;于是我根据自己和周围车友的经验以及网上的资源&#xff0c;整理出这份清单&#xff0c;希望能给遇到此类问题的车友们一些帮助。写在前面&#xff1a;当异…

【LeetCode笔记】20.有效的括号(Java、栈) 21. 合并两个有序链表(Java)

文章目录20. 题目描述 & 解题21. 题目描述 & 解题1. 一开始的写法2. 参考大佬的写法两道简单类型连着&#xff0c;就直接一起写了。 20. 题目描述 & 解题 括号题是真挺烦人的。。。经典题目了&#xff0c;在学数据结构到栈的时候也会写到类似的题目其实主要就是&a…