如何处理Singleton以及序列化

时间:2010-10-14 04:53:03

标签: java singleton

考虑我将Singleton类定义如下。

public class MySingleton implements Serializable{
 private static MySingleton myInstance;

 private MySingleton(){

 }
  static{
    myInstance =new MySingleton();
 }
 public static MySingleton getInstance(){
    return MySingleton.myInstance;
 }
}

根据我的上述定义满足Singleton的要求。添加的唯一附加行为是该类实现了可序列化的接口。

如果另一个类X获取单个实例并将其写入文件,稍后将其反序列化以获取另一个实例,我们将有两个实例违反Singleton原则。

我怎样才能避免这种情况,或者在上面的定义中我是错的。

9 个答案:

答案 0 :(得分:35)

执行此操作的最佳方法是使用枚举单例模式:

public enum MySingleton {
  INSTANCE;
}

这可以保证对象的单一性,并为您提供可串行化的方式,使您始终获得相同的实例。

更一般地说,您可以提供readResolve()方法,如下所示:

protected Object readResolve() {
  return myInstance;
}

答案 1 :(得分:27)

@ColinD是对的,但他的回答也说明了为什么单身人士并没有真正对序列化感到愤怒。

以下是序列化枚举值时会发生的情况(请参阅here)。

  

序列化枚举实例的规则与序列化“普通”可序列化对象的规则不同:枚举实例的序列化形式仅包含其枚举常量名称,以及标识其基本枚举类型的信息。反序列化行为也有所不同 - 类信息用于查找适当的枚举类,并使用该类和接收的常量名称调用Enum.valueOf方法,以获取要返回的枚举常量。

因此,附加到枚举值的任何其他状态都不会在序列化和反序列化后继续存在。

您可以通过向单例类添加自定义序列化/反序列化代码来自己做同样的事情。该代码需要根本不记录单例的状态,或者在单例被反序列化时抛弃它。无论哪种方式,你都会将逻辑放入readResolve()方法,如@ColinD的答案所解释的那样。

现在,我认为你想要序列化单身的原因是你想要坚持他们的状态。不幸的是,这提出了一个概念问题。假设您的应用程序已在事件的正常过程中实例化单例,然后它反序列化一些包含单例的先前实例的副本的对象图。它能做什么?

  • 如果它通常对单例进行反序列化,则会违反“singleton-ness”。
  • 如果没有,则应用程序无法访问单例的先前状态。

答案 2 :(得分:5)

使用枚举的解决方案不适用于由Spring,EJB,Guice或任何其他DI框架管理的Singletons。它仅适用于枚举,只是因为枚举算法特别处理了枚举。

首先,单例不需要序列化,因为如果你反序列化它,然后反序列化singleton!= YourSingleton.getInstance(),那就意味着你有两个你的单例实例,意味着YourSingleton根本不是单例,这可能会导致不可预测的错误。

但有时您需要序列化包含对singleton的引用的非单例。解决方案很简单:

class NonSingleton implements Serializable {
    private transient YourSingleton singleton = YourSingleton.getInstance();
    ...
}

使用Spring:

@Configurable
class NonSingleton implements Serializable {
    @Autowired
    private transient YourSingleton singleton;
    ...
}

答案 3 :(得分:4)

下面是我的Singleton类,它实现了Serializable接口。标记它还包含readResolve()方法。

import java.io.Serializable;

public class Singleton implements Serializable {

    private static Singleton singleton = new Singleton( );

    public int i = 1;

    private Singleton() { }

    public static Singleton getInstance( ) {

       return singleton;
    }

    public Object readResolve() {
       return getInstance( );
    }

    public static void main(String[] args) {
        Singleton s1 = getInstance();
        System.out.println(s1.hashCode());

        Singleton s2 = getInstance();
        System.out.println(s2.hashCode());
    }
}

下面是首先序列化然后反序列化上述类的类。这里反序列化发生了两次,但由于readResolve()方法,这两次只会创建一个实例。

public class SingletonSerializableDemo {

    static Singleton sing = Singleton.getInstance();
    static Singleton s1  = null;
    static Singleton s2 = null;
    public static void main(String[] args) {
        try {
             FileOutputStream fileOut =
             new FileOutputStream("E:/singleton.ser");
             ObjectOutputStream out = new ObjectOutputStream(fileOut);
             out.writeObject(sing);
             out.close();
             fileOut.close();
             System.out.println("Serialized data is saved");

             FileInputStream fileIn1 = new FileInputStream("E:/singleton.ser");
             FileInputStream fileIn2 = new FileInputStream("E:/singleton.ser");
             ObjectInputStream in1 = new ObjectInputStream(fileIn1);
             ObjectInputStream in2 = new ObjectInputStream(fileIn2);
             s1 = (Singleton) in1.readObject();
             s2 = (Singleton) in2.readObject();
             System.out.println(s1.hashCode() + " "+ s1.i);
             s1.i = 10;
             System.out.println(s2.hashCode() + " "+ s2.i);
             in1.close();
             in2.close();
             fileIn1.close();
             fileIn2.close();
          }catch(Exception i) {
             i.printStackTrace();
          }
    }
}

输出将是:

保存序列化数据
21061094 1
21061094 10

结论:也可以通过在Singleton类中保留readResolve()方法来序列化单例类。

答案 4 :(得分:1)

这可能是一个熟悉的解决方案,但仅供参考。

select pe.ID_Person, pe.Name, count(*) as ProfessionCount
from Person pe
  inner join Profession pr
    on pe.ID_Person = pr.ID_Person
group by pe.ID_Person, pe.Name
having count(*)>1

测试代码

select 
    multi.ID_Person
  , multi.Name
  , multi.ProfessionCount
  , prof.ID_Prof
  , prof.Prof_Name
from (
    select pe.ID_Person, pe.Name, count(*) as ProfessionCount
    from Person pe
      inner join Profession pr
        on pe.ID_Person = pr.ID_Person
    group by pe.ID_Person, pe.Name
    having count(*)>1
  ) multi
  inner join Profession prof
    on multi.ID_Person = prof.ID_Person

输出

public class ConnectionFactory implements Serializable {

    //Static variable for holding singleton reference object
    private static ConnectionFactory INSTANCE;

    /**
     * Private constructor
     */
    private ConnectionFactory() {

    }

    /**
     * Static method for fetching the instance
     *
     * @return
     */
    public static ConnectionFactory getIntance() {
        //Check whether instance is null or not
        if (INSTANCE == null) {
            //Locking the class object
            synchronized (ConnectionFactory.class) {
                //Doing double check for the instance
                //This is required in case first time two threads simultaneously invoke
                //getInstance().So when another thread get the lock,it should not create the
                //object again as its already created by the previous thread.
                if (INSTANCE == null) {
                    INSTANCE = new ConnectionFactory();
                }
            }
        }
        return INSTANCE;
    }

    /**
     * Special hook provided by serialization where developer can control what object needs to sent.
     * However this method is invoked on the new object instance created by de serialization process.
     *
     * @return
     * @throws ObjectStreamException
     */
    private Object readResolve() throws ObjectStreamException {
        return INSTANCE;
    }

}

答案 5 :(得分:1)

假设我们有以下单身类:

public class ConnectionFactory implements Serializable {
    private static ConnectionFactory INSTANCE;

    private ConnectionFactory() {  }

    public static ConnectionFactory getInstance() {
        if (INSTANCE == null) {
            synchronized(ConnectionFactory.class) {
                if(INSTANCE == null)
                    INSTANCE = new ConnectionFactory();
            }
        }
        return INSTANCE;
    }
}

现在我们有像下面这样的主类来序列化和反序列化对象:

 public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
     ConnectionFactory INSTANCE=ConnectionFactory.getInstance();
     ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("connFactory.ser"));  
     oos.writeObject(INSTANCE);  
     oos.close();

     // Here I am recreating the instance by reading the serialized object data store
     ObjectInputStream ois = new ObjectInputStream(new FileInputStream("connFactory.ser"));  
     ConnectionFactory factory1 = (ConnectionFactory) ois.readObject();  
     ois.close();  

     // I am recreating the instance AGAIN by reading the serialized object data store
     ObjectInputStream ois2 = new ObjectInputStream(new FileInputStream("connFactory.ser"));  
     ConnectionFactory factory2 = (ConnectionFactory) ois2.readObject();  
     ois2.close();

     // Let's see how we have broken the singleton behavior
     System.out.println("Instance reference check->" +factory1.getInstance());
     System.out.println("Instance reference check->" +factory2.getInstance());
     System.out.println("=========================================================");
     System.out.println("Object reference check->" + factory1);
     System.out.println("Object reference check->" + factory2);
}

因此,如果我们执行上面的代码,我们将得到以下行为: “它为INSTANCE创建了两个对象和一个静态引用。这意味着如果我们多次读取单个对象的序列化格式,我们将创建多个对象。这不是单个对象应该做的事情。所以我们可以避免吗?是的,我们可以。“

为了避免单个类的多个实例,我们将使用序列化提供的以下方法:

private Object readResolve() throws ObjectStreamException {
    return INSTANCE;
}

这将阻止创建单个类的多个实例。

答案 6 :(得分:0)

我认为单身人士可以被序列化,以下是如何做的代码:


import java.io.Serializable;

public class MySingleton implements Serializable {

    private MySingleton(String name) {
        this.name = name;
    }

    private static MySingleton mySingleton;

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public static MySingleton getInstance(String name) {
        if(mySingleton == null) {
            System.out.println("in if...");
            mySingleton = new MySingleton(name);
        }

        return mySingleton;
    }
}

这里是"主要"获取上面的Singleton类的实例的方法,序列化和反序列化它:



 public static void main (String[] args) {

        MySingleton m = MySingleton.getInstance("Akshay");

        try {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D://temp.ser"));
            oos.writeObject(m);

            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D://temp.ser"));
            MySingleton m2 = (MySingleton) ois.readObject();
            System.out.println(m2.getName());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

输出为: -

  

in if ...

     

阿克沙伊

感谢。

答案 7 :(得分:0)

这是打破Singleton类的答案,以及如何通过使用readResolve()方法来防止我们的类创建其他对象;

导入java.io.Serializable;

公共类Singleton实现可序列化{

private static final long serialVersionUID = 1L;

private Singleton() {
}

private static class SingletonHelper {

    private static final Singleton INSTANCE = new Singleton();

}

public static Singleton getInstance() {

    return SingletonHelper.INSTANCE;
}

private Object readResolve() {
    Singleton instance = getInstance();
    return instance;
}

}

公共类BreakSIngletonUsingSerialization {

public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {

    Singleton demo1 =Singleton.getInstance();
    ObjectOutput out = new ObjectOutputStream(new FileOutputStream("C:/Eclipse/serial.ser"));
    out.writeObject(demo1);
    Singleton demo2 =null;
    ObjectInput in = new ObjectInputStream(new FileInputStream("C:/Eclipse/serial.ser"));

    demo2 = (Singleton)in.readObject();

    System.out.println("Hascode demo1 : " +demo1);
    System.out.println("Hascode demo2 : " +demo2);
}

}

答案 8 :(得分:-1)

Singleton类就像一个管理器或控制器,一般来说我们不想保存任何控制器的状态而不是实体。通常我们需要保存任何实体的对象状态而不是控制器 Singleton是单个类加载器的单例,不是多类加载器。如果在类加载器中加载了一个类,那么另一个类加载器将不会知道它,所以它的行为就像这样。