Search code examples
javareflectionconstructorprivate

How to make sure instance of a class with a private constructor can not be created from outside of the class using Reflection?


I know that an instance of a class with a private constructor can be created using reflection but is there any way to make sure that the instance of the class can only be created within the same class using its own private constructor?

Let's take an example of a class Test, with a private constructor.

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

My question is - is there any way to ensure that the instance of the class can only be created within the same class and not from outside using reflection or any other method?


Solution

  • There are several ways to prevent the creation - but it is hard to tell which one is appropriate for your use-case:

    1. Throw an exception in the constructor

      You can either unconditionally throw an exception - making it (near) impossible to instantiate an instance - or only throw under certain conditions.

      Some conditions can be:

      • Inspecting the caller - using StackWalker for example.
      • Passing a "secret" passphrase to the constructor. JMH does this for example.
    2. Use Java Modules.

      As other modules can't deeply reflect into other named modules, Constructor.setAccessible will not work on your class outside of your own module.
      Of course this restriction doesn't apply to your own module - but you should be able to control your own module ;).

    3. Install a SecurityManager.

      Prevents Constructor.setAccessible from returning successfully.
      But the security manager is deprecated for removal, so I can't recommend it's use.

    Note: Most of those solutions can be circumvented in some way. And it is sometimes possible to add additional defenses against that. But at the end, it'll become a game of cat and mouse.