web-dev-qa-db-ja.com

クラスのプライベートコンストラクターにアクセスするにはどうすればよいですか?

私はJava開発者です。インタビューで私はプライベートコンストラクターについて質問されました。

クラスのプライベートコンストラクターにアクセスしてインスタンス化できますか?

「いいえ」と答えましたが、間違っていました。

私が間違っていた理由を説明し、プライベートコンストラクターでオブジェクトをインスタンス化する例を挙げていただけますか?

65
gmhk

制限を回避する1つの方法は、リフレクションを使用することです:

import Java.lang.reflect.Constructor;

public class Example {
    public static void main(final String[] args) throws Exception {
        Constructor<Foo> constructor = Foo.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        Foo foo = constructor.newInstance();
        System.out.println(foo);
    }
}

class Foo {
    private Foo() {
        // private!
    }

    @Override
    public String toString() {
        return "I'm a Foo and I'm alright!";
    }
}
81
Joachim Sauer
  • クラス自体内でアクセスできます(例:public static factoryメソッド)
  • ネストされたクラスの場合、外側のクラスからアクセスできます
  • 適切な権限があれば、リフレクションでアクセスできます

ただし、これらのいずれかが当てはまるかどうかは明確ではありません。詳細を教えてください。

67
Jon Skeet

これは、反射を使用して実現できます。

プライベートコンストラクターを使用したクラスTestを検討します。

Constructor<?> constructor  = Test.class.getDeclaredConstructor(Context.class, String[].class);
Assert.assertTrue(Modifier.isPrivate(constructor.getModifiers()));
constructor.setAccessible(true);
Object instance = constructor.newInstance(context, (Object)new String[0]);
19
tonio

インタビューでのプライベートコンストラクターに関する最初の質問は、

クラスにプライベートコンストラクターを含めることはできますか?

時には、候補者からの答えは、「いいえ、私たちはプライベートコンストラクターを持つことはできません」です。

だから私は言いたい、 はい、クラスにプライベートコンストラクタを含めることができます。

特別なことではありません。このように考えてみてください。

プライベート: プライベートなものはクラス内からのみアクセスできます。

コンストラクタ: クラスと同じ名前のメソッドで、クラスのオブジェクトが作成されると暗黙的に呼び出されます。

または、オブジェクトを作成するには、そのコンストラクターを呼び出す必要があります。コンストラクターが呼び出されない場合、オブジェクトをインスタンス化できません。

つまり、クラスにプライベートコンストラクターがある場合、そのオブジェクトはクラス内でのみインスタンス化できます。簡単な言葉で言えば、コンストラクターがプライベートの場合、クラス外でそのオブジェクトを作成することはできません。

利点は何ですか この概念は、達成するために実装することができます シングルトンオブジェクト (クラスのオブジェクトを1つだけ作成できることを意味します)。

次のコードを参照してください。

class MyClass{
    private static MyClass obj = new MyClass();

    private MyClass(){

    }

    public static MyClass getObject(){
        return obj;
    }
}
class Main{
    public static void main(String args[]){

        MyClass o = MyClass.getObject();
        //The above statement will return you the one and only object of MyClass


        //MyClass o = new MyClass();
        //Above statement (if compiled) will throw an error that you cannot access the constructor.

    }
}

トリッキーな部分、なぜあなたが間違っていたのか、他の回答ですでに説明したように、Reflectionを使用して制限をバイパスできます。

6
gprathour

上記の回答は気に入っていますが、プライベートコンストラクターを持つクラスの新しいインスタンスを作成する方法は2つあります。それはすべて、あなたが何を達成したいのか、どのような状況下にあるのかによります。

1: Javaインストルメンテーション および [〜#〜] asm [〜#〜] の使用

この場合、トランスフォーマーでJVMを起動する必要があります。これを行うには、新しいJavaエージェントを実装し、このトランスフォーマーがコンストラクターを変更するようにする必要があります。

最初に クラストランスフォーマー を作成します。このクラスには、transformというメソッドがあります。このメソッドをオーバーライドし、このメソッド内でASM クラスリーダー およびその他のクラスを使用して、コンストラクターの可視性を操作できます。トランスフォーマーが完了すると、クライアントコードはコンストラクターにアクセスできるようになります。

詳細については、こちらをご覧ください: Changing a private Java constructor with ASM

2:コンストラクターコードを書き換える

まあ、これは実際にはコンストラクタにアクセスするわけではありませんが、それでもインスタンスを作成できます。サードパーティのライブラリ(Guavaと言います)を使用し、コードにアクセスできるが、何らかの理由でJVMによってロードされるjarのコードを変更したくないと仮定しましょう(私は知っています、これはあまりリアルではありませんが、コードがJettyのような共有コンテナにあり、共有コードを変更できないと仮定しますが、個別のクラスローディングコンテキストがあります)、プライベートコンストラクタでサードパーティコードのコピーを作成し、変更コード内でprotectedまたはpublicにプライベートコンストラクターを追加し、クラスパスの先頭にクラスを配置します。その時点から、クライアントは変更されたコンストラクタを使用してインスタンスを作成できます。

この後者の変更は link seam と呼ばれます。これは、有効化ポイントがクラスパスである一種のシームです。

4
Captain Fogetti

Java Reflectionを次のように使用します。

   import Java.lang.reflect.Constructor;

   import Java.lang.reflect.InvocationTargetException;

   class Test   
   {

      private Test()  //private constructor
      {
      } 
   }

  public class Sample{

      public static void main(String args[]) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException
     {

       Class c=Class.forName("Test"); //specify class name in quotes

       //----Accessing private constructor
       Constructor con=c.getDeclaredConstructor();
       con.setAccessible(true);     
       Object obj=con.newInstance();
   }   
} 
4

はい、できます、@ Jon Steetが述べたように。

プライベートコンストラクターにアクセスする別の方法は、このクラス内でパブリック静的メソッドを作成し、その戻り値の型をオブジェクトとして使用することです。

public class ClassToAccess
{

    public static void main(String[] args)
    {
        {
            ClassWithPrivateConstructor obj = ClassWithPrivateConstructor.getObj();
            obj.printsomething();
        }

    }

}

class ClassWithPrivateConstructor
{

    private ClassWithPrivateConstructor()
    {
    }

    public void printsomething()
    {
        System.out.println("HelloWorld");
    }

    public static ClassWithPrivateConstructor getObj()
    {
        return new ClassWithPrivateConstructor();
    }
}
3
Tapeshvar

もちろん、同じクラスおよびその内部クラスの他のメソッドまたはコンストラクターからプライベートコンストラクターにアクセスできます。リフレクションを使用すると、SecurityManagerによって妨げられない限り、他の場所でプライベートコンストラクターを使用することもできます。

2
jarnbjo

はい、プライベートコンストラクターにアクセスするか、プライベートコンストラクターでクラスをインスタンス化できます。 JavaリフレクションAPIとシングルトンデザインパターンは、プライベートコンストラクターにアクセスするための概念を頻繁に利用しています。また、SpringフレームワークコンテナーはBeanのプライベートコンストラクターにアクセスでき、このフレームワークはJavaリフレクションAPI:次のコードは、プライベートコンストラクターにアクセスする方法を示しています。

class Demo{
     private Demo(){
      System.out.println("private constructor invocation");
     }
}

class Main{
   public static void main(String[] args){
       try{
           Class class = Class.forName("Demo");
           Constructor<?> con = string.getDeclaredConstructor();
           con.setAccessible(true);
           con.newInstance(null);
       }catch(Exception e){}

   }
}

output:
private constructor invocation

私はあなたがそれを得たことを願っています。

2
Sushan Baskota

この例がお役に立てば幸いです:

package MyPackage;

import Java.lang.reflect.Constructor;

/**
 * @author Niravdas
 */

class ClassWithPrivateConstructor {

    private ClassWithPrivateConstructor() {
        System.out.println("private Constructor Called");
    }

}
public class InvokePrivateConstructor 
{
     public static void main(String[] args) {
        try
        {
           Class ref = Class.forName("MyPackage.ClassWithPrivateConstructor");
           Constructor<?> con = ref.getDeclaredConstructor();
           con.setAccessible(true);
           ClassWithPrivateConstructor obj = (ClassWithPrivateConstructor) con.newInstance(null);
       }catch(Exception e){
           e.printStackTrace();
       }
    }

}

出力:呼び出されるprivateコンストラクター

1
Niravdas

シングルトンパターンを見てください。プライベートコンストラクターを使用します。

0
3biga

クラス外のプライベートコンストラクターにはアクセスできませんが、Java Reflection APIを使用してプライベートコンストラクターにアクセスできます。以下のコードを参照してください。

public class Test{
    private Test()
    System.out.println("Private Constructor called");
}
}


public class PrivateConsTest{
    public void accessPrivateCons(Test test){

        Field[] fields = test.getClass().getDeclaredFields();

        for (Field field : fields) {
            if (Modifier.isPrivate(field.getModifiers())) {
                field.setAccessible(true);
                System.out.println(field.getName()+" : "+field.get(test));
            }
        }
    }
}

Spring IoCを使用している場合、Springコンテナはプライベートコンストラクターを持つクラスのオブジェクトも作成および注入します。

0

Javaでプライベートコンストラクターを使用できます。

プライベートコンストラクターを使用できるさまざまなシナリオがあります。主なものは

  • 内部コンストラクターの連鎖
  • シングルトンクラスのデザインパターン
0
CodeRider

私はこれがうまくいくように試しました。私が間違っている場合、いくつかの提案をお願いします。

import Java.lang.reflect.Constructor;

class TestCon {
private TestCon() {
    System.out.println("default constructor....");
}

public void testMethod() {
    System.out.println("method executed.");
  }
}

class TestPrivateConstructor {

public static void main(String[] args) {
    try {
        Class testConClass = TestCon.class;
        System.out.println(testConClass.getSimpleName());

        Constructor[] constructors =    testConClass.getDeclaredConstructors();
        constructors[0].setAccessible(true);
        TestCon testObj = (TestCon) constructors[0].newInstance();
        //we can call method also..
        testObj.testMethod();
    } catch (Exception e) {
        e.printStackTrace();
    }

}
}
0

はい、 Reflection を使用して、プライベートコンストラクターでインスタンスをインスタンス化できます。以下で貼り付けた例を参照してください Java2s から取得:

import Java.lang.reflect.Constructor;
import Java.lang.reflect.InvocationTargetException;

class Deny {
  private Deny() {
    System.out.format("Deny constructor%n");
  }
}

public class ConstructorTroubleAccess {
  public static void main(String... args) {
    try {
      Constructor c = Deny.class.getDeclaredConstructor();
      // c.setAccessible(true); // solution
      c.newInstance();

      // production code should handle these exceptions more gracefully
    } catch (InvocationTargetException x) {
      x.printStackTrace();
    } catch (NoSuchMethodException x) {
      x.printStackTrace();
    } catch (InstantiationException x) {
      x.printStackTrace();
    } catch (IllegalAccessException x) {
      x.printStackTrace();
    }
  }
}
0
madx

Reflectionは、JavaのAPIであり、使用されるアクセス指定子に関係なく、実行時にメソッドを呼び出すために使用できます。クラスのプライベートコンストラクタにアクセスするには:

_My utility class_

_public final class Example{
    private Example(){
        throw new UnsupportedOperationException("It is a utility call");
    }
    public static int twice(int i)
    {
        int val = i*2;
        return val;
    }
}
_

My Test class which creates an object of the Utility class(Example)

_import Java.lang.reflect.Constructor;
import Java.lang.reflect.Field;
import Java.lang.reflect.InvocationTargetException;
class Test{
    public static void main(String[] args) throws Exception {
        int i =2;
        final Constructor<?>[] constructors = Example.class.getDeclaredConstructors();
        constructors[0].setAccessible(true);
        constructors[0].newInstance();
    }
}
_

コンストラクターを呼び出すと、エラー_Java.lang.UnsupportedOperationException: It is a utility call_が返されます

ただし、リフレクションAPIを使用するとオーバーヘッドの問題が発生することを忘れないでください

0
Animesh Jaiswal

プライベートコンストラクターを使用するための基本的な前提は、プライベートコンストラクターを使用すると、自身のクラスのコード以外のコードのアクセスがそのクラスのオブジェクトを作成することを制限することです。

はい、クラスにプライベートコンストラクターを含めることができます。また、クラスの新しいオブジェクトを作成する静的メソッドを作成することにより、アクセス可能にすることができます。

 Class A{
private A(){
}
private static createObj(){
return new A();
}

Class B{
public static void main(String[]args){
A a=A.createObj();
}}

したがって、このクラスのオブジェクトを作成するには、他のクラスが静的メソッドを使用する必要があります。

コンストラクタをプライベートにするときに静的メソッドを使用するポイントは何ですか?

静的メソッドが存在するため、そのクラスのインスタンスを作成する必要がある場合、インスタンスを作成する前に静的メソッドに適用できる事前定義済みのチェックを行うことができます。たとえば、シングルトンクラスでは、静的メソッドはインスタンスが既に作成されているかどうかを確認します。インスタンスがすでに作成されている場合、新しいインスタンスを作成するのではなく、単にそのインスタンスを返すだけです。

 public static MySingleTon getInstance(){
    if(myObj == null){
        myObj = new MySingleTon();
    }
    return myObj;
}
0
Nikhil Arora