先决条件: 单子模式
在本文中,我们将了解哪些概念可以破坏类的单例属性,以及如何避免它们。主要有三个概念可以打破一个类的单例属性。让我们逐一讨论。
- 反思: 反射 可以导致销毁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属性。
- 序列化:- 系列化 也可能导致单例类的单例属性的破坏。序列化用于转换字节流的对象并保存在文件中或通过网络发送。假设您序列化了一个单例类的对象。然后,如果反序列化该对象,它将创建一个新实例,从而打破单例模式。
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是相同的,因此没有创建其他实例。
- 克隆: 克隆 是创建重复对象的概念。使用克隆,我们可以创建对象的副本。假设我们创建一个单例对象的克隆,然后它将创建一个副本,即一个单例类有两个实例,因此该类不再是单例。
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主页上,并帮助其他极客。
如果您发现任何不正确的地方,或者您想分享有关上述主题的更多信息,请写下评论。