如何防止单例模式反射、序列化和克隆?

先决条件: 单子模式

null

在本文中,我们将了解哪些概念可以破坏类的单例属性,以及如何避免它们。主要有三个概念可以打破一个类的单例属性。让我们逐一讨论。

  1. 反思: 反射 可以导致销毁singleton类的singleton属性,如下例所示:

    JAVA

    // Java code to explain effect of Reflection
    // on Singleton property
    import java.lang.reflect.Constructor;
    // Singleton class
    class Singleton
    {
    // public instance initialized when loading the class
    public static Singleton instance = new Singleton();
    private Singleton()
    {
    // private constructor
    }
    }
    public class GFG
    {
    public static void main(String[] args)
    {
    Singleton instance1 = Singleton.instance;
    Singleton instance2 = null ;
    try
    {
    Constructor[] constructors =
    Singleton. class .getDeclaredConstructors();
    for (Constructor constructor : constructors)
    {
    // Below code will destroy the singleton pattern
    constructor.setAccessible( true );
    instance2 = (Singleton) constructor.newInstance();
    break ;
    }
    }
    catch (Exception e)
    {
    e.printStackTrace();
    }
    System.out.println( "instance1.hashCode():- "
    + instance1.hashCode());
    System.out.println( "instance2.hashCode():- "
    + instance2.hashCode());
    }
    }

    
    

    Output:-
    instance1.hashCode():- 366712642
    instance2.hashCode():- 1829164700
    

    运行这个类之后,您将看到hashcode是不同的,这意味着创建了同一类的两个对象,并且销毁了单例模式。

    克服反思问题: 为了克服反思引发的问题, 枚举 之所以使用,是因为java在内部确保枚举值只实例化一次。因为java枚举是全局可访问的,所以它们可以用于单例。它唯一的缺点是不灵活,即不允许延迟初始化。

    JAVA

    //Java program for Enum type singleton
    public enum Singleton
    {
    INSTANCE;
    }

    
    

    因为枚举没有任何构造函数,所以反射不可能使用它。枚举有它们的默认构造函数,我们不能自己调用它们。 JVM在内部处理枚举构造函数的创建和调用。 由于枚举没有给程序提供构造函数定义,我们也不可能通过反射来访问它们。因此,在枚举的情况下,反射不能破坏singleton属性。

  2. 序列化:- 系列化 也可能导致单例类的单例属性的破坏。序列化用于转换字节流的对象并保存在文件中或通过网络发送。假设您序列化了一个单例类的对象。然后,如果反序列化该对象,它将创建一个新实例,从而打破单例模式。

    JAVA

    // Java code to explain effect of
    // Serialization on singleton classes
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.ObjectInput;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutput;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    class Singleton implements Serializable
    {
    // public instance initialized when loading the class
    public static Singleton instance = new Singleton();
    private Singleton()
    {
    // private constructor
    }
    }
    public class GFG
    {
    public static void main(String[] args)
    {
    try
    {
    Singleton instance1 = Singleton.instance;
    ObjectOutput out
    = new ObjectOutputStream( new FileOutputStream( "file.text" ));
    out.writeObject(instance1);
    out.close();
    // deserialize from file to object
    ObjectInput in
    = new ObjectInputStream( new FileInputStream( "file.text" ));
    Singleton instance2 = (Singleton) in.readObject();
    in.close();
    System.out.println( "instance1 hashCode:- "
    + instance1.hashCode());
    System.out.println( "instance2 hashCode:- "
    + instance2.hashCode());
    }
    catch (Exception e)
    {
    e.printStackTrace();
    }
    }
    }

    
    

    Output:- 
    instance1 hashCode:- 1550089733
    instance2 hashCode:- 865113938
    

    正如您所见,这两个实例的hashCode是不同的,因此一个singleton类有两个对象。因此,这个班级不再是单身。

    克服序列化问题:- 为了克服这个问题,我们必须实现readResolve()方法。

    JAVA

    // Java code to remove the effect of
    // Serialization on singleton classes
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.ObjectInput;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutput;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    class Singleton implements Serializable
    {
    // public instance initialized when loading the class
    public static Singleton instance = new Singleton();
    private Singleton()
    {
    // private constructor
    }
    // implement readResolve method
    protected Object readResolve()
    {
    return instance;
    }
    }
    public class GFG
    {
    public static void main(String[] args)
    {
    try
    {
    Singleton instance1 = Singleton.instance;
    ObjectOutput out
    = new ObjectOutputStream( new FileOutputStream( "file.text" ));
    out.writeObject(instance1);
    out.close();
    // deserialize from file to object
    ObjectInput in
    = new ObjectInputStream( new FileInputStream( "file.text" ));
    Singleton instance2 = (Singleton) in.readObject();
    in.close();
    System.out.println( "instance1 hashCode:- "
    + instance1.hashCode());
    System.out.println( "instance2 hashCode:- "
    + instance2.hashCode());
    }
    catch (Exception e)
    {
    e.printStackTrace();
    }
    }
    }

    
    

    Output:- 
    instance1 hashCode:- 1550089733
    instance2 hashCode:- 1550089733
    

    上面两个hashcode是相同的,因此没有创建其他实例。

  3. 克隆: 克隆 是创建重复对象的概念。使用克隆,我们可以创建对象的副本。假设我们创建一个单例对象的克隆,然后它将创建一个副本,即一个单例类有两个实例,因此该类不再是单例。

    JAVA

    // Java code to explain cloning
    // issue with singleton
    class SuperClass implements Cloneable
    {
    int i = 10 ;
    @Override
    protected Object clone() throws CloneNotSupportedException
    {
    return super .clone();
    }
    }
    // Singleton class
    class Singleton extends SuperClass
    {
    // public instance initialized when loading the class
    public static Singleton instance = new Singleton();
    private Singleton()
    {
    // private constructor
    }
    }
    public class GFG
    {
    public static void main(String[] args) throws CloneNotSupportedException
    {
    Singleton instance1 = Singleton.instance;
    Singleton instance2 = (Singleton) instance1.clone();
    System.out.println( "instance1 hashCode:- "
    + instance1.hashCode());
    System.out.println( "instance2 hashCode:- "
    + instance2.hashCode());
    }
    }

    
    

    Output :- 
    instance1 hashCode:- 366712642
    instance2 hashCode:- 1829164700
    

    两个不同的哈希代码意味着singleton类有两个不同的对象。

    克服克隆问题:- 要解决此问题,请重写clone()方法,并从clone方法中引发一个异常,即CloneNotSupportedException。现在,每当用户试图创建singleton对象的克隆时,它都会抛出异常,因此我们的类仍然是singleton。

    JAVA

    // Java code to explain overcome
    // cloning issue with singleton
    class SuperClass implements Cloneable
    {
    int i = 10 ;
    @Override
    protected Object clone() throws CloneNotSupportedException
    {
    return super .clone();
    }
    }
    // Singleton class
    class Singleton extends SuperClass
    {
    // public instance initialized when loading the class
    public static Singleton instance = new Singleton();
    private Singleton()
    {
    // private constructor
    }
    @Override
    protected Object clone() throws CloneNotSupportedException
    {
    throw new CloneNotSupportedException();
    }
    }
    public class GFG
    {
    public static void main(String[] args) throws CloneNotSupportedException
    {
    Singleton instance1 = Singleton.instance;
    Singleton instance2 = (Singleton) instance1.clone();
    System.out.println( "instance1 hashCode:- "
    + instance1.hashCode());
    System.out.println( "instance2 hashCode:- "
    + instance2.hashCode());
    }
    }

    
    

    Output:-
    Exception in thread "main" java.lang.CloneNotSupportedException
        at GFG.Singleton.clone(GFG.java:29)
        at GFG.GFG.main(GFG.java:38)
    

    现在我们已经停止用户创建singleton类的克隆。如果你没有;如果不想引发异常,也可以从clone方法返回相同的实例。

    JAVA

    // Java code to explain overcome
    // cloning issue with singleton
    class SuperClass implements Cloneable
    {
    int i = 10 ;
    @Override
    protected Object clone() throws CloneNotSupportedException
    {
    return super .clone();
    }
    }
    // Singleton class
    class Singleton extends SuperClass
    {
    // public instance initialized when loading the class
    public static Singleton instance = new Singleton();
    private Singleton()
    {
    // private constructor
    }
    @Override
    protected Object clone() throws CloneNotSupportedException
    {
    return instance;
    }
    }
    public class GFG
    {
    public static void main(String[] args) throws CloneNotSupportedException
    {
    Singleton instance1 = Singleton.instance;
    Singleton instance2 = (Singleton) instance1.clone();
    System.out.println( "instance1 hashCode:- "
    + instance1.hashCode());
    System.out.println( "instance2 hashCode:- "
    + instance2.hashCode());
    }
    }

    
    

    Output:-
    instance1 hashCode:- 366712642
    instance2 hashCode:- 366712642
    

    现在,由于两个实例的哈希代码相同,这意味着它们代表一个实例。

本文由 维沙尔·加格 .如果你喜欢GeekSforgek,并想贡献自己的力量,你也可以使用 写极客。组织 或者把你的文章寄去评论-team@geeksforgeeks.org.看到你的文章出现在Geeksforgeks主页上,并帮助其他极客。

如果您发现任何不正确的地方,或者您想分享有关上述主题的更多信息,请写下评论。

© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享