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

时间:2011-08-09 09:58:18

标签: java reflection singleton

我知道在Java中我们可以通过newclone()Reflectionserializing and de-serializing创建一个类的实例。

我创建了一个实现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停止了课程实例,但我无法通过反思来阻止它。

我创建对象的代码是

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

13 个答案:

答案 0 :(得分:52)

尝试创建公共构造函数

private Singleton() {
    if( Singleton.singleton != null ) {
        throw new InstantiationError( "Creating of this object is not allowed." );
    }
}

答案 1 :(得分:16)

像这样定义单身:

public enum Singleton {
    INSTANCE
}

答案 2 :(得分:13)

如何检查构造函数:

private Singleton() {
    if (singleton != null) {
        throw new IllegalStateException("Singleton already constructed");
    }
}

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

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

答案 3 :(得分:10)

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

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

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

答案 4 :(得分:1)

作为单身人士的替代方案,您可以查看monostate pattern。然后,您的类的实例化不再是问题,您不必担心列出的任何场景。

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

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

答案 5 :(得分:1)

I Thing下面的代码可以工作..

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

输出:单例设计模式

答案 6 :(得分:1)

我们可以使用静态嵌套类

来打破它

请按照以下代码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

答案 7 :(得分:1)

除枚举解决方案外,其他所有解决方案都可以通过Reflexion解决 这是有关如何解决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

因此,如果您不想使用枚举,我可以考虑两种方式:

第一个选项:使用securityManager:

它防止使用未经授权的操作(从类外部调用私有方法....)

所以您只需要向其他答案提出的单例构造函数添加一行

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")

第二个选项:在单例构造函数中,测试是否通过Reflexion进行了调用

我将您引荐给其他Stackoverflow thread,以获取调用方类或方法的最佳方法。

因此,如果我将其添加到Singleton构造函数中:

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

但是如果我定期调用它(实例化公共构造函数或在没有Reflexion的情况下调用方法),则会打印出调用方法的类的名称。例如,我有:

public class MainReflexion {
    public static void main(String[] args) {
        Singleton.getInstance();
    }
}

callerClassName将为MainReflexion,因此输出将为MainReflexion


PS :如果建议的解决方案存在解决方法,请告诉我

答案 8 :(得分:0)

请注意,从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)

答案 9 :(得分:0)

Perfect Singleton Class,可以避免在序列化,克隆和反射过程中创建实例。

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

}

答案 10 :(得分:0)

为了克服反射引发的问题,使用了枚举,因为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访问它们。

Refer the post for more details.

答案 11 :(得分:0)

采用延迟初始化的方法:

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

答案 12 :(得分:-1)

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