您的位置:首页 > 编程语言 > Java开发

java单例模式详解完美实现(包括反射破坏的防止和线程安全)

2016-07-27 11:17 627 查看
一.描述 

Singleton(单例)是设计模式的一种,为了保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要用途是保证某个很占系统资源的类,在同一时间只能拥有一个的情况。

例如:一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;

一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。

二.单例模式的主要特点:

1.某个类只能有一个实例;

2.必须自行创建这个实例;

3.必须自行向整个系统提供这个实例。

三.简单的具体实现:

第一种:懒汉式 (线程不安全)

public class SingletonDemo {
private static SingletonDemo single = null;
//私有的构造方法,无法主动实例化这个类
private SingletonDemo(){}

//静态工厂方法 通过getInstance得到具体的单例对象
public static SingletonDemo getInstance(){
if(single == null){
single = new SingletonDemo();
}
return single;
}
}


第二种 懒汉 线程安全
package com.hjh.Singleton;

/**
*
* @author Administrator
* 	懒汉式单例 线程安全
*	把构造方法private 无法主动实例化Singletondemo类
*	通过getInstance 实例化对象
*/
public class SingletonDemo {
private static SingletonDemo single = null;

private SingletonDemo(){}

//静态工厂方法
public static synchronized SingletonDemo getInstance(){
if(single == null){
single = new SingletonDemo();
}
return single;
}

}


四. 反射破坏单例
看似单例好像私有了构造方法。无法访问,只能通过getInstance()得到唯一的实例对象。实际上java的反射技术可以破坏单例

public class ReflectSingleton {
private static ReflectSingleton singleton = null;

private ReflectSingleton(){};

private String name = "singleton";

/**
* 懒汉式线程安全 单例
*/
public static synchronized ReflectSingleton getInstance(){
//常量写在前面可以避免一些小错误 比如 == 写成 =
if(null == singleton){
singleton = new ReflectSingleton();
}
return singleton;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

/**
* 利用反射破坏单例
*/
public static void main(String[] args) {
try {
//得到构造方法
Constructor cons = ReflectSingleton.class.getDeclaredConstructor();
//把构造方法设为可访问 private失效
cons.setAccessible(true);
//利用反射实例化的单例对象
ReflectSingleton s1 = (ReflectSingleton) cons.newInstance();
s1.setName("s1");

ReflectSingleton s2 = (ReflectSingleton) cons.newInstance();
s2.setName("s2");

//常规方法实例化的对象
ReflectSingleton s3 = ReflectSingleton.getInstance();
s3.setName("s3");

ReflectSingleton s4 = ReflectSingleton.getInstance();
s4.setName("s4");

System.out.println("s1的name = "+s1.getName() );
System.out.println("s2的name = "+s2.getName() );
System.out.println("s3的name = "+s3.getName() );
System.out.println("s4的name = "+s4.getName() );

} catch (NoSuchMethodException | SecurityException 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 (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}


最后运行结果s3的name = s4 说明s3和s4是同一个单例,
但是s1,s2却成功的创建了新的实例化对象。
这就破坏了单例模式

所以我们必须做出防范。

五.防止反射破坏的单例模式。

package com.hjh.Reflect;

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

/**
* 防止反射破坏的安全单例
*/
public class SafeSingleton {

private String name = "singleton";
private static boolean flag = false;

private SafeSingleton(){
synchronized (this){
//如果没有创建,就创建实例
if(false == flag){
flag = true;
}else{
throw new RuntimeException("单例对象已经创建,不能反复创建");
}
}
};
//static 在 jvm 创建时就会调用该实例化方法创建单例
private  static class SingletonHolder{
private static final SafeSingleton INSTANCE = new SafeSingleton();
}

public static synchronized SafeSingleton getInstance(){
//返回这个实例化对象
return SingletonHolder.INSTANCE;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

/**
* 利用反射破坏单例
*/
public static void main(String[] args) {
try {
//得到构造方法
Constructor cons = SafeSingleton.class.getDeclaredConstructor();
//把构造方法设为可访问 private失效
cons.setAccessible(true);
//利用反射实例化的单例对象
SafeSingleton s1 = (SafeSingleton) cons.newInstance();
s1.setName("s1");

//常规方法实例化的对象
SafeSingleton s2 = SafeSingleton.getInstance();
s2.setName("s2");

} catch (NoSuchMethodException | SecurityException 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 (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}


结果是抛出异常 此时既然s1已经存在,就无法getInstance 或newInstance 得到s2

六. 进阶 --- enum枚举完美实现单例模式

jdk1.5以后的单例模式 里面enum实现
创建一个只有一个实例的enum 不仅可以防止反射破坏,还可以防止序列化破坏单例
写法也非常简单
public enum SingletonClass
{
INSTANCE;

public void test()
{
System.out.println("The Test!");
}
}
单元素的枚举类型已经成为实现Singleton模式的最佳方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息