目录
java单例模式--懒汉式、饿汉式
1、要点
2、单例模式的类型(五种)
2.1 饿汉式
2.2 枚举饿汉式(推荐使用)
2.3 懒汉式单例
2.4 DCL懒汉式(双检锁的懒汉式)
2.5 内部类懒汉式(推荐使用)
3、jdk中的体现
1、饿汉式的单例模式
2、枚举饿汉式
3、懒汉式的DCL(双检锁)
4、内部类懒汉式
单例模式,属于创建类型的一种常用的软件设计模式。(21种设计模式之一)通过单例模式的方法创建的类在当前进程中只有一个实例
构造器私有化
定义静态变量存储这个对象唯一实例
直接暴露或者封装使用getter方法提供实例的获取
饿汉式、枚举饿汉式、懒汉式、双锁懒汉式、内部类懒汉式 五种
饿汉式:即当类初始化的时候就创建实例对象 --就叫做饿汉式
代码
package com.sofwin.singletonDemo;
/*** @packageName: com.sofwin.singletonDemo* @author: wentao* @date: 2022/11/21 10:00* @version: 1.0* @email 1660420659@qq.com* @description: 单例模式-饿汉式** 要点:* 1.构造器私有化* 2.定义静态常量存储对象唯一实例* 3.通过getter方法获取实例对象*/
public class Singleton01 {
/*** 构造器私有化*/private static final Singleton01 SINGLETON_01 = new Singleton01();
private Singleton01() {System.out.println("private Singleton01");}
public static Singleton01 getSingleton01() {return SINGLETON_01;}
/*** 通过调用静态方法,实现类的初始化* 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建*/public static void otherMethod() {System.out.println("判断是懒汉式还是饿汉式");}
}
测试
package com.sofwin.singletonDemo;
import org.junit.Test;
/*** @packageName: com.sofwin.singletonDemo* @author: wentao* @date: 2022/11/21 10:09* @version: 1.0* @email 1660420659@qq.com* @description: 单例模式的测试*/
public class SingletonTest {
//单例模式-饿汉式@Testpublic void SinletonTest01() {//先判断是否类初始化的时候就创建对象了Singleton01.otherMethod();System.out.println("=================");Singleton01 singleton01 = Singleton01.getSingleton01();Singleton01 singleton02 = Singleton01.getSingleton01();System.out.println(singleton01);System.out.println(singleton02);}
}
(类加载就创建实例---饿汉式)
这种设计有三种方法可以进行破坏
1、反射破坏单例
package com.sofwin.singletonDemo;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/*** @packageName: com.sofwin.singletonDemo* @author: wentao* @date: 2022/11/21 10:09* @version: 1.0* @email 1660420659@qq.com* @description: 单例模式的测试*/
public class SingletonTest {
//单例模式-饿汉式@Testpublic void SinletonTest01() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {//先判断是否类初始化的时候就创建对象了Singleton01.otherMethod();System.out.println("=================");Singleton01 singleton01 = Singleton01.getSingleton01();Singleton01 singleton02 = Singleton01.getSingleton01();System.out.println(singleton01);System.out.println(singleton02);System.out.println("暴力破解");SingletonTest.reflection(Singleton01.class);}
/*** 反射破坏单例pos* @param clazz 类对象*/public static void reflection(Class clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {//获取无参构造方法Constructor declaredConstructor = clazz.getDeclaredConstructor();//暴力破解declaredConstructor.setAccessible(true);//通过反射创建实例对象Object o = declaredConstructor.newInstance();System.out.println(o);}
}
通过反射暴力破解,得到新的实例对象
改进
package com.sofwin.singletonDemo;
/*** @packageName: com.sofwin.singletonDemo* @author: wentao* @date: 2022/11/21 10:00* @version: 1.0* @email 1660420659@qq.com* @description: 单例模式-饿汉式** 要点:* 1.构造器私有化* 2.定义静态常量存储对象唯一实例* 3.通过getter方法获取实例对象*/
public class Singleton01 {
/*** 构造器私有化*/private static final Singleton01 SINGLETON_01 = new Singleton01();
private Singleton01() {//防止反射破坏单例if (SINGLETON_01 !=null) {throw new RuntimeException("单例对象不能重复创建");}System.out.println("private Singleton01");}
public static Singleton01 getSingleton01() {return SINGLETON_01;}
/*** 通过调用静态方法,实现类的初始化* 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建*/public static void otherMethod() {System.out.println("判断是懒汉式还是饿汉式");}
}
2、反序列化破坏单例
Singleton01类要实现Serializable
package com.sofwin.singletonDemo;
import java.io.Serializable;
/*** @packageName: com.sofwin.singletonDemo* @author: wentao* @date: 2022/11/21 10:00* @version: 1.0* @email 1660420659@qq.com* @description: 单例模式-饿汉式** 要点:* 1.构造器私有化* 2.定义静态常量存储对象唯一实例* 3.通过getter方法获取实例对象*/
public class Singleton01 implements Serializable {
/*** 构造器私有化*/private static final Singleton01 SINGLETON_01 = new Singleton01();
private Singleton01() {//防止反射破坏单例if (SINGLETON_01 !=null) {throw new RuntimeException("单例对象不能重复创建");}System.out.println("private Singleton01");}
public static Singleton01 getSingleton01() {return SINGLETON_01;}
/*** 通过调用静态方法,实现类的初始化* 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建*/public static void otherMethod() {System.out.println("判断是懒汉式还是饿汉式");}
}
测试代码
package com.sofwin.singletonDemo;
import org.junit.Test;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/*** @packageName: com.sofwin.singletonDemo* @author: wentao* @date: 2022/11/21 10:09* @version: 1.0* @email 1660420659@qq.com* @description: 单例模式的测试*/
public class SingletonTest {
//单例模式-饿汉式@Testpublic void SinletonTest01() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {//先判断是否类初始化的时候就创建对象了Singleton01.otherMethod();System.out.println("=================");Singleton01 singleton01 = Singleton01.getSingleton01();Singleton01 singleton02 = Singleton01.getSingleton01();System.out.println(singleton01);System.out.println(singleton02);
// System.out.println("反射暴力破解");
// SingletonTest.reflection(Singleton01.class);System.out.println("反序列化破解");SingletonTest.serialzable(singleton02);}
/*** 反射破坏单例pos* @param clazz 类对象*/public static void reflection(Class clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {//获取无参构造方法Constructor declaredConstructor = clazz.getDeclaredConstructor();//暴力破解declaredConstructor.setAccessible(true);//通过反射创建实例对象Object o = declaredConstructor.newInstance();System.out.println(o);}
/*** 反序列破解单例模式* @param instance 对象*/public static void serialzable(Object instance) throws IOException, ClassNotFoundException {//将对象读取到baos中ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(instance);//通过字节数组获取对象ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));Object o = ois.readObject();System.out.println(o);}
}
改进
package com.sofwin.singletonDemo;
import java.io.Serializable;
/*** @packageName: com.sofwin.singletonDemo* @author: wentao* @date: 2022/11/21 10:00* @version: 1.0* @email 1660420659@qq.com* @description: 单例模式-饿汉式** 要点:* 1.构造器私有化* 2.定义静态常量存储对象唯一实例* 3.通过getter方法获取实例对象*/
public class Singleton01 implements Serializable {
/*** 构造器私有化*/private static final Singleton01 SINGLETON_01 = new Singleton01();
private Singleton01() {//防止反射破坏单例if (SINGLETON_01 !=null) {throw new RuntimeException("单例对象不能重复创建");}System.out.println("private Singleton01");}
public static Singleton01 getSingleton01() {return SINGLETON_01;}
/*** 通过调用静态方法,实现类的初始化* 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建*/public static void otherMethod() {System.out.println("判断是懒汉式还是饿汉式");}
/*** 解决反序列化* @return 实例对象* */public Object readResolve() {return SINGLETON_01;}
}
这里 readResolve() 方法的解释
简而言之,当我们通过反序列化readObejct方法获取对象的时候,会去寻找readResolve()方法,如果该方法不存在则直接返回新对象,如果该方法存在则按照该方法的内容返回对象,以确保如果我们之前实例化了单例对象,就返回该对象。
为什么要使用功能readResolve()方法就解决了?
这里看一下重点代码,readOrdinaryObject
方法的代码片段:
isInstantiable
:如果一个serializable/externalizable的类可以在运行时被实例化,那么该方法就返回true。针对serializable和externalizable我会在其他文章中介绍。
desc.newInstance
:该方法通过反射的方式调用无参构造方法新建一个对象。
然后关键是下面的代码
if (obj != null &&handles.lookupException(passHandle) == null &&desc.hasReadResolveMethod())
{Object rep = desc.invokeReadResolve(obj);if (unshared && rep.getClass().isArray()) {rep = cloneArray(rep);}if (rep != obj) {// Filter the replacement objectif (rep != null) {if (rep.getClass().isArray()) {filterCheck(rep.getClass(), Array.getLength(rep));} else {filterCheck(rep.getClass(), -1);}}handles.setObject(passHandle, obj = rep);}
}
hasReadResolveMethod
:如果实现了serializable 或者 externalizable接口的类中包含readResolve
则返回true
invokeReadResolve
:通过反射的方式调用要被反序列化的类的readResolve方法。
所以,原理也就清楚了,主要在Singleton中定义readResolve方法,并在该方法中指定要返回的对象的生成策略,就可以防止单例被破坏。
3、Unsafe破坏单例
package com.sofwin.singletonDemo;
import org.junit.Test;
import org.springframework.objenesis.instantiator.util.UnsafeUtils;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/*** @packageName: com.sofwin.singletonDemo* @author: wentao* @date: 2022/11/21 10:09* @version: 1.0* @email 1660420659@qq.com* @description: 单例模式的测试*/
public class SingletonTest {
//单例模式-饿汉式@Testpublic void SinletonTest01() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {//先判断是否类初始化的时候就创建对象了Singleton01.otherMethod();System.out.println("=================");Singleton01 singleton01 = Singleton01.getSingleton01();Singleton01 singleton02 = Singleton01.getSingleton01();System.out.println(singleton01);System.out.println(singleton02);
// System.out.println("反射暴力破解");
// SingletonTest.reflection(Singleton01.class);
// System.out.println("反序列化破解");
// SingletonTest.serialzable(singleton02);System.out.println("unsafe");unsafe(singleton01.getClass());}
/*** 反射破坏单例pos* @param clazz 类对象*/public static void reflection(Class clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {//获取无参构造方法Constructor declaredConstructor = clazz.getDeclaredConstructor();//暴力破解declaredConstructor.setAccessible(true);//通过反射创建实例对象Object o = declaredConstructor.newInstance();System.out.println(o);}
/*** 反序列破解单例模式* @param instance 对象*/public static void serialzable(Object instance) throws IOException, ClassNotFoundException {//将对象读取到baos中ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(instance);//通过字节数组获取对象ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));Object o = ois.readObject();System.out.println(o);}
/*** 使用spring的工具类在jdk底层来访问,无序调用构造方法* @param clazz* @throws InstantiationException*/public static void unsafe(Class clazz) throws InstantiationException {Object o = UnsafeUtils.getUnsafe().allocateInstance(clazz);System.out.println(o);}
}
这个是无法解决的
代码
package com.sofwin.singletonDemo;
import sun.dc.pr.PRError;
/*** @packageName: com.sofwin.singletonDemo* @author: wentao* @date: 2022/11/21 11:34* @version: 1.0* @email 1660420659@qq.com* @description: 单例模式 --枚举饿汉式*/
public enum Singleton02 {INSTANCE;
private Singleton02() {System.out.println("private Singleton02");}
public static Singleton02 getInstance() {return INSTANCE;}
/*** 通过调用静态方法,实现类的初始化* 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建*/public static void otherMethod() {System.out.println("判断是懒汉式还是饿汉式");}
@Overridepublic String toString() {//Integer.toHexString 转换为16进制return getClass().getName()+"@"+Integer.toHexString(hashCode());}
}
测试
package com.sofwin.singletonDemo;
import org.junit.Test;
import org.springframework.objenesis.instantiator.util.UnsafeUtils;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/*** @packageName: com.sofwin.singletonDemo* @author: wentao* @date: 2022/11/21 10:09* @version: 1.0* @email 1660420659@qq.com* @description: 单例模式的测试*/
public class SingletonTest {
//单例模式-饿汉式@Testpublic void SinletonTest01() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {//先判断是否类初始化的时候就创建对象了Singleton01.otherMethod();System.out.println("=================");Singleton02 singleton01 = Singleton02.INSTANCE;Singleton02 singleton02 = Singleton02.INSTANCE;System.out.println(singleton01);System.out.println(singleton02);//1、反射不存在,枚举没有无参构造器
// System.out.println("反射暴力破解");
// SingletonTest.reflection(Singleton02.class);//2、反序列化不存在,readObject的时候会进行判断 ,如果是枚举类有特殊处理
// System.out.println("反序列化破解");
// SingletonTest.serialzable(singleton02);//3、jdk这个是无法避免的,是可以破坏的
// System.out.println("unsafe");
// unsafe(singleton01.getClass());}
/*** 反射破坏单例pos* @param clazz 类对象*/public static void reflection(Class clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {//获取无参构造方法Constructor declaredConstructor = clazz.getDeclaredConstructor();//暴力破解declaredConstructor.setAccessible(true);//通过反射创建实例对象Object o = declaredConstructor.newInstance();System.out.println(o);}
/*** 反序列破解单例模式* @param instance 对象*/public static void serialzable(Object instance) throws IOException, ClassNotFoundException {//将对象读取到baos中ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(instance);//通过字节数组获取对象ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));Object o = ois.readObject();System.out.println(o);}
/*** 使用spring的工具类在jdk底层来访问,无序调用构造方法* @param clazz* @throws InstantiationException*/public static void unsafe(Class clazz) throws InstantiationException {Object o = UnsafeUtils.getUnsafe().allocateInstance(clazz);System.out.println(o);}
}
1、反射无法破坏枚举类单例
枚举没有无参构造
修改成有参构造
/*** 反射破坏单例pos* @param clazz 类对象*/
public static void reflection(Class clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {//获取无参构造方法Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class,int.class);//暴力破解declaredConstructor.setAccessible(true);//通过反射创建实例对象Object o = declaredConstructor.newInstance("OTHER",1);System.out.println(o);
}
2、反序列话也无法破坏枚举类的单例
3、Unsafe是可以破坏的
代码
package com.sofwin.singletonDemo;
import java.io.Serializable;
/*** @packageName: com.sofwin.singletonDemo* @author: wentao* @date: 2022/11/21 10:00* @version: 1.0* @email 1660420659@qq.com* @description: 单例模式-懒汉式** 要点:* 1.构造器私有化* 2.定义静态常量存储对象唯一实例* 3.通过getter方法获取实例对象*/
public class Singleton03 implements Serializable {
/*** 构造器私有化*/private static Singleton03 instance = null;
private Singleton03() {//防止反射破坏单例if (instance !=null) {throw new RuntimeException("单例对象不能重复创建");}System.out.println("private Singleton01");}
public static Singleton03 getSingleton01() {if (instance == null) {instance = new Singleton03();}return instance;}
/*** 通过调用静态方法,实现类的初始化* 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建*/public static void otherMethod() {System.out.println("判断是懒汉式还是饿汉式");}
/*** 解决反序列化* @return 实例对象**/public Object readResolve() {return instance;}
}
测试
package com.sofwin.singletonDemo;
import org.junit.Test;
import org.springframework.objenesis.instantiator.util.UnsafeUtils;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/*** @packageName: com.sofwin.singletonDemo* @author: wentao* @date: 2022/11/21 10:09* @version: 1.0* @email 1660420659@qq.com* @description: 单例模式的测试*/
public class SingletonTest {
//单例模式-饿汉式@Testpublic void SinletonTest01() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {//先判断是否类初始化的时候就创建对象了Singleton03.otherMethod();System.out.println("=================");Singleton03 singleton01 = Singleton03.getSingleton01();Singleton03 singleton02 = Singleton03.getSingleton01();System.out.println(singleton01);System.out.println(singleton02);//1、反射可以破坏单例
// System.out.println("反射暴力破解");
// SingletonTest.reflection(Singleton03.class);//2、反序列化也可以破坏
// System.out.println("反序列化破解");
// SingletonTest.serialzable(singleton02);//3、jdk这个是无法避免的,是可以破坏的
// System.out.println("unsafe");
// unsafe(singleton01.getClass());}
/*** 反射破坏单例pos* @param clazz 类对象*/public static void reflection(Class clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {//获取无参构造方法Constructor declaredConstructor = clazz.getDeclaredConstructor();//暴力破解declaredConstructor.setAccessible(true);//通过反射创建实例对象Object o = declaredConstructor.newInstance();System.out.println(o);}
/*** 反序列破解单例模式* @param instance 对象*/public static void serialzable(Object instance) throws IOException, ClassNotFoundException {//将对象读取到baos中ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(instance);//通过字节数组获取对象ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));Object o = ois.readObject();System.out.println(o);}
/*** 使用spring的工具类在jdk底层来访问,无序调用构造方法* @param clazz* @throws InstantiationException*/public static void unsafe(Class clazz) throws InstantiationException {Object o = UnsafeUtils.getUnsafe().allocateInstance(clazz);System.out.println(o);}
}
反射、序列化和unsafe都可以破坏
但是这样都会导致一个问题多线程的情况会出现错误情况 (之前,懒汉式的常量和静态代码块中的代码,其实是jvm底层维护的是线程安全的,无序考虑) 因为这个是方法中创建实例就会出现线程安全的问题
测试代码
public class SingletonTest {
public static void main(String[]args){new Thread( new Runnable() {@Overridepublic void run() {Singleton03 singleton01 = Singleton03.getSingleton01();System.out.println(singleton01);}}).start();
new Thread( new Runnable() {@Overridepublic void run() {Singleton03 singleton01 = Singleton03.getSingleton01();System.out.println(singleton01);}}).start();}}
正常应该是这种情况:
但是多运行几次,也会出现这样的情况:
造成的这种情况的结果是问题是:
这个时候就出现了DCL懒汉式(双检锁的懒汉式)
package com.sofwin.singletonDemo;
import java.io.Serializable;
/*** @packageName: com.sofwin.singletonDemo* @author: wentao* @date: 2022/11/21 10:00* @version: 1.0* @email 1660420659@qq.com* @description: 单例模式-懒汉式** 要点:* 1.构造器私有化* 2.定义静态常量存储对象唯一实例* 3.通过getter方法获取实例对象*/
public class Singleton04 implements Serializable {
/*** 构造器私有化*///这里加入valatile是保证这个变量是有序的,可见的//防止指令重排序,在赋值语句后加入一个内存屏障,防止之前的赋值操作,进行重新排序private static volatile Singleton04 instance = null;
private Singleton04() {//防止反射破坏单例if (instance !=null) {throw new RuntimeException("单例对象不能重复创建");}System.out.println("private Singleton01");}
public static Singleton04 getSingleton01() {if (instance == null) {synchronized (Singleton04.class){if (instance == null) {instance = new Singleton04();}}}return instance;}
/*** 通过调用静态方法,实现类的初始化* 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建*/public static void otherMethod() {System.out.println("判断是懒汉式还是饿汉式");}
/*** 解决反序列化* @return 实例对象**/public Object readResolve() {return instance;}
}
这个代码可以这样理解:
public static Singleton04 getSingleton01() {
if (instance == null) {
synchronized (Singleton04.class){
if (instance == null) {
instance = new Singleton04();
}
}
//里面这一块是进行加锁放在出现上面的情况
//外面的if判断是进行代码的优化
//我们这个线程锁只是还没创建对象实例的情况出现,因此加上if后
//我们就保证了后序的调用无需进行加锁操作,加快了效率
}
return instance;
}
package com.sofwin.singletonDemo;
import java.io.Serializable;
/*** @packageName: com.sofwin.singletonDemo* @author: wentao* @date: 2022/11/21 10:00* @version: 1.0* @email 1660420659@qq.com* @description: 单例模式-内部类懒汉式** 要点:* 内部类不会随着玩不类的加载而初始化* 它是单独进行初始化和加载的,并且静态成员的赋值,都是在静态代码块中的,是底层保证线程安全的* 只有调用内部类的类变量的时候,才进行初始化,实现懒汉式模式*/
public class Singleton05 implements Serializable {
/*** 构造器私有化*/private Singleton05() {System.out.println("private Singleton01");}
private static class Holder {static Singleton05 instance = new Singleton05();}
public static Singleton05 getSingleton01() {return Holder.instance;}
/*** 通过调用静态方法,实现类的初始化* 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建*/public static void otherMethod() {System.out.println("判断是懒汉式还是饿汉式");}
}
System的gc() 垃圾回收机制中的Runtime.getRuntime().gc()
public class Runtime {private static Runtime currentRuntime = new Runtime();
/*** Returns the runtime object associated with the current Java application.* Most of the methods of class Runtime
are instance* methods and must be invoked with respect to the current runtime object.** @return the Runtime
object associated with the current* Java application.*/public static Runtime getRuntime() {return currentRuntime;}
/** Don't let anyone else instantiate this class */private Runtime() {}
Comparator的naturalOrder()
System中的cons成员变量
Collections中的emptyListInterator()
上一篇: 圣诞节的作文300字
下一篇:HS编码是什么意思请指教