单例模式(反射破坏-枚举)
饿汉式单例
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
});
}
}
标签:EnumSingle,class,模式,public,LazyMan,static,单例,lazyMan
From: https://www.cnblogs.com/zhongjianYuan/p/17447178.html