单例模式(反射破坏-枚举)
饿汉式单例
- package com.jan.single;
- //饿汉式单例
- public class Hungry {
- //一上来就会加载好,可能会浪费空间
- private byte[] data11=new byte[1024*1024];
- private byte[] data12=new byte[1024*1024];
- private byte[] data13=new byte[1024*1024];
- private byte[] data14=new byte[1024*1024];
- private Hungry(){
- }
- private final static Hungry HUNGRY = new Hungry();//保证是唯一的了
- public static Hungry getInstance(){ //一上来就加载了
- return HUNGRY;
- }
- }
复制代码 懒汉式单例
- package com.jan.single;
- //懒汉式单例
- public class LazyMan {
- private LazyMan(){ ///构造器私有
- System.out.println(Thread.currentThread().getName()+"ok");
- }
- private static LazyMan lazyMan;//对象
- public static LazyMan getInstance(){
- if(lazyMan==null){
- lazyMan = new LazyMan();
- }
- return lazyMan;
- }
- //多线程并发
- public static void main(String[] args){
- for (int i = 0; i < 10; i++) {
- new Thread(()->{
- LazyMan.getInstance();
- }).start();
- }
- }
- }
复制代码 此时运行结果:单例有问题,偶尔会有多线程

静态内部类:
- package com.jan.single;
- //静态内部类
- public class Holder {
- private Holder(){ //构造器私有
- }
- public static Holder getInstance(){
- return InnerClass.HOLDER;
- }
- public static class InnerClass{
- private static final Holder HOLDER = new Holder();
- }
- }
复制代码 进阶-->加锁
- package com.jan.single;
- //懒汉式单例
- public class LazyMan {
- private LazyMan(){ ///构造器私有
- System.out.println(Thread.currentThread().getName()+"ok");
- }
- //为了安全,避免 lazyMan 指令重排,必须加volatile
- private volatile static LazyMan lazyMan;//对象
- public static LazyMan getInstance(){ //这代码安全吗?
- //双重检测锁模式的 懒汉式单例 DCL模式
- if (lazyMan==null){
- synchronized (LazyMan.class){ //保证这个类 LazyMan.class 只有一个
- if(lazyMan==null){
- lazyMan = new LazyMan(); //不是原子性操作
- /*
- * 1. 分配内存空间
- * 2. 执行构造方法,初始化对象
- * 3. 把这个对象指向这个空间
- *
- * 期望执行 123
- * 132 A(先占内存空间)
- * 若此时有 B线程 基于A,系统会认为 lazyMan 不为Null 直接return
- * //此时lazyMan没有完成构造
- * */
- }
- }
- }
- return lazyMan;
- }
- //多线程并发
- public static void main(String[] args){
- for (int i = 0; i < 10; i++) {
- new Thread(()->{
- LazyMan.getInstance();
- }).start();
- }
- }
- }
复制代码单例不安全,反射
枚举
炫技时刻 :反射破坏单例
- package com.jan.single;
- import java.lang.reflect.Constructor;
- //懒汉式单例
- public class LazyMan {
- private LazyMan(){ ///构造器私有
- System.out.println(Thread.currentThread().getName()+"ok");
- }
- //为了安全,避免 lazyMan 指令重排,必须加volatile
- private volatile static LazyMan lazyMan;//对象
- public static LazyMan getInstance(){ //这代码安全吗?
- //双重检测锁模式的 懒汉式单例 DCL模式
- if (lazyMan==null){
- synchronized (LazyMan.class){ //保证这个类 LazyMan.class 只有一个
- if(lazyMan==null){
- lazyMan = new LazyMan(); //不是原子性操作
-
- }
- }
- }
- return lazyMan;
- }
- //反射
- public static void main(String[] args) throws Exception {
- LazyMan instance1 = LazyMan.getInstance();
- Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
- declaredConstructor.setAccessible(true);//破坏私有权限,无视私有的构造器 通过反射来创建对象
- LazyMan instance2= declaredConstructor.newInstance();
- System.out.println(instance1);
- System.out.println(instance2);
- }
- }
复制代码 结果:

继续改进:不被反射破坏(三重检测)
- package com.jan.single;
- import java.lang.reflect.Constructor;
- //懒汉式单例
- public class LazyMan {
- private LazyMan(){ ///构造器私有
- synchronized (LazyMan.class){ //这里加一把锁,第二次用就会报错
- if (lazyMan!=null){
- throw new RuntimeException("不要试图使用反射破坏异常");
- }
- }
- }
- //为了安全,避免 lazyMan 指令重排,必须加volatile
- private volatile static LazyMan lazyMan;//对象
- public static LazyMan getInstance(){ //这代码安全吗?
- //双重检测锁模式的 懒汉式单例 DCL模式
- if (lazyMan==null){
- synchronized (LazyMan.class){ //保证这个类 LazyMan.class 只有一个
- if(lazyMan==null){
- lazyMan = new LazyMan(); //不是原子性操作
- /*
- * 1. 分配内存空间
- * 2. 执行构造方法,初始化对象
- * 3. 把这个对象指向这个空间
- *
- * 期望执行 123
- * 132 A(先占内存空间)
- * 若此时有 B线程 基于A,系统会认为 lazyMan 不为Null 直接return
- * //此时lazyMan没有完成构造
- * */
- }
- }
- }
- return lazyMan;
- }
-
- //反射
- public static void main(String[] args) throws Exception {
- LazyMan instance1 = LazyMan.getInstance();
- Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
- declaredConstructor.setAccessible(true);//破坏私有权限,无视私有的构造器 通过反射来创建对象
- LazyMan instance2= declaredConstructor.newInstance();
- System.out.println(instance1);
- System.out.println(instance2);
- }
- }
复制代码 结果:

继续破坏:
如果两个对象都是LazyMan instance2= declaredConstructor.newInstance();这样获得,单例又被破坏
解决:加密(jan)
- package com.jan.single;
- import java.lang.reflect.Constructor;
- //懒汉式单例
- public class LazyMan {
- private static boolean jan = false;//定义一个变量
- private LazyMan(){ ///构造器私有
- synchronized (LazyMan.class){ //这里加一把锁,第二次用就会报错
- if (jan==false){
- jan = true; //这个肯定会变
- }else {
- throw new RuntimeException("不要试图使用反射破坏异常");
- }
- }
- }
- //为了安全,避免 lazyMan 指令重排,必须加volatile
- private volatile static LazyMan lazyMan;//对象
- public static LazyMan getInstance(){ //这代码安全吗?
- //双重检测锁模式的 懒汉式单例 DCL模式
- if (lazyMan==null){
- synchronized (LazyMan.class){ //保证这个类 LazyMan.class 只有一个
- if(lazyMan==null){
- lazyMan = new LazyMan(); //不是原子性操作
- }
- }
- }
- return lazyMan;
- }
-
- //反射
- public static void main(String[] args) throws Exception {
- //LazyMan instance1 = LazyMan.getInstance();
- Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
- declaredConstructor.setAccessible(true);//破坏私有权限,无视私有的构造器 通过反射来创建对象
- LazyMan instance2= declaredConstructor.newInstance();
- LazyMan instance1= declaredConstructor.newInstance();
- System.out.println(instance1);
- System.out.println(instance2);
- }
- }
复制代码 再破坏:
通过获取加密字段,从而破坏私有权限,再设置加密字段.instance = false
- package com.jan.single;
- import java.lang.reflect.Constructor;
- import java.lang.reflect.Field;
- //懒汉式单例
- //道高一尺魔高一丈
- public class LazyMan {
- private static boolean jan = false;//定义一个变量
- private LazyMan(){ ///构造器私有
- synchronized (LazyMan.class){ //这里加一把锁,第二次用就会报错
- if (jan==false){
- jan = true; //这个肯定会变
- }else {
- throw new RuntimeException("不要试图使用反射破坏异常");
- }
- }
- }
- //为了安全,避免 lazyMan 指令重排,必须加volatile
- private volatile static LazyMan lazyMan;//对象
- public static LazyMan getInstance(){ //这代码安全吗?
- //双重检测锁模式的 懒汉式单例 DCL模式
- if (lazyMan==null){
- synchronized (LazyMan.class){ //保证这个类 LazyMan.class 只有一个
- if(lazyMan==null){
- lazyMan = new LazyMan(); //不是原子性操作
- }
- }
- }
- return lazyMan;
- }
-
- //反射
- public static void main(String[] args) throws Exception {
- //LazyMan instance1 =
- Field jan = LazyMan.class.getDeclaredField("jan");//获取字段
- jan.setAccessible(true);//破坏私有权限
- Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
- declaredConstructor.setAccessible(true);//破坏私有权限,无视私有的构造器 通过反射来创建对象
- LazyMan instance2= declaredConstructor.newInstance();
- jan.set(instance2,false);
- LazyMan instance1= declaredConstructor.newInstance();
- System.out.println(instance1);
- System.out.println(instance2);
- }
- }
复制代码 结果:

万恶的反射怎么解决:枚举
反编译jad:https://varaneckas.com/jad/- package com.jan.single;
- //枚举 是一个什么? 本身也是class 类
- public enum EnumSingle {
-
- INSTANCE;//不可能被拿走
-
- public EnumSingle getInstance(){
- return INSTANCE;
- }
-
- }
- class Test{ //先保证对象是唯一的,测试一下
- public static void main(String[] args) {
- EnumSingle instance1= EnumSingle.INSTANCE;
- EnumSingle instance2= EnumSingle.INSTANCE;
- System.out.println(instance1);
- System.out.println(instance2);
- }
- }
复制代码 结果:

看源码,有一个无参构造

在测试也使用无参构造
- package com.jan.single;
- import java.lang.reflect.Constructor;
- //枚举 是一个什么? 本身也是class 类
- public enum EnumSingle {
- INSTANCE;//不可能被拿走
- public EnumSingle getInstance(){
- return INSTANCE;
- }
- }
- class Test{ //试一试能不能破坏,看源代码 有一个无参构造
- public static void main(String[] args) throws Exception {
- EnumSingle instance1= EnumSingle.INSTANCE; //有一个无参构造,试一试,看看是不是真的
- Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
- declaredConstructor.setAccessible(true);
- EnumSingle instance2 = declaredConstructor.newInstance();
- //com.jan.single.EnumSingle.<init>()
- //(newInstance 源码)正常报错是:throw new IllegalArgumentException("Cannot reflectively create enum objects");
-
- System.out.println(instance1);
- System.out.println(instance2);
- }
- }
复制代码 结果:被欺骗,这源码不是真的

因为newinstance的源码:

两个输出的结果不同!!!
反编译
在IDEA的targer中找到 EnumSingle.class,打开文件的文件的位置,再从文件的卫视输入 cmd
也有空参,也是骗我们的

更专业的反编译
jad

这里是有参构造

修改代码,这里只展示不同的- Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
复制代码 结果:这个反射的确不能破坏枚举的单例

枚举最终反编译源码- // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
- // Jad home page: http://www.kpdus.com/jad.html
- // Decompiler options: packimports(3)
- // Source File Name: EnumSingle.java
- package com.jan.single;
- public final class EnumSingle extends Enum
- {
- public static EnumSingle[] values()
- {
- return (EnumSingle[])$VALUES.clone();
- }
- public static EnumSingle valueOf(String name)
- {
- return (EnumSingle)Enum.valueOf(com/jan/single/EnumSingle, name);
- }
- private EnumSingle(String s, int i)
- {
- super(s, i);
- }
- public EnumSingle getInstance()
- {
- return INSTANCE;
- }
- public static final EnumSingle INSTANCE;
- private static final EnumSingle $VALUES[];
- static
- {
- INSTANCE = new EnumSingle("INSTANCE", 0);
- $VALUES = (new EnumSingle[] {
- INSTANCE
- });
- }
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |