Java 如何处理单例和序列化
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3930181/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to deal with Singleton along with Serialization
提问by frictionlesspulley
Consider I have a Singleton class defined as follows.
考虑我有一个定义如下的单例类。
public class MySingleton implements Serializable{
private static MySingleton myInstance;
private MySingleton(){
}
static{
myInstance =new MySingleton();
}
public static MySingleton getInstance(){
return MySingleton.myInstance;
}
}
The above definition according to me satisfies the requirements of a Singleton.The only additional behaviour added is that the class implements serializable interface.
根据我的上述定义满足单例的要求。添加的唯一额外行为是该类实现了可序列化接口。
If another class X get the instance of the single and writes it to a file and at a later point deserializes it to obtain another instance we would have two instances which is against the Singleton principle.
如果另一个类 X 获取单例的实例并将其写入文件,然后在稍后反序列化它以获得另一个实例,我们将有两个实例,这违反了单例原则。
How can I avoid this or am I wrong in above definition itself.
我怎样才能避免这种情况,或者我在上面的定义中错了。
采纳答案by ColinD
The best way to do this is to use the enum singleton pattern:
最好的方法是使用枚举单例模式:
public enum MySingleton {
INSTANCE;
}
This guarantees the singleton-ness of the object and provides serializability for you in such a way that you always get the same instance.
这保证了对象的单例性,并以始终获得相同实例的方式为您提供可序列化性。
More generally, you can provide a readResolve()
method like so:
更一般地,您可以提供如下readResolve()
方法:
protected Object readResolve() {
return myInstance;
}
回答by Stephen C
@ColinD is kind of right, but his answer also illustrates why singletons don't really jell with serialization.
@ColinD 是对的,但他的回答也说明了为什么单身人士并不真正喜欢序列化。
Here's what happens when you serialize an enum value (see here).
以下是序列化枚举值时发生的情况(请参阅此处)。
The rules for serializing an enum instance differ from those for serializing an "ordinary" serializable object: the serialized form of an enum instance consists only of its enum constant name, along with information identifying its base enum type. Deserialization behavior differs as well--the class information is used to find the appropriate enum class, and the Enum.valueOf method is called with that class and the received constant name in order to obtain the enum constant to return.
序列化枚举实例的规则与序列化“普通”可序列化对象的规则不同:枚举实例的序列化形式仅由其枚举常量名称以及标识其基本枚举类型的信息组成。反序列化行为也不同——类信息用于查找适当的枚举类,并使用该类和接收到的常量名称调用 Enum.valueOf 方法以获得要返回的枚举常量。
So any additional state that you attach to your enum values does not survive serialization and deserialization.
因此,您附加到枚举值的任何其他状态都不会在序列化和反序列化后继续存在。
You could do the same thing yourself, by adding custom serialization / deserialization code to your singleton classes. That code would need to either not record the singleton's state at all, or throw it away when the singleton is deserialized. Either way, you'd put the logic into a readResolve()
method as explained by @ColinD's answer.
您可以自己做同样的事情,通过向单例类添加自定义序列化/反序列化代码。该代码要么根本不记录单例的状态,要么在单例反序列化时将其丢弃。无论哪种方式,您都可以按照readResolve()
@ColinD 的回答将逻辑放入方法中。
Now, I presume that the reason you want to serialize singletons is that you want to persist their state. Unfortunately, that presents a conceptual problem. Suppose that your application has instantiated the singleton in the normal course of events, and then it deserializes some object graph that includes a copy of a previous instance of the singleton. What can it do?
现在,我假设您想要序列化单例的原因是您想要保持它们的状态。不幸的是,这提出了一个概念问题。假设您的应用程序在事件的正常过程中实例化了单例,然后它反序列化了一些对象图,其中包括单例的前一个实例的副本。它能做什么?
- If it deserializes the singleton normally, it violates "singleton-ness".
- If it doesn't then the application cannot access the singleton's previous state.
- 如果它正常反序列化单例,则违反了“单例性”。
- 如果不是,则应用程序无法访问单例的先前状态。
回答by iirekm
The solution with enum won't work with Singletons managed by Spring, EJB, Guice or any other DI framework. It works only with enums, only because enum is treated specially by the serialization algorithm.
带有枚举的解决方案不适用于 Spring、EJB、Guice 或任何其他 DI 框架管理的单例。它只适用于枚举,只是因为序列化算法对枚举进行了特殊处理。
Firstly, singletons don't need serialization, because if you deserialized it, and then deserialized singleton != YourSingleton.getInstance(), it would mean that you have two instances of your singleton, which means that YourSingleton isn't singleton at all, which may lead to unpredictable bugs.
首先,单例不需要序列化,因为如果你反序列化它,然后反序列化单例!= YourSingleton.getInstance(),那就意味着你有两个单例实例,这意味着你的单例根本不是单例,这可能会导致不可预测的错误。
However sometimes you need to serialize non-singleton which contains a reference to singleton. The solution is easy:
但是有时您需要序列化包含对单例的引用的非单例。解决方法很简单:
class NonSingleton implements Serializable {
private transient YourSingleton singleton = YourSingleton.getInstance();
...
}
With Spring:
与春天:
@Configurable
class NonSingleton implements Serializable {
@Autowired
private transient YourSingleton singleton;
...
}
回答by Animesh
Singleton classes is just like a manager or controller and in general we do not want to save the state of any controller instead of the entity. Generally we need to save the object state of any entity not the controller.
Singleton is singleton for a single class loader not for multiple class loader. If a class is loaded in a classes loader then the other class loader will not know about it so it behaves like this.
单例类就像一个管理器或控制器,通常我们不想保存任何控制器而不是实体的状态。通常我们需要保存任何实体而不是控制器的对象状态。
Singleton 是单个类加载器的单例,而不是多个类加载器。如果在类加载器中加载了一个类,那么另一个类加载器将不知道它,因此它的行为是这样的。
回答by Akshay Lokur
I think Singletons can be serialized and here is the code on how to do it:
我认为单身人士可以序列化,这里是如何做到这一点的代码:
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;
}
}
and here is "main" method which gets the instance of the Singleton class above, serializes and de-serializes it:
这是“main”方法,它获取上面的 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();
}
}
and output is:-
和输出是:-
in if...
Akshay
在如果...
阿克谢
Thanks.
谢谢。
回答by Rishi garg
Here below is my Singleton
class that implements Serializable
interface. Mark that it contains readResolve()
method also.
下面是我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());
}
}
Below is the class that will first serialize and then deserialize the above class. Here deserialization takes place two times, but both time only one instance will be created because of readResolve() method.
下面是首先序列化然后反序列化上述类的类。这里反序列化发生了两次,但由于 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();
}
}
}
And the output will be:
输出将是:
Serialized data is saved
21061094 1
21061094 10
序列化数据保存
21061094 1
21061094 10
Conclusion:Singleton class can also be serialized by keeping readResolve()
method in the Singleton class.
结论:Singleton 类也可以通过readResolve()
在 Singleton 类中保留方法来序列化。
回答by Rajesh D
This may be a familiar solution but just in case for reference.
这可能是一个熟悉的解决方案,但仅供参考。
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;
}
}
Testing the code
测试代码
public class SerializationTest {
public static void main(String[] args) {
ConnectionFactory INSTANCE = ConnectionFactory.getIntance();
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("connectFactory.ser"));
oos.writeObject(INSTANCE);
oos.close();
ObjectInputStream osi = new ObjectInputStream(new FileInputStream("connectFactory.ser"));
ConnectionFactory factory1 = (ConnectionFactory) osi.readObject();
osi.close();
ObjectInputStream osi2 = new ObjectInputStream(new FileInputStream("connectFactory.ser"));
ConnectionFactory factory2 = (ConnectionFactory) osi2.readObject();
osi2.close();
System.out.println("Instance reference check->" + factory1.getIntance());
System.out.println("Instance reference check->" + factory2.getIntance());
System.out.println("===================================================");
System.out.println("Object reference check->" + factory1);
System.out.println("Object reference check->" + factory2);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Output
输出
Instance reference check->com.javabrains.ConnectionFactory@6f94fa3e
Instance reference check->com.javabrains.ConnectionFactory@6f94fa3e
===================================================
Object reference check->com.javabrains.ConnectionFactory@6f94fa3e
Object reference check->com.javabrains.ConnectionFactory@6f94fa3e
回答by Aman Goel
Let say we have the following singleton class:
假设我们有以下单例类:
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;
}
}
Now we have the main class like below for serialising and deserializing objects:
现在我们有如下的主类,用于序列化和反序列化对象:
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);
}
So if we execute above code we will get following behaviour: "it has created two objects and one static reference for INSTANCE. That means if we read the serialized format of a singleton object multiple times, we will create multiple objects. This is not what a singleton object is supposed to do. So can we avoid i?, Yes, we can."
因此,如果我们执行上面的代码,我们将得到以下行为: “它为 INSTANCE 创建了两个对象和一个静态引用。这意味着如果我们多次读取单个对象的序列化格式,我们将创建多个对象。这不是什么应该是单例对象。所以我们可以避免 i 吗?是的,我们可以。”
To avoid multiple instances of singleton class we will use following method provided by serialization:
为了避免单例类的多个实例,我们将使用序列化提供的以下方法:
private Object readResolve() throws ObjectStreamException {
return INSTANCE;
}
This will prevent the creation of multiple instances of a singleton class.
这将阻止创建单个类的多个实例。
回答by Abhinav Katyayen
Here is the Answer for Breaking the Singleton class and and how to prevent our class from creating different object by using readResolve() methood;
这是打破单例类的答案,以及如何通过使用 readResolve() 方法来防止我们的类创建不同的对象;
import java.io.Serializable;
导入 java.io.Serializable;
public class Singleton implements Serializable {
公共类单例实现可序列化{
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;
}
}
}
public class BreakSIngletonUsingSerialization {
公共类 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);
}
}
}