我知道在Java中,我们可以通过new
,clone()
,Reflection
和serializing and de-serializing
来创建类的实例。
我创建了一个实现单例的简单类。
我需要一直停止,可以创建我的类的实例。
public class Singleton implements Serializable{
private static final long serialVersionUID = 3119105548371608200L;
private static final Singleton singleton = new Singleton();
private Singleton() { }
public static Singleton getInstance(){
return singleton;
}
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("Cloning of this class is not allowed");
}
protected Object readResolve() {
return singleton;
}
//-----> This is my implementation to stop it but Its not working. :(
public Object newInstance() throws InstantiationException {
throw new InstantiationError( "Creating of this object is not allowed." );
}
}
在这个类中,我已经设法通过new
、clone()
和serialization
来阻止类实例,但我无法通过反射来阻止它。
我用于创建对象的代码是
try {
Class<Singleton> singletonClass = (Class<Singleton>) Class.forName("test.singleton.Singleton");
Singleton singletonReflection = singletonClass.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
通过在私有构造函数中添加以下内容检查
private Singleton() {
if( singleton != null ) {
throw new InstantiationError( "Creating of this object is not allowed." );
}
}
像这样定义单例:
public enum Singleton {
INSTANCE
}
如何签入构造函数:
private Singleton() {
if (singleton != null) {
throw new IllegalStateException("Singleton already constructed");
}
}
当然,这可能不会真正阻止它 - 如果有人弄乱反射以访问私有成员,他们可能会将字段设置为空。你必须问问自己,你想阻止什么,以及它有多值得。
(编辑:正如Bozho所提到的,即使通过反射,最终字段也可能无法设置。不过,如果有某种方法可以通过 JNI 等做到这一点,我不会感到惊讶......如果你给人们足够的访问权限,他们几乎可以做任何事情......
private Singleton() {
if (Singleton.singleton != null) {
throw new RuntimeException("Can't instantiate singleton twice");
}
}
您应该注意的另一件事是 readResolve(..)
方法,因为您的类实现了 Serialiable
。在那里,您应该返回现有实例。
但是使用单例的最简单方法是通过枚举 - 您不必担心这些事情。
作为单例的替代方法,您可以查看单态模式。然后,类的实例化不再是问题,您不必担心列出的任何场景。
在单状态模式中,类中的所有字段都static
。这意味着类的所有实例共享相同的状态,就像与单例一样。此外,这一事实对呼叫者是透明的;他们不需要了解像getInstance
这样的特殊方法,他们只需创建实例并使用它们。
但是,就像单例一样,它是一种隐藏的全局状态;这是非常糟糕的。
除了枚举解决方案,所有其他解决方案都可以通过反射来解决以下是有关如何解决Dave G解决方案的两个示例:
1:将变量 Singleton.singleton 设置为 null
Constructor<?>[] constructors = Singleton.class.getDeclaredConstructors();
Constructor theConstructor = constructors[0];
theConstructor.setAccessible(true);
Singleton instance1 = (Singleton) theConstructor.newInstance();
Singleton.getInstance();
Field f1 = Singleton.class.getDeclaredField("singleton");
f1.setAccessible(true);
f1.set(f1, null);
Singleton instance2 = (Singleton) theConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
输出:
- Singleton@17f6480
- Singleton@2d6e8792
2:不调用getInstance
Constructor<?>[] constructors = Singleton.class.getDeclaredConstructors();
Constructor theConstructor = constructors[0];
theConstructor.setAccessible(true);
Singleton instance1 = (Singleton) theConstructor.newInstance();
Singleton instance2 = (Singleton) theConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
输出:
- Singleton@17f6480
- Singleton@2d6e8792
所以如果你不想使用枚举,我可以想到 2 种方法:
第一个选项:使用安全管理器:
它防止使用未经授权的操作(从类外部调用私有方法......
所以你只需要向其他答案提出的单例构造函数添加一行
private Singleton() {
if (singleton != null) {
throw new IllegalStateException("Singleton already constructed");
}
System.setSecurityManager(new SecurityManager());
}
它的作用是防止调用setAccessible(true)
所以当你想称它为:
Constructor<?>[] constructors = Singleton.class.getDeclaredConstructors();
Constructor theConstructor = constructors[0];
theConstructor.setAccessible(true);
此解释将发生: java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "createSecurityManager")
第二个选项:在单例构造函数中,测试调用是否通过反射进行:
我向您推荐另一个 Stackoverflow 线程,以获取获取调用者类或方法的最佳方式。
因此,如果我在单例构造函数中添加它:
String callerClassName = new Exception().getStackTrace()[1].getClassName();
System.out.println(callerClassName);
我这样称呼它:
Constructor<?>[] constructors = Singleton.class.getDeclaredConstructors();
Constructor theConstructor = constructors[0];
theConstructor.setAccessible(true);
Singleton instance1 = (Singleton) theConstructor.newInstance();
输出将为:jdk.internal.reflect.DelegatingConstructorAccessorImpl
但是如果我定期调用它(实例化公共构造函数或调用方法而不进行反射(,则会打印调用方法的类的名称。所以例如我有:
public class MainReflexion {
public static void main(String[] args) {
Singleton.getInstance();
}
}
调用方类名将被MainReflexion
,因此输出将MainReflexion
。
PS :如果建议的解决方案存在解决方法,请告诉我
请注意,从Java 8开始,根据我的检查 - 只要它有一个私有构造函数,你就不能通过反射实例化单例。
你将得到以下异常:
Exception in thread "main" java.lang.IllegalAccessException: Class com.s.Main can not access a member of class com.s.SingletonInstance with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Unknown Source)
at java.lang.Class.newInstance(Unknown Source)
at com.s.Main.main(Main.java:6)
我的事情下面的代码将起作用..
class Test {
static private Test t = null;
static {
t = new Test();
}
private Test(){}
public static Test getT() {
return t;
}
public String helloMethod() {
return "Singleton Design Pattern";
}
}
public class MethodMain {
public static void main(String[] args) {
Test t = Test.getT();
System.out.println(t.helloMethod());
}
}
输出:单例设计模式
我们可以使用静态嵌套类来打破它
请按照以下代码100%正确,我测试过
package com.singleton.breakable;
import java.io.Serializable;
class SingletonImpl implements Cloneable, Serializable {
public static SingletonImpl singleInstance = null;
private SingletonImpl() {
}
@Override
protected Object clone() throws CloneNotSupportedException {
return singleInstance;
};
public Object readResolve() {
return SingletonImpl.getInstance(); //
}
public static SingletonImpl getInstance() {
if (null == singleInstance) {
singleInstance = new SingletonImpl();
}
return singleInstance;
}
}
package com.singleton.breakable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
class FullySingletonClass {
public static void main(String[] args) {
SingletonImpl object1 = SingletonImpl.getInstance();
System.out.println("Object1:" + object1);
try {
FileOutputStream fos = new FileOutputStream("abc.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(object1);
FileInputStream fis = new FileInputStream("abc.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
SingletonImpl object2 = (SingletonImpl) ois.readObject();
System.out.println("Object2" + object2);
} catch (Exception e) {
// TODO: handle exception
}
try {
Constructor[] constructors = SingletonImpl.class.getDeclaredConstructors();
for (Constructor constructor : constructors) {
// Below code will not destroy the singleton pattern
constructor.setAccessible(true);
SingletonImpl Object3 = (SingletonImpl) constructor.newInstance();
System.out.println("Object3: Break through Reflection:" + Object3);
break;
}
} catch (Exception ew) {
}
}
}
**OUTPUT**
Object1:com.singleton.breakable.SingletonImpl@15db9742
Object2com.singleton.breakable.SingletonImpl@15db9742
Object3: Break through Reflection:com.singleton.breakable.SingletonImpl@33909752
完美的单例类,可以避免在序列化、克隆和反射期间创建实例。
import java.io.Serializable;
public class Singleton implements Cloneable, Serializable {
private static final long serialVersionUID = 1L;
private static volatile Singleton instance;
private Singleton() {
if (instance != null) {
throw new InstantiationError("Error creating class");
}
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
return new Singleton();
}
}
}
return null;
}
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
Object readResolve() {
return Singleton.getInstance();
}
}
延迟初始化的方法:
private static Singleton singleton;
public static Singleton getInstance() {
if(singleton==null){
singleton= new Singleton();
}
return singleton;
}
private Singleton() {
if (Singleton.singleton != null) {
throw new InstantiationError("Can't instantiate singleton twice");
}
Singleton.singleton = this;
}
即使您决定在任何 getInstance 调用之前使用反射创建实例,此方法也有效
Here Reflection not work
package com.singleton.nonbreakable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
class FullySingletonClass {
public static void main(String[] args) {
SingletonImpl object1 = SingletonImpl.getInstance();
System.out.println("Object1:" + object1);
try {
FileOutputStream fos = new FileOutputStream("abc.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(object1);
FileInputStream fis = new FileInputStream("abc.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
SingletonImpl object2 = (SingletonImpl) ois.readObject();
System.out.println("Object2" + object2);
} catch (Exception e) {
// TODO: handle exception
}
try {
Constructor[] constructors = SingletonImpl.class.getDeclaredConstructors();
for (Constructor constructor : constructors) {
// Below code will not destroy the singleton pattern
constructor.setAccessible(true);
SingletonImpl Object3 = (SingletonImpl) constructor.newInstance();
System.out.println("Object3:" + Object3);
break;
}
} catch (Exception ew) {
}
}
}
package com.singleton.nonbreakable;
import java.io.Serializable;
class SingletonImpl implements Cloneable, Serializable {
public static SingletonImpl singleInstance = null;
private static class SingletonHolder {
public static SingletonImpl getInstance() {
if (null == singleInstance) {
singleInstance = new SingletonImpl();
}
return singleInstance;
}
}
private SingletonImpl() {
}
@Override
protected Object clone() throws CloneNotSupportedException {
return singleInstance;
};
public Object readResolve() {
System.out.println("Executing readResolve again");
return SingletonImpl.getInstance(); // FIXME
}
public static SingletonImpl getInstance() {
return SingletonHolder.getInstance();
}
}
Output :
Object1:com.singleton.nonbreakable.SingletonImpl@15db9742
Executing readResolve again
Object2com.singleton.nonbreakable.SingletonImpl@15db9742