java单例模式详解完美实现(包括反射破坏的防止和线程安全)
2016-07-27 11:17
627 查看
一.描述
Singleton(单例)是设计模式的一种,为了保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要用途是保证某个很占系统资源的类,在同一时间只能拥有一个的情况。
例如:一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;
一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。
二.单例模式的主要特点:
1.某个类只能有一个实例;
2.必须自行创建这个实例;
3.必须自行向整个系统提供这个实例。
三.简单的具体实现:
第一种:懒汉式 (线程不安全)
第二种 懒汉 线程安全
四. 反射破坏单例
看似单例好像私有了构造方法。无法访问,只能通过getInstance()得到唯一的实例对象。实际上java的反射技术可以破坏单例
最后运行结果s3的name = s4 说明s3和s4是同一个单例,
但是s1,s2却成功的创建了新的实例化对象。
这就破坏了单例模式
所以我们必须做出防范。
五.防止反射破坏的单例模式。
结果是抛出异常 此时既然s1已经存在,就无法getInstance 或newInstance 得到s2
六. 进阶 --- enum枚举完美实现单例模式
jdk1.5以后的单例模式 里面enum实现
创建一个只有一个实例的enum 不仅可以防止反射破坏,还可以防止序列化破坏单例
写法也非常简单
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模式的最佳方法。
相关文章推荐
- 设计模式之创建型模式 - 特别的变量问题
- C#单例模式(Singleton Pattern)实例教程
- .NET中保证线程安全的高级方法Interlocked类使用介绍
- php设计模式之单例、多例设计模式的应用分析
- javascript 单例/单体模式(Singleton)
- Java线程安全中的单例模式
- 深入线程安全容器的实现方法
- PHP 线程安全与非线程安全版本的区别深入解析
- C++单例模式应用实例
- 深入理解线程安全与Singleton
- 浅谈Java编程中的单例设计模式
- Python单例模式实例分析
- java多线程之线程安全的单例模式
- Java单例模式实例简述
- 新手谈设计模式 - 单例模式
- Java BlockingQueue
- JAVA语言泛型编程实现单例模式
- 多线程问题及处理方法【转】
- Swift之单例模式
- Java之线程安全的简单理解