用私有构造方法或枚类实现Singleton属性

This commit is contained in:
尉勇强2 2019-09-11 20:18:37 +08:00
parent 3e9b73708f
commit f1df6a3327
4 changed files with 112 additions and 0 deletions

View File

@ -0,0 +1,35 @@
## 使用私有构造方法或枚类实现Singleton属性
单例是一个仅实例化一次的类。单例对象通常表示无状态对象,如函数(条目 24)或一个本质上唯一的系统组件。让一个类成为单例会使测试它的客户变得困难,因为除非实现一个作为它类型的接口,否则不可能用一个模拟实现替代单例。
### 单例类方法一:成员是 final 修饰的属性
**示例代码**[Item03Example01.java](CreatingAndDestroyingObjects/src/main/java/com/jueee/item03/Item03Example01.java)
私有构造方法只调用一次,来初始化公共静态 final `Elvis.INSTANCE`属性。
缺少一个公共的或受保护的构造方法保证了全局的唯一性一旦Elvis类被初始化一个Elvis的实例就会存在——不多也不少。
客户端所做的任何事情都不能改变这一点,但需要注意的是:特权客户端可以使用`AccessibleObject.setAccessible`方法,以反射方式调用私有构造方法(条目 65)。
如果需要防御此攻击,请修改构造函数,使其在请求创建第二个实例时抛出异常。
### 单例类方法二:公共成员是一个静态的工厂方法
**示例代码**[Item03Example02.java](CreatingAndDestroyingObjects/src/main/java/com/jueee/item03/Item03Example02.java)
所有对`Elvis.getInstance`的调用都返回相同的对象引用并且不会创建其他的Elvis实例与前面提到的警告相同
公共属性方法的主要优点是API明确表示该类是一个单例公共静态属性是final的所以它总是包含相同的对象引用。 第二个好处是它更简单。
静态工厂方法的一个优点是它可以灵活地改变你的想法无论该类是否为单例而不必更改其API。 工厂方法返回唯一的实例,但是可以修改,比如,返回调用它的每个线程的单独实例。 第二个好处是如果你的应用程序需要它可以编写一个泛型单例工厂generic singleton factory 条目30。 使用静态工厂的最后一个优点是方法引用可以用`supplier`,例如`Elvis :: instance`等同于`Supplier<Elvis>`。 除非与这些优点相关的,否则公共属性方法是可取的。
### 单例类方法三:声明单一元素的枚举类
**示例代码**[Item03Example03.java](CreatingAndDestroyingObjects/src/main/java/com/jueee/item03/Item03Example03.java)
这种方式类似于公共属性方法,但更简洁,提供了免费的序列化机制,并提供了针对多个实例化的坚固保证,即使是在复杂的序列化或反射攻击的情况下。
这种方法可能感觉有点不自然,但是**单一元素枚举类通常是实现单例的最佳方式**。
注意,如果单例必须继承`Enum`以外的父类(尽管可以声明一个`Enum`来实现接口),那么就不能使用这种方法。

View File

@ -0,0 +1,26 @@
package com.jueee.item03;
public class Item03Example01 {
public static void main(String[] args) {
Elvis1 elvis11 = Elvis1.INSTANCE;
elvis11.leaveTheBuilding();
Elvis1 elvis12 = Elvis1.INSTANCE;
elvis12.leaveTheBuilding();
Elvis1 elvis13 = Elvis1.INSTANCE;
elvis13.leaveTheBuilding();
}
}
// 成员是final修饰的属性
class Elvis1 {
public static final Elvis1 INSTANCE = new Elvis1();
// 私有构造方法只调用一次来初始化公共静态 final Elvis1.INSTANCE属性
private Elvis1() {}
public void leaveTheBuilding() {
System.out.println(INSTANCE);
}
}

View File

@ -0,0 +1,29 @@
package com.jueee.item03;
public class Item03Example02 {
public static void main(String[] args) {
Elvis2 elvis21 = Elvis2.getInstance();
elvis21.leaveTheBuilding();
Elvis2 elvis22 = Elvis2.getInstance();
elvis22.leaveTheBuilding();
Elvis2 elvis23 = Elvis2.getInstance();
elvis23.leaveTheBuilding();
}
}
// 公共成员是一个静态的工厂方法
class Elvis2 {
private static final Elvis2 INSTANCE = new Elvis2();
private Elvis2() {}
public static Elvis2 getInstance() {
return INSTANCE;
}
public void leaveTheBuilding() {
System.out.println(INSTANCE);
}
}

View File

@ -0,0 +1,22 @@
package com.jueee.item03;
public class Item03Example03 {
public static void main(String[] args) {
Elvis3 elvis21 = Elvis3.INSTANCE;
elvis21.leaveTheBuilding();
Elvis3 elvis22 = Elvis3.INSTANCE;
elvis22.leaveTheBuilding();
Elvis3 elvis23 = Elvis3.INSTANCE;
elvis23.leaveTheBuilding();
}
}
// 声明单一元素的枚举类
enum Elvis3 {
INSTANCE;
public void leaveTheBuilding() {
System.out.println(INSTANCE);
}
}