搞懂防止破坏单例类模式

 

在优锐课在线的java架构学习探讨中,让我们探讨破坏单例属性的三种主要方法以及如何防止它

 

我们习惯于在需要时在我们的应用程序中使用单例设计模式。 众所周知,在单例设计模式中,我们只能创建一个实例并在整个应用程序中访问它。 但是在某些情况下,它将破坏单例行为。

在三个主要概念中,我们可以打破Java中Singleton类的singleton属性。 在这篇文章中,我们将讨论如何破坏它以及如何防止它。

这是示例Singleton类和SingletonTest类。

单例.

 1 Singleton.Java
 2 
 3 package demo1;
 4 
 5 public final class Singleton {
 6 
 7     private static volatile Singleton instance = null;
 8 
 9     private Singleton() {
10 
11     }
12 
13     public static Singleton getInstance() {
14 
15         if (instance == null) {
16 
17             synchronized (Singleton.class) {
18 
19                 if (instance == null) {
20 
21                     instance = new Singleton();
22 
23                 }
24 
25             }
26 
27         }
28 
29         return instance;
30 
31     }
32 
33 }
34 
35  
36 
37 SingletonTest.java
38 
39   package demo1;
40 
41 public class SingletonTest {
42 
43     public static void main(String[] args) {
44 
45         Singleton object1 = Singleton.getInstance();
46 
47         Singleton object2 = Singleton.getInstance();
48 
49         System.out.println("Hashcode of Object 1 - " + object1.hashCode());
50 
51         System.out.println("Hashcode of Object 2 - " + object2.hashCode());
52 
53     }
54 
55 }
56 
57  

 

这是输出; 可以看到它具有与objectOne和objectTwo相同的hashcode :

1 Hashcode of Object 1 - 1836019240
2 
3 Hashcode of Object 2 - 1836019240

 

 

现在,我们将打破这种模式。 首先,我们将使用Java反射。

反射

Java Reflection是一种API,用于在运行时检查或修改方法,类和接口的行为。 使用Reflection API,我们可以在Singleton类中创建多个对象。 考虑以下示例:

 1 ReflectionSingleton.java
 2 
 3   package demo1;
 4 
 5 import java.lang.reflect.Constructor;
 6 
 7 public class ReflectionSingleton {
 8 
 9     public static void main(String[] args)  {
10 
11         Singleton objOne = Singleton.getInstance();
12 
13         Singleton objTwo = null;
14 
15         try {
16 
17             Constructor constructor = Singleton.class.getDeclaredConstructor();
18 
19             constructor.setAccessible(true);
20 
21             objTwo = (Singleton) constructor.newInstance();
22 
23         } catch (Exception ex) {
24 
25             System.out.println(ex);
26 
27         }
28 
29         System.out.println("Hashcode of Object 1 - "+objOne.hashCode());
30 
31         System.out.println("Hashcode of Object 2 - "+objTwo.hashCode());
32 
33     }
34 
35 }
36 
37  

 

这个例子展示了反射如何用Java反射来打破单例模式。 将获得两个哈希码,如下所示。 它在单例模式上有所突破。

防止单例模式反射

有很多方法可以防止反射API中的Singleton模式,但是最好的解决方案之一是,如果实例已经存在,则在构造函数中引发运行时异常。 在这种情况下,我们无法创建第二个实例。

反序列化

在序列化中,我们可以将字节流的对象保存到文件中或通过网络发送。 假设先序列化Singleton类,然后再次对该对象反序列化,它将创建一个新实例,因此反序列化将破坏Singleton模式。

以下代码用于说明单反模式如何随反序列化而中断。

Singleton类实现Serializable接口。

 1 DeserializationSingleton.Java
 2 
 3 package demo1;
 4 
 5 import java.io.*;
 6 
 7 public class DeserializationSingleton {
 8 
 9     public static void main(String[] args) throws Exception {
10 
11         Singleton instanceOne = Singleton.getInstance();
12 
13         ObjectOutput out = new ObjectOutputStream(new FileOutputStream("file.text"));
14 
15         out.writeObject(instanceOne);
16 
17         out.close();
18 
19         ObjectInput in = new ObjectInputStream(new FileInputStream("file.text"));
20 
21         Singleton instanceTwo = (Singleton) in.readObject();
22 
23         in.close();
24 
25         System.out.println("hashCode of instance 1 is - " + instanceOne.hashCode());
26 
27         System.out.println("hashCode of instance 2 is - " + instanceTwo.hashCode());
28 
29     }
30 
31 }
32 
33  

 

输出如下,可以看到两个 hashcodes.

1 hashCode of instance 1 is - 2125039532
2 
3 hashCode of instance 2 is - 381259350

 

 

防止单例模式反序列化

为了克服这个问题,我们需要覆盖Singleton类中的readResolve()方法并返回相同的Singleton实例。 使用以下方法更新Singleton.java。

1    protected Object readResolve() {
2 
3            return instance;
4 
5      }
6 
7  

 

现在,运行上面的DeserializationDemo类并查看输出。

1 hashCode of instance 1 is - 2125039532
2 
3 hashCode of instance 2 is - 2125039532

 

 

克隆

使用“克隆”方法,我们可以创建原始对象的副本; 如果我们在单例模式中应用克隆,这是同一回事。 它将创建两个实例:一个实例和另一个实例。 在这种情况下,我们将打破Singleton原理,如下面的代码所示。

实施「可克隆」介面,并在上述Singleton类别中覆写clone方法

 1 Singleton.java
 2 
 3     @Override
 4 
 5     protected Object clone() throws CloneNotSupportedException  {
 6 
 7         return super.clone();
 8 
 9     }
10 
11  

 

然后,测试克隆以打破单例。

 1 CloningSingleton.java
 2 
 3 public class CloningSingleton {
 4 
 5     public static void main(String[] args) throws CloneNotSupportedException, Exception {
 6 
 7         Singleton instanceOne = Singleton.getInstance();
 8 
 9         Singleton instanceTwo = (Singleton) instanceOne.clone();
10 
11         System.out.println("hashCode of instance 1 - " + instanceOne.hashCode());
12 
13         System.out.println("hashCode of instance 2 - " + instanceTwo.hashCode());
14 
15     }
16 
17 }
18 
19  

 

这是输出:

1 hashCode of instance 1 - 1836019240
2 
3 hashCode of instance 2 - 325040804

 

 

如果我们看到上面的输出,则两个实例具有不同的hashcodes。 这意味着这些实例不相同。

防止单例模式克隆

在上面的代码中,它打破了Singleton原理,即。 e创建了两个实例。 为了克服上述问题,我们需要实现/覆盖clone()方法并从克隆方法中抛出异常CloneNotSupportedException。 如果有人尝试创建Singleton的克隆对象,它将抛出异常,如以下代码所示。

1     @Override
2 
3     protected Object clone() throws CloneNotSupportedException  {
4 
5         throw new CloneNotSupportedException();
6 
7     }
8 
9  

 

现在,我们可以运行loningSingleton类; 在创建单个对象的克隆对象时,它将抛出CloneNotSupportedException。

希望发现这篇文章有用!

 

 

> 喜欢这篇文章的可以点个赞,欢迎大家留言评论,记得关注我,每天持续更新技术干货、职场趣事、海量面试资料等等
 > 如果你对java技术很感兴趣也可以交流学习,共同学习进步。 
> 不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代

文章写道这里,欢迎完善交流。最后奉上近期整理出来的一套完整的java架构思维导图,分享给大家对照知识点参考学习。有更多JVM、Mysql、Tomcat、Spring Boot、Spring Cloud、Zookeeper、Kafka、RabbitMQ、RockerMQ、Redis、ELK、Git等Java干货

  搞懂防止破坏单例类模式_第1张图片

 

 

你可能感兴趣的:(搞懂防止破坏单例类模式)