私は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
によってクラスインスタンスを停止できましたが、Reflectionによって停止することはできません。
オブジェクトを作成するための私のコードは
_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.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");
}
}
もちろん、これは本当にそれを止めないかもしれません-誰かがリフレクションをいじってプライベートメンバーにアクセスする場合、彼らはフィールドをnullに設定できるかもしれません自分自身。しかし、あなたは何を防ごうとしているのか、そしてそれがどれほど価値があるのかを自問する必要があります。
(編集:Bozhoが述べたように、最終フィールドはリフレクションを介しても設定できない場合があります。someを介してそれを行う方法があったとしても驚かないでしょうJNIなど...十分なアクセス権を与えると、ほとんど何でもできるようになります...)
_private Singleton() {
if (Singleton.singleton != null) {
throw new RuntimeException("Can't instantiate singleton twice");
}
}
_
注目すべきもう1つのことは、readResolve(..)
メソッドです。これは、クラスがSerialiable
を実装しているためです。そこで、既存のインスタンスを返す必要があります。
しかし、シングルトンを使用する最も簡単な方法は列挙型を使用することです。これらについて心配する必要はありません。
列挙型ソリューションとは別に、他のすべてはReflexionを介して回避することができますこれらは、Dave Gソリューションを回避する方法の2つの例です。
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);
_
出力:
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);
_
出力:
したがって、Enumを使用したくない場合は2つの方法が考えられます。
最初のオプション:securityManagerの使用:
不正な操作の使用を防ぎます(クラスの外部からプライベートメソッドを呼び出す....)
したがって、他の回答で提案されたシングルトンコンストラクターに1行追加するだけです。
_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")
2番目のオプション:シングルトンコンストラクターで、呼び出しが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 [〜#〜]:提案されたソリューションに回避策が存在する場合はお知らせください
静的なネストされたクラスを使用してそれを壊すことができます
その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
I Thing Belowコードは動作します..
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());
}
}
出力:シングルトンデザインパターン
シングルトンの代替として、 monostate pattern を見ることができます。そうすれば、クラスのインスタンス化はもう問題にならず、リストしたシナリオのanyを心配する必要はありません。
モノステートパターンでは、クラスのすべてのフィールドはstatic
です。つまり、シングルトンの場合と同様に、クラスのすべてのインスタンスが同じ状態を共有します。さらに、この事実は発信者には透過的です。 getInstance
のような特別なメソッドについて知る必要はありません。単にインスタンスを作成し、それらを操作します。
しかし、シングルトンと同様に、それは隠されたグローバルな状態の形式です。これは非常に悪いです。
リフレクションによって発生する問題を克服するために、Javaは内部的に列挙値が1回だけインスタンス化されることを保証するため、列挙が使用されます。Java唯一の欠点は、柔軟性がないこと、つまり遅延初期化ができないことです。
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は、enumコンストラクターの作成と呼び出しを内部的に処理します。列挙型はコンストラクターの定義をプログラムに提供しないため、Reflectionからもそれらにアクセスすることはできません。
遅延初期化のアプローチ:
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を呼び出す前にリフレクションを使用してインスタンスを作成する場合でも機能します
ただ注意してください、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)
シリアル化、クローン作成、およびリフレクション中のインスタンス作成を回避できる完全なシングルトンクラス。
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();
}
}