From f1df6a3327b892ca16ab450e83cc2d5179438c8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=89=E5=8B=87=E5=BC=BA2?= Date: Wed, 11 Sep 2019 20:18:37 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=A8=E7=A7=81=E6=9C=89=E6=9E=84=E9=80=A0?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E6=88=96=E6=9E=9A=E7=B1=BB=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?Singleton=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../03.使用私有构造方法或枚类实现Singleton属性.md | 35 +++++++++++++++++++ .../main/java/com/jueee/item03/Item03Example01.java | 26 ++++++++++++++ .../main/java/com/jueee/item03/Item03Example02.java | 29 +++++++++++++++ .../main/java/com/jueee/item03/Item03Example03.java | 22 ++++++++++++ 4 files changed, 112 insertions(+) create mode 100644 ch02创建和销毁对象/03.使用私有构造方法或枚类实现Singleton属性.md create mode 100644 ch02创建和销毁对象/CreatingAndDestroyingObjects/src/main/java/com/jueee/item03/Item03Example01.java create mode 100644 ch02创建和销毁对象/CreatingAndDestroyingObjects/src/main/java/com/jueee/item03/Item03Example02.java create mode 100644 ch02创建和销毁对象/CreatingAndDestroyingObjects/src/main/java/com/jueee/item03/Item03Example03.java diff --git a/ch02创建和销毁对象/03.使用私有构造方法或枚类实现Singleton属性.md b/ch02创建和销毁对象/03.使用私有构造方法或枚类实现Singleton属性.md new file mode 100644 index 0000000..5e6edd5 --- /dev/null +++ b/ch02创建和销毁对象/03.使用私有构造方法或枚类实现Singleton属性.md @@ -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`。 除非与这些优点相关的,否则公共属性方法是可取的。 + +### 单例类方法三:声明单一元素的枚举类 + +**示例代码**:[Item03Example03.java](CreatingAndDestroyingObjects/src/main/java/com/jueee/item03/Item03Example03.java) + +这种方式类似于公共属性方法,但更简洁,提供了免费的序列化机制,并提供了针对多个实例化的坚固保证,即使是在复杂的序列化或反射攻击的情况下。 + +这种方法可能感觉有点不自然,但是**单一元素枚举类通常是实现单例的最佳方式**。 + +注意,如果单例必须继承`Enum`以外的父类(尽管可以声明一个`Enum`来实现接口),那么就不能使用这种方法。 \ No newline at end of file diff --git a/ch02创建和销毁对象/CreatingAndDestroyingObjects/src/main/java/com/jueee/item03/Item03Example01.java b/ch02创建和销毁对象/CreatingAndDestroyingObjects/src/main/java/com/jueee/item03/Item03Example01.java new file mode 100644 index 0000000..d825768 --- /dev/null +++ b/ch02创建和销毁对象/CreatingAndDestroyingObjects/src/main/java/com/jueee/item03/Item03Example01.java @@ -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); + } +} \ No newline at end of file diff --git a/ch02创建和销毁对象/CreatingAndDestroyingObjects/src/main/java/com/jueee/item03/Item03Example02.java b/ch02创建和销毁对象/CreatingAndDestroyingObjects/src/main/java/com/jueee/item03/Item03Example02.java new file mode 100644 index 0000000..2340823 --- /dev/null +++ b/ch02创建和销毁对象/CreatingAndDestroyingObjects/src/main/java/com/jueee/item03/Item03Example02.java @@ -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); + } +} \ No newline at end of file diff --git a/ch02创建和销毁对象/CreatingAndDestroyingObjects/src/main/java/com/jueee/item03/Item03Example03.java b/ch02创建和销毁对象/CreatingAndDestroyingObjects/src/main/java/com/jueee/item03/Item03Example03.java new file mode 100644 index 0000000..cb60b75 --- /dev/null +++ b/ch02创建和销毁对象/CreatingAndDestroyingObjects/src/main/java/com/jueee/item03/Item03Example03.java @@ -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); + } +} \ No newline at end of file