`

3. 用私有构造器或枚举类型强化singleton属性

 
阅读更多

Effective Java 中提出实现单例的3中方法,详细内容如下:

1.将公有静态成员变量做成final域

package com.jason.effectivejava.rule3.one;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Elvis {

	public static final Elvis INSTANCE = new Elvis();

	private Elvis() {
               //it is very import for this type
		if(INSTANCE != null){
			throw new IllegalArgumentException("No exist the second instance");
		}
	}

	public void leaveTheBuilding() {

		System.out.println("Whoa baby, I'm outta here!");

	}

	public static void main(String[] args) {

		Elvis singleton = Elvis.INSTANCE;
		singleton.leaveTheBuilding();
		Constructor[] arrayConstructor = singleton.getClass().getDeclaredConstructors();
		arrayConstructor[0].setAccessible(true);
		
		try {
			arrayConstructor[0].newInstance();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

 注意:为了防止特殊用户通过AccessibleObject.setAccessible方法,利用反射机制访问私有构造函数,创建新的实例,应在私有构造函数中添加判断,阻止创建新的实例。

测试结果如下:

Whoa baby, I'm outta here!
java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
	at com.jason.effectivejava.rule3.Elvis.main(Elvis.java:30)
Caused by: java.lang.IllegalArgumentException: No exist the second instance
	at com.jason.effectivejava.rule3.Elvis.<init>(Elvis.java:12)
	... 5 more

2.将公有成员变量做成静态工厂方法

package com.jason.effectivejava.rule3.two;

public class Elvis {

	private static final Elvis INSTANCE = new Elvis();

	private Elvis() {
	}

	public static Elvis getInstance() {
		return INSTANCE;
	}

	public void leaveTheBuilding() {

		System.out.println("Whoa baby, I'm outta here!");
	}

	// 必须提供该方法,以便重新指定反序列化得到的对象.
	private Object readResolve(){

		return INSTANCE;
	}

	public static void main(String[] args) {

		Elvis elvis = Elvis.getInstance();
		elvis.leaveTheBuilding();
	}
}

3.单元素枚举类型

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
*
*枚举实现单例
*<p>目前最好的方式,避免了反射的攻击和序列化的问题
*
*反射调用枚举私有构造函数测试结果:
* Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at com.book.chap2.singleton.Singleton3.main(Singleton3.java:34)
*
*/

public enum Elvis {

	INSTANCE;

	public void leaveTheBuilding() {

		System.out.println("Whoa baby, I'm outta here!");
	}

	public static void main(String[] args) {

		Elvis elvis = Elvis.INSTANCE;
		elvis.leaveTheBuilding();
		
		//测试,是否可以反射生成枚举,利用反射调用私有构造器
		Constructor[] arrayConstructor=Elvis.INSTANCE.getClass().getDeclaredConstructors();

		arrayConstructor[0].setAccessible(true);
		try {
			arrayConstructor[0].newInstance();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}

 试结果:

Whoa baby, I'm outta here!
java.lang.IllegalArgumentException: Cannot reflectively create enum objects
	at java.lang.reflect.Constructor.newInstance(Constructor.java:511)
	at com.jason.effectivejava.rule3.two.Elvis.main(Elvis.java:36)

 总结:

在三种方法中,单元素枚举类型的单例,代码简洁,无偿提供序列化机制,同时可防止多次实例化,在实际中应该是最佳选择。

分享到:
评论

相关推荐

    Effective-Java读书笔记(上)

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

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

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

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

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

    单例模式(Singleton)的6种实现

    Java中的枚举类型是线程安全的,并且只会装载一次,设计者充分考虑到了线程安全问题,因此使用枚举实现单例模式是一种简洁而且高效的解决方案。 6. 容器式单例(Singleton Holder) 通过一个私有的静态内部类...

    C#面向对象设计模式纵横谈(2):Singleton 单件(创建型模式)

    1. **构造器的访问控制**:将构造器设为私有或受保护的,防止外部直接通过`new`关键字创建实例。 2. **静态实例成员**:定义一个静态的类成员变量,用于存储Singleton实例。 3. **公共访问方法**:提供一个公共的...

    如果我将构造函数声明为私有,可以在另一个类中调用此构造函数

    总的来说,私有构造函数在Java中用于实现特定的设计模式,如单例或隐藏类的初始化细节。尽管同一包内的类可以访问私有构造函数,但这种做法并不常见,而且往往违反了封装的原则。更常见的做法是通过静态工厂方法、...

    Java练习reflect,singleton,DomAndSax

    而“singleton method.txt”很可能是讲解或展示了不同类型的单例实现方式,包括传统的单例模式和基于枚举的单例实现。 通过深入学习和实践这三个主题,你将能更好地理解和掌握Java的核心特性,以及在实际项目中如何...

    JavaSE 面试题 (2).docx

    - 构造器私有化,防止其他类通过new关键字创建实例。 - 创建并保存该类的唯一实例,通常使用静态变量存储。 - 提供一个公共的静态方法或属性,以便外部获取这个唯一的实例。 常见的单例实现方式有以下几种: - **...

    单例模式.doc

    - **枚举方式**:利用枚举类型天然的线程安全性和唯一性实现单例模式,简洁高效。 #### 五、单例模式的适用场景 单例模式适用于以下情况: - 系统只需要一个实例对象时。 - 单例对象需要被共享,并且所有的实例...

    Java中控制创建对象的个数

    1. **私有构造器(Private Constructor)**:这是最常用的控制对象创建的方式,特别是用于实现单例模式。通过将类的构造器声明为私有,外部类无法直接实例化它。例如: ```java public class Singleton { private ...

    java面试常问的问题

    32. **Singleton模式**:确保一个类只有一个实例,通常通过私有构造器和静态工厂方法实现。 以上是对Java面试中常见问题的详细解答,涵盖了语言基础、集合、并发、异常处理、设计模式等多个方面。掌握这些知识点...

    设计模式面试专题.pdf

    在Java中,通过私有构造器和静态方法来实现,线程安全的单例可以使用枚举或双重检查锁定(double-checked locking)来创建。例如: ```java public enum Singleton { INSTANCE; } // 或者使用双重检查锁定 public...

    java面试题(2022)

    14. **构造器不可重写**:构造器不能被重写,但可以重载。 15. **equals与hashCode**:两个对象值相等但hash code不同是可能的,但违反了equals的一致性原则。 16. **值传递与引用传递**:Java中所有参数都是按值...

    单例设计模式个人总结+摘录

    1. **避免反射攻击**:在私有构造器中添加条件判断,如`if (instance != null)`,这样即便反射尝试创建新实例也会被阻止。 2. **避免反序列化攻击**:为单例类实现`readResolve()`方法,确保即使反序列化也不会创建...

    2024最新的面试题来了

    **使用枚举实现单例**:枚举类型的实例是在类加载时创建的,并且枚举类的构造方法默认是私有的,这使得通过反射创建新实例变得不可能。 ```java public enum Singleton { INSTANCE; // 单例类的其他方法... } ...

    java面向对象编程单实例模式解析

    ### Java面向对象编程中的单实例(Singleton)模式解析 #### 概述 在Java面向对象编程中,单...此外,随着Java语言的发展,还可以利用枚举类型来实现单实例模式,这种方式既简单又安全,值得在实际开发中考虑使用。

    day020-继承加强和设计模式代码和笔记.rar

    私有化构造方法 2. 在类中创建一个对象,并且用private、static、final修饰 private为了避免在外部直接访问当前对象 static是为了在静态方法中可以返回当前类中的对象 final:可加可不加...

    Java单例模式

    1. **私有构造器**: 单例类的构造器通常被声明为`private`,这样其他类无法通过`new`关键字直接创建该类的实例。这是防止外部代码实例化单例对象的第一道防线。 2. **静态内部类**: 在上述例子中,单例对象`...

Global site tag (gtag.js) - Google Analytics