`

Effective Java(二)之用私有构造器或者枚举类型强化Singleton属性

阅读更多

书上分别说了以下三种

 

        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私有构造器使用方法示例

    在 Java 中实现 Singleton 属性有两种方式,一种是使用私有构造器强化 Singleton 属性,另一种是使用枚举强化 Singleton 属性。对于第一种方法,可以使用私有构造器来实例化公有的静态 final 域,然后使用静态方法 ...

    高阶Java-Java枚举类型 enum 应用详解

    枚举类型可以有自己的构造器,但只能在枚举声明时使用,并且不能是公有的。这允许在创建枚举实例时进行初始化操作。 ```java public enum Planet { MERCURY(3.303e+23, 2.4397e6), VENUS(4.869e+24, 6.0518e6), ...

    Effective-Java读书笔记(上)

    - **副作用**:使用私有构造器的一个副作用是使得该类不能被继承。 **避免创建不必要的对象**: 1. **不可变对象**:对于不可变对象,可以通过缓存已创建的对象来避免重复创建。 2. **使用静态工厂方法**:如果一...

    Java常用设计模式(SingleTon、FactoryMethod、AbstractFactory)

    实现单例时,通常会使用私有构造器防止外部创建实例,同时提供一个静态方法用于获取唯一的实例。Java中常使用双重检查锁定(Double-Check Locking)或枚举方式来实现线程安全的单例。 ```java // 双重检查锁定示例 ...

    Java 枚举类型案例.zip

    在本压缩包中,"Java 枚举类型案例"可能包含了多个示例,以帮助我们深入理解Java枚举类型的使用。 1. **枚举的定义** 在Java中,枚举是通过关键字`enum`来定义的。例如,创建一个表示星期的枚举类型: ```java ...

    singleton设计模式java实现及对比

    Singleton设计模式是软件工程中最常用的设计模式之一,它的主要目的是确保一个类只有一个实例,并提供全局访问点。在Java中,Singleton模式的实现有多种方式,每种方式都有其优缺点,我们将详细探讨这些实现方法并...

    java枚举ppt和详细代码

    Java枚举是面向对象编程中的一个重要特性,它用于定义固定的常量集合,这些常量通常代表某种特定的数据类型。在Java中,枚举提供了一种更安全、更规范的方式来处理常量,相比于传统的final static变量,枚举具有更多...

    Java语言中的构造方法私有化.pdf

    Java语言中的构造方法私有化是一种常见的编程技巧,主要用于实现特定的设计模式,如单态模式(Singleton Pattern)。在Java中,私有构造方法是通过将构造函数声明为private来实现的,这使得其他类无法直接实例化该类...

    单例模式Singleton(java源码)

    Singleton拥有一个私有构造函数,确保用户无法通过new直接实例化它。除此之外,该模式中包含一个静态私有成员变量instance与静态公有方法Instance()。Instance()方法负责检验并实例化自己,然后存储在静态成员变量中...

    Java 单例模式Singleton

    简单的单例模式举例Singleton 分为恶汉式 懒汉式

    java 枚举(enum) 详解(学习资料)

    当编译器遇到枚举类型时,会自动为每个枚举常量创建一个私有的构造函数,并在类的静态初始化块中创建实例。这些实例存储在静态字段中,确保它们在类加载时创建,因此枚举对象是线程安全的。例如,反编译后的 `Color...

    Java的Singleton模式代码(免资源分)

    ### Java的Singleton模式详解 #### 一、Singleton模式概述 Singleton模式是一种常用的设计模式,在Java中主要用于确保一个类只有一个实例,并提供一个全局访问点。这种模式对于管理共享资源(如数据库连接池、...

    Singleton 单例模式

    通常,我们会将构造函数设为私有(private),然后提供一个静态方法来获取类的唯一实例。这个静态方法通常被称为“单例方法”。 在Java中,实现单例模式有多种方式: 1. 饿汉式(Eager Initialization): 这种...

    java Singleton单例模式

    接下来,我们将深入探讨Java Singleton模式的实现方式、优缺点以及使用场景。 一、Singleton模式的实现 1. 饿汉式(静态常量) ```java public class Singleton { private static final Singleton INSTANCE = ...

    singleton_单例模式_java_设计模式_

    单例模式是软件设计模式中的一种经典模式,它在Java编程中被广泛使用。这个模式的主要目的是确保一个类只有一个实例,并提供一个全局访问点。这样做的好处在于控制资源的共享,减少系统开销,以及在多线程环境中避免...

    析构或构造函数声明为保护或私有成员

    析构或构造函数声明为保护或私有成员 标题“析构或构造函数声明为保护或私有成员”所对应的知识点主要是关于C++语言中构造函数和析构函数的访问控制。 在C++语言中,构造函数和析构函数可以被声明为私有或保护成员...

    Singleton

    Singleton模式的核心特点是单例类只能有一个实例,且该类必须自行创建这个实例,同时还要提供一个静态方法或属性,使得其他部分的代码可以获取到这个唯一的实例。 在Java中,实现Singleton有多种方式,包括: 1. ...

    singleton面试题转

    使用枚举类型实现Singleton可以防止反射攻击,同时具有很好的性能和线程安全性。 ```java public enum Singleton { INSTANCE; } ``` **Singleton模式的优缺点** 优点: - **控制实例化过程**:Singleton...

    现代编程语言- Kotlin 之美 - 当下最火的编程语言欣赏.pdf

    #### EJ 第3条:用私有构造器或枚举类型强化Singleton属性 单例模式是一种常用的软件设计模式,旨在确保一个类只有一个实例,并提供一个全局访问点。Kotlin通过`object`关键字进一步简化了单例模式的实现,使得创建...

    Java学习总结(2023/03/19)

    Java是世界上最流行的编程语言之一,尤其在企业级应用开发领域占据主导地位。本文将基于“Java学习总结(2023/03/19)”的主题,深入探讨Java的核心概念、设计模式以及最佳实践,结合《Effective Java》的学习心得,...

Global site tag (gtag.js) - Google Analytics