Singleton:如何通过Reflection停止创建实例

我知道在Java中我们可以通过newclone()Reflection以及serializing and de-serializing来创建Class的实例。

我创建了一个实现Singleton的简单类。

我需要一直停止创建我的类的实例。

 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." ); } } 

在这个类中,我设法通过newclone()serialization来停止类实例,但是我无法通过Reflection来阻止它。

我创建对象的代码是

 try { Class singletonClass = (Class) 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.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"); } } 

当然,这可能并没有真正阻止它 – 如果有人正在使用reflection来访问私有成员,他们可能能够将字段设置为null。 你必须问问自己,你想要阻止什么,以及它是多么有价值。

(编辑:正如Bozho所说的那样,即使通过反思也可能无法设置最终字段。如果有一些方法可以通过JNI等进行,我也不会感到惊讶……如果你给人们足够的访问权限,他们就能够做几乎任何事……)

 private Singleton() { if (Singleton.singleton != null) { throw new RuntimeException("Can't instantiate singleton twice"); } } 

您应该注意的另一件事是readResolve(..)方法,因为您的类实现了Serialiable 。 在那里你应该返回现有的实例。

但使用单身人士的最简单方法是通过枚举 – 你不必担心这些事情。

作为单身人士的替代方案,您可以查看单稳态模式 。 然后,您的课程的实例化不再是问题,您不必担心您列出的任何方案。

在monostate模式中,类中的所有字段都是static 。 这意味着该类的所有实例共享相同的状态,就像使用单例一样。 而且,这个事实对来电者来说是透明的; 他们不需要知道像getInstance这样的特殊方法,他们只是创建实例并使用它们。

但是,就像单身人士一样,它是一种隐藏的全球状态; 这非常糟糕

我的下面代码将工作..

 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 

需要注意的是,从Java 8开始,根据我的检查 – 只要它有私有构造函数,就不能通过Reflections实例化Singleton。

你会得到这个例外:

 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) 

Perfect Singleton Class,可以避免在序列化,克隆和reflection期间创建实例。

 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(); } } 

为了克服reflection引起的问题,使用了枚举,因为java在内部确保枚举值仅实例化一次。 由于Java Enum可以全局访问,因此它们可以用于单例。 它唯一的缺点是它不灵活,即它不允许延迟初始化。

 public enum Singleton { INSTANCE } public class ReflectionTest { public static void main(String[] args) { Singleton instance1 = Singleton.INSTANCE; Singleton instance2 = Singleton.INSTANCE; System.out.println("instance1 hashcode- " + instance1.hashCode()); System.out.println("instance2 hashcode- " + instance2.hashCode()); } } 

JVM在内部处理枚举构造函数的创建和调用。 由于枚举不会将其构造函数定义提供给程序,因此我们也无法通过Reflection访问它们。

请参阅post了解更多详情。

使用延迟初始化的方法:

  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调用之前使用reflection创建实例,此方法仍然有效

 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