赞
踩
五种实现方式:
- //饿汉式
- public class Singleton1 implements Serializable {
- private static final Singleton1 SINGLETON_TEST=new Singleton1();
- //构造私有
- private Singleton1() {
- System.out.println("私有构造方法");
- }
- public static Singleton1 getInstance(){
- return SINGLETON_TEST;
- }
- public static void otherMotherd(){
- System.out.println("其他方法");
- }
- }
测试:
-
- public class SingleTest {
- public static void main(String[] args) {
- Singleton1.otherMotherd();//检测是饿汉式
- //私有构造方法
- //其他方法
- System.out.println(Singleton1.getInstance());//Singleton1@4d7e1886
- System.out.println(Singleton1.getInstance());//Singleton1@4d7e1886
- //以上两个实例为同一个对象
- }
- }
结果:
当单例类实现了Serializable接口时,对象被序列化后再反序列化时会创建新的实例。为了避免这种情况,可以通过重写readResolve()方法来返回单例实例。
测试反序列化后的对象和原始对象是否一致:
- /**
- * 实现序列化接口后可能破坏单例模式
- */
- @Test
- void test5() throws Exception {
- //将对象写入文件中
- ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("single1.ser"));
- oos.writeObject(Singleton1.getInstance());
- oos.close();
- //从文件中读取对象
- ObjectInputStream ois=new ObjectInputStream(new FileInputStream("single1.ser"));
- Singleton1 single = (Singleton1) ois.readObject();
- ois.close();
- System.out.println("original:"+Singleton1.getInstance().hashCode());
- System.out.println("DeSerializable:"+single.hashCode());
- }
结果:
预防:
- public class Singleton implements Serializable {
- private static final Singleton instance = new Singleton();
-
- private Singleton() {}
-
- public static Singleton getInstance() {
- return instance;
- }
-
- protected Object readResolve() {
- return instance;
- }
- }
通过反射可以调用类的私有构造方法,从而创建多个实例。为了防止通过反射破坏单例模式,可以在构造方法中添加逻辑判断,确保只创建一个实例。
反射破坏单例:
- @Test
- void test() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
- Singleton1 instance = Singleton1.getInstance();
- Constructor<? extends Singleton1> constructor = instance.getClass().getDeclaredConstructor();
- constructor.setAccessible(true);
- Singleton1 singleton1 = constructor.newInstance();
- System.out.println(instance==singleton1);
- }
结果:
预防:在构造方法处进行判断
-
- //饿汉式
- public class Singleton2 implements Serializable {
- private static final Singleton2 SINGLETON_TEST=new Singleton2();
- //构造私有
- private Singleton2() {
- if (SINGLETON_TEST!=null){
- throw new IllegalStateException("对象已经创建");
- }
- System.out.println("私有构造方法");
- }
- public static Singleton2 getInstance(){
-
- return SINGLETON_TEST;
- }
-
- public static void otherMotherd(){
- System.out.println("其他方法");
- }
- }
以上防止反射破坏单例测试:
- /**
- *防止单例被破坏
- */
- @Test
- void test3() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
- Singleton2 instance = Singleton2.getInstance();
- Constructor<? extends Singleton2> constructor = instance.getClass().getDeclaredConstructor();
- constructor.setAccessible(true);
- Singleton2 singleton2 = constructor.newInstance();
- System.out.println(instance==singleton2);
- }
结果:
如果使用不同的类加载器加载同一个类,也可能导致创建多个实例。为了解决这个问题,可以在获取实例时指定类加载器,确保只有一个实例被创建。
测试使用不同的类加载器导致同一个类创建多个实例的情况:
- public static void main(String[] args) throws Exception {
- CurstomLoader loader1=new CurstomLoader();
- CurstomLoader loader2=new CurstomLoader();
- Class<?> single1 = loader1.loadClass("com.example.singletonmodle.single.Singleton1");
- Class<?> single2 = loader2.loadClass("com.example.singletonmodle.single.Singleton1");
- Singleton1 instance1 = (Singleton1) single1.newInstance();
- Singleton1 instance2= (Singleton1) single2.newInstance();
- System.out.println("loader1:"+instance1.hashCode());
- System.out.println("loader2:"+instance2.hashCode());
- }
- public class Singleton {
- private static Singleton instance = new Singleton();
-
- private Singleton() {}
-
- public static Singleton getInstance(ClassLoader classLoader) {
- synchronized (Singleton.class) {
- if (instance == null) {
- try {
- Class<?> clazz = classLoader.loadClass(Singleton.class.getName());
- instance = (Singleton) clazz.newInstance();
- } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
- e.printStackTrace();
- }
- }
- }
- return instance;
- }
- }
总结:多线程破坏单例使用DCL+voliatle预防,反射破坏单例使用在构造方法处检查对象是否存在进行预防,实现序列化接口破坏单例使用实现readResolve方法,返回已经创建的单例对象。unsafe目前还没有预防的方式。
-
- public enum Singleton3 {
- INSTANCE;
- private Singleton3(){
- System.out.println("枚举类的私有构造方法");
- }
-
- @Override
- public String toString() {
- //getClass().getName()返回该对象的类名,Integer.toHexString(hashCode())返回该对象的哈希码的十六进制表示。
- return getClass().getName()+"@"+Integer.toHexString(hashCode());
- }
-
- public Singleton3 getInstance(){
- return INSTANCE;
- }
- public void otherMethod(){
- System.out.println("其他方法");
- }
-
- }
测试:
枚举实现的单例可以预防反序列化破坏单例
枚举也可以预防反射破坏单例
枚举类的构造方法不是无参构造,有两个参数。
但是枚举无法预防unsafe破坏单例模式。
- //懒汉式
- public class Singleton4 {
- private static Singleton4 SINGLETON_TEST;
- //构造私有
- private Singleton4() {
- System.out.println("私有构造方法");
- }
- public static Singleton4 getInstance(){
- if (SINGLETON_TEST==null){
- SINGLETON_TEST=new Singleton4();
- }
- return SINGLETON_TEST;
- }
- public static void otherMotherd(){
- System.out.println("其他方法");
- }
- }
测试:
以上单例模式在多线程下存在单例被破坏的可能。
如果在多线程环境下,多个线程同时尝试获取单例实例,可能会导致创建多个实例的情况。可以使用DCL(双重检测来确保只有一个实例被创建,DCL需要配合volatile使用,确保可见性)。懒汉式
- public class Singleton {
- private static final Singleton instance = new Singleton();
-
- private Singleton() {}
-
- public static Singleton getInstance() {
- return instance;
- }
- }
-
- public class Main {
- public static void main(String[] args) {
- // 创建多个线程同时获取单例实例
- Thread thread1 = new Thread(() -> {
- Singleton singleton = Singleton.getInstance();
- System.out.println("Thread 1: " + singleton.hashCode());
- });
-
- Thread thread2 = new Thread(() -> {
- Singleton singleton = Singleton.getInstance();
- System.out.println("Thread 2: " + singleton.hashCode());
- });
-
- thread1.start();
- thread2.start();
- }
- }
预防:DCL+voliatle ,也就是第四种实现方式。
- public class Singleton {
- private static volatile Singleton instance; //保证共享变量的有序性
-
- private Singleton() {}
-
- public static Singleton getInstance() {
- if (instance == null) {
- synchronized (Singleton.class) {
- if (instance == null) {
- instance = new Singleton();
- }
- }
- }
- return instance;
- }
- }
- /静态内部类方式
- public class Singleton6 {
- private static Singleton6 SINGLETON_TEST=null;
- //构造私有
- private Singleton6() {
-
- System.out.println("私有构造方法");
- }
- private static class Holder{
- static Singleton6 INSTANCE=new Singleton6();
- }
- public static Singleton6 getInstance(){
- return Holder.INSTANCE;
- }
- public static void otherMotherd() {
- System.out.println("其他方法");
- }
- }
JDK中单例的体现方式:
单例模式一般在jdk的一些库中见到,自己不要乱用单例模式,很容易用错。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。