书上分别说了以下三种
1)将公有静态成员做成final域享有特权的客户端可以借助AccessibleObject.setAccessible方法,通过反射机制调用私有构造器。
package com.lzw.singleton1; import java.lang.reflect.Constructor; /** * * 单例实现1 * <p> * 公有静态成员是个public final域 * <p> * 为防止client利用反射调用私有改造函数,所以在创建第二个实例的时候抛出了异常 * * @author troyli * */ public class Singleton1 { public static final Singleton1 INSTANCE = new Singleton1(); // 私有改造函数 private Singleton1() { if (INSTANCE != null) { throw new IllegalArgumentException("不存在第二个实例对象……"); } } // 其他方法实现 public void otherMethod() { System.out.println("call otherMethod"); } @SuppressWarnings({ "rawtypes", "unchecked" }) public static void main(String args[]) throws Exception { Singleton1 singleton = Singleton1.INSTANCE; singleton.otherMethod(); // 利用反射调用私有构造器 Constructor[] arrayConstructor = singleton.getClass() .getDeclaredConstructors(); for (Constructor constructor : arrayConstructor) { // 调用setAccessible(true); constructor.setAccessible(true); // 实例化,这里一定会抛出异常 constructor.newInstance(); } } }
问题:为什么会抛出异常(不存在第二个实例对象……),这里我们分析一下代码的执行顺序。
①JVM 装载类的静态成员变量:public static final Singleton1 INSTANCE;
②执行入口方面main;
③获取实例对象:Singleton1 singleton = Singleton1.INSTANCE;也就会执行private Singleton1(){……}
④执行newInstatnce:constructor.newInstance();就会是在已经有一个对象的时候重新调用private Singleton1(){……},于是就会抛出异常。
2)将公有静态成员做成final域和将公有成员做成静态工厂方法时,为了使Singleton类变成可序列化的,仅仅在声明上加上“implements Serializable”是不够的。
为了维护并保证Singleton,必须声明所有实例域都是瞬时的(transient),并提供一个readResolve方法。否则,每次反序列化一个序列化的实例时,都会创建一个新的实例。
package com.lzw.singleton2; import java.io.Serializable; /** * *单例实现2 *<p> * 公有的成员为静态工厂方法 *<p> * 序列化时,要实现readResolve方法,防止反序列化出新的实例(这个应该怎么测试呢???) * *@author troyli * */ public class Singleton2 implements Serializable { // 私有static Instance private static final Singleton2 INSTANCE = new Singleton2(); // 私有构造函数 private Singleton2() { } // 获取单例方法 public static Singleton2 getInstance() { return INSTANCE; } // 其他方法 public void otherMethod() { // } // 必须提供该方法,以便重新指定反序列化得到的对象. private Object readResolve() { return INSTANCE; } }
这点对我是全新的东西,因此也在积累中……
3)单元素枚举类型更加简洁、无偿的提供了序列化机制,绝对防止多次实例化,即使是在面对复杂的序列化或者反射攻击时;它已经成为实现Singleton的最佳方法。
package com.lzw.singleton3; import java.lang.reflect.Constructor; /** * *枚举实现单例 *<p> * 目前最好的方式,避免了反射的攻击和序列化的问题 * *<pre> * 射调用枚举私有构造函数测试结果: * Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects at java.lang.reflect.Constructor.newInstance(Unknown Source) at com.lzw.singleton3.Singleton3.main(Singleton3.java:41) *</pre> * *@author troyli * */ public class Singleton3 { enum EnumSingleton { INSTANCE; public void otherMethod() { } } // 测试,是否可以反射生成枚举 public static void main(String args[]) throws Exception { // 利用反射调用私有构造器 Constructor[] arrayConstructor = EnumSingleton.INSTANCE.getClass() .getDeclaredConstructors(); for (Constructor constructor : arrayConstructor) { // 调用setAccessible(true),设置为可以访问; constructor.setAccessible(true); // 实例化,这里一定会抛出异常 constructor.newInstance(); } } }
可以看上面的第一种的问题解析,这里不再重复。
虽然这种方法目前还没有被广泛采用,但是单元素的枚举类型已经成为实现Singleton的最佳方法。
相关推荐
在 Java 中实现 Singleton 属性有两种方式,一种是使用私有构造器强化 Singleton 属性,另一种是使用枚举强化 Singleton 属性。对于第一种方法,可以使用私有构造器来实例化公有的静态 final 域,然后使用静态方法 ...
枚举类型可以有自己的构造器,但只能在枚举声明时使用,并且不能是公有的。这允许在创建枚举实例时进行初始化操作。 ```java public enum Planet { MERCURY(3.303e+23, 2.4397e6), VENUS(4.869e+24, 6.0518e6), ...
- **副作用**:使用私有构造器的一个副作用是使得该类不能被继承。 **避免创建不必要的对象**: 1. **不可变对象**:对于不可变对象,可以通过缓存已创建的对象来避免重复创建。 2. **使用静态工厂方法**:如果一...
实现单例时,通常会使用私有构造器防止外部创建实例,同时提供一个静态方法用于获取唯一的实例。Java中常使用双重检查锁定(Double-Check Locking)或枚举方式来实现线程安全的单例。 ```java // 双重检查锁定示例 ...
在本压缩包中,"Java 枚举类型案例"可能包含了多个示例,以帮助我们深入理解Java枚举类型的使用。 1. **枚举的定义** 在Java中,枚举是通过关键字`enum`来定义的。例如,创建一个表示星期的枚举类型: ```java ...
Singleton设计模式是软件工程中最常用的设计模式之一,它的主要目的是确保一个类只有一个实例,并提供全局访问点。在Java中,Singleton模式的实现有多种方式,每种方式都有其优缺点,我们将详细探讨这些实现方法并...
Java枚举是面向对象编程中的一个重要特性,它用于定义固定的常量集合,这些常量通常代表某种特定的数据类型。在Java中,枚举提供了一种更安全、更规范的方式来处理常量,相比于传统的final static变量,枚举具有更多...
Java语言中的构造方法私有化是一种常见的编程技巧,主要用于实现特定的设计模式,如单态模式(Singleton Pattern)。在Java中,私有构造方法是通过将构造函数声明为private来实现的,这使得其他类无法直接实例化该类...
Singleton拥有一个私有构造函数,确保用户无法通过new直接实例化它。除此之外,该模式中包含一个静态私有成员变量instance与静态公有方法Instance()。Instance()方法负责检验并实例化自己,然后存储在静态成员变量中...
简单的单例模式举例Singleton 分为恶汉式 懒汉式
当编译器遇到枚举类型时,会自动为每个枚举常量创建一个私有的构造函数,并在类的静态初始化块中创建实例。这些实例存储在静态字段中,确保它们在类加载时创建,因此枚举对象是线程安全的。例如,反编译后的 `Color...
### Java的Singleton模式详解 #### 一、Singleton模式概述 Singleton模式是一种常用的设计模式,在Java中主要用于确保一个类只有一个实例,并提供一个全局访问点。这种模式对于管理共享资源(如数据库连接池、...
通常,我们会将构造函数设为私有(private),然后提供一个静态方法来获取类的唯一实例。这个静态方法通常被称为“单例方法”。 在Java中,实现单例模式有多种方式: 1. 饿汉式(Eager Initialization): 这种...
接下来,我们将深入探讨Java Singleton模式的实现方式、优缺点以及使用场景。 一、Singleton模式的实现 1. 饿汉式(静态常量) ```java public class Singleton { private static final Singleton INSTANCE = ...
单例模式是软件设计模式中的一种经典模式,它在Java编程中被广泛使用。这个模式的主要目的是确保一个类只有一个实例,并提供一个全局访问点。这样做的好处在于控制资源的共享,减少系统开销,以及在多线程环境中避免...
析构或构造函数声明为保护或私有成员 标题“析构或构造函数声明为保护或私有成员”所对应的知识点主要是关于C++语言中构造函数和析构函数的访问控制。 在C++语言中,构造函数和析构函数可以被声明为私有或保护成员...
Singleton模式的核心特点是单例类只能有一个实例,且该类必须自行创建这个实例,同时还要提供一个静态方法或属性,使得其他部分的代码可以获取到这个唯一的实例。 在Java中,实现Singleton有多种方式,包括: 1. ...
使用枚举类型实现Singleton可以防止反射攻击,同时具有很好的性能和线程安全性。 ```java public enum Singleton { INSTANCE; } ``` **Singleton模式的优缺点** 优点: - **控制实例化过程**:Singleton...
#### EJ 第3条:用私有构造器或枚举类型强化Singleton属性 单例模式是一种常用的软件设计模式,旨在确保一个类只有一个实例,并提供一个全局访问点。Kotlin通过`object`关键字进一步简化了单例模式的实现,使得创建...
Java是世界上最流行的编程语言之一,尤其在企业级应用开发领域占据主导地位。本文将基于“Java学习总结(2023/03/19)”的主题,深入探讨Java的核心概念、设计模式以及最佳实践,结合《Effective Java》的学习心得,...