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

一.描述

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模式的最佳方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值