单例模式
一日一句: 有能力不一定有机会,但是有机会,可以倒逼着你有能力。
单例模式有以下特点:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
饿汉式和懒汉式的共同特点构造器私有化
private LazyMan(){//单例模式最重要的就是构造器私有(懒汉式)
}
private Hungry(){//单列模式最重要就是构造器私有(饿汉式)
}
饿汉式单例
package com.liang.single;
//饿汉式单例
public class Hungry {
//饿汉式未使用就已经加载了,浪费内存空间
private byte[] data1=new byte[1024*1024];
private byte[] data2=new byte[1024*1024];
private byte[] data3=new byte[1024*1024];
private byte[] data4=new byte[1024*1024];
private Hungry(){//单列模式最重要就是构造器私有
}
private final static Hungry HUNGRY = new Hungry();//上来就直接new一个,加载对象
public static Hungry getInstance(){
return HUNGRY;
}
}
DCL懒汉式单例
volatile避免指令重排
package com.liang.single;
//懒汉式单例
public class LazyMan {
private LazyMan(){//单例模式最重要的就是构造器私有
System.out.println(Thread.currentThread().getName()+"ok");
}
private volatile static LazyMan lazyMan;**//必须在这里加上一个volatile,这才是一个完整的双重监测锁模式
//volatile避免指令重排**
public static LazyMan getInstance(){
//加锁 双重监测锁模式的 懒汉式单例 DCL懒汉式
if (lazyMan==null){
synchronized (LazyMan.class){
if (lazyMan==null){
lazyMan = new LazyMan();//不是一个原子性操作
}
}
}
return lazyMan;
}
/**
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把对象指向这个空间
*
*123 希望
*132 万一 A
* B //此时lazyman还没完成构造
*
*/
//多线程的情况下,就出问题了
public static void main(String[] args){
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
这样输出就是一个线程了
反射! 只要有反射 任何代码都不安全 任何私有关键字都是纸老虎
使用反射破坏单例
那该如何破解反射呢?
答:再在上面加一个锁,三重锁
private LazyMan() {//单例模式最重要的就是构造器私有
synchronized (LazyMan.class){ //再在上面加一个锁,三重锁
if (lazyMan!=null){
throw new RuntimeException("不要试图使用反射破坏异常!");
}
}
package com.liang.single;
import java.lang.reflect.Constructor;
//懒汉式单例
public class LazyMan {
private LazyMan() {//单例模式最重要的就是构造器私有
synchronized (LazyMan.class){ //再在上面加一个锁,三重锁
if (lazyMan!=null){
throw new RuntimeException("不要试图使用反射破坏异常!");
}
}
System.out.println(Thread.currentThread().getName() + "ok");
}
private volatile static LazyMan lazyMan;//必须在这里加上一个volatile,这才是一个完整的双重监测锁模式
//volatile避免指令重排
public static LazyMan getInstance() {
//加锁 双重监测锁模式的 懒汉式单例 DCL懒汉式
if (lazyMan == null) {
synchronized (LazyMan.class) {
if (lazyMan == null) {
lazyMan = new LazyMan();//不是一个原子性操作
}
}
}
return lazyMan;
}
//反射! 只要有反射 任何代码都不安全 任何私有关键字都是纸老虎
//使用反射破坏单例
public static void main(String[] args) throws Exception {
LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//空参构造器
declaredConstructor.setAccessible(true);//无视私有构造器
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
/**
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把对象指向这个空间
*
*123 希望
*132 万一 A
* B //此时lazyman还没完成构造
*
*/
//多线程的情况下,就出问题了
// public static void main(String[] args){
// for (int i = 0; i < 10; i++) {
// new Thread(()->{
// LazyMan.getInstance();
// }).start();
//
// }
// }
}
静态内部类
package com.liang.single;
//静态内部类
public class Holder {
private Holder(){//单例模式最重要的就是构造器私有
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
public static final Holder HOLDER = new Holder();
}
}
单例不安全,反射
枚举 enum
什么是枚举?
答:enum是一个什么?jdk1.5之后就有了 枚举本身也是一个class类 //反射不能破坏枚举
枚举类型的最终反编译源码
反射中采用newInstance方法废弃,可采用getDeclaredConstructor().newInstance()方法来得到类对象