创建新的类实例

原文: https://docs.oracle.com/javase/tutorial/reflect/member/ctorInstance.html

创建类的实例有两种反射方法: java.lang.reflect.Constructor.newInstance()Class.newInstance() 。前者是首选,因此在这些示例中使用,因为:

有时可能需要从仅在构造之后设置的对象检索内部状态。考虑需要获取 java.io.Console 使用的内部字符集的场景。 (Console字符集存储在私有字段中,不一定与 java.nio.charset.Charset.defaultCharset() 返回的 Java 虚拟机默认字符集相同。 ConsoleCharset 示例显示了如何实现这一目标:

  1. import java.io.Console;
  2. import java.nio.charset.Charset;
  3. import java.lang.reflect.Constructor;
  4. import java.lang.reflect.Field;
  5. import java.lang.reflect.InvocationTargetException;
  6. import static java.lang.System.out;
  7. public class ConsoleCharset {
  8. public static void main(String... args) {
  9. Constructor[] ctors = Console.class.getDeclaredConstructors();
  10. Constructor ctor = null;
  11. for (int i = 0; i < ctors.length; i++) {
  12. ctor = ctors[i];
  13. if (ctor.getGenericParameterTypes().length == 0)
  14. break;
  15. }
  16. try {
  17. ctor.setAccessible(true);
  18. Console c = (Console)ctor.newInstance();
  19. Field f = c.getClass().getDeclaredField("cs");
  20. f.setAccessible(true);
  21. out.format("Console charset : %s%n", f.get(c));
  22. out.format("Charset.defaultCharset(): %s%n",
  23. Charset.defaultCharset());
  24. // production code should handle these exceptions more gracefully
  25. } catch (InstantiationException x) {
  26. x.printStackTrace();
  27. } catch (InvocationTargetException x) {
  28. x.printStackTrace();
  29. } catch (IllegalAccessException x) {
  30. x.printStackTrace();
  31. } catch (NoSuchFieldException x) {
  32. x.printStackTrace();
  33. }
  34. }
  35. }

Note:

Class.newInstance() 只有在构造器的参数为​​零且已经可访问时才会成功。否则,必须如上例所示使用 Constructor.newInstance()


UNIX 系统的示例输出:

  1. $ java ConsoleCharset
  2. Console charset : ISO-8859-1
  3. Charset.defaultCharset() : ISO-8859-1

Windows 系统的示例输出:

  1. C:\> java ConsoleCharset
  2. Console charset : IBM437
  3. Charset.defaultCharset() : windows-1252

Constructor.newInstance() 的另一个常见应用是调用带参数的构造器。 RestoreAliases 示例查找特定的单参数构造器并调用它:

  1. import java.lang.reflect.Constructor;
  2. import java.lang.reflect.Field;
  3. import java.lang.reflect.InvocationTargetException;
  4. import java.util.HashMap;
  5. import java.util.Map;
  6. import java.util.Set;
  7. import static java.lang.System.out;
  8. class EmailAliases {
  9. private Set<String> aliases;
  10. private EmailAliases(HashMap<String, String> h) {
  11. aliases = h.keySet();
  12. }
  13. public void printKeys() {
  14. out.format("Mail keys:%n");
  15. for (String k : aliases)
  16. out.format(" %s%n", k);
  17. }
  18. }
  19. public class RestoreAliases {
  20. private static Map<String, String> defaultAliases = new HashMap<String, String>();
  21. static {
  22. defaultAliases.put("Duke", "duke@i-love-java");
  23. defaultAliases.put("Fang", "fang@evil-jealous-twin");
  24. }
  25. public static void main(String... args) {
  26. try {
  27. Constructor ctor = EmailAliases.class.getDeclaredConstructor(HashMap.class);
  28. ctor.setAccessible(true);
  29. EmailAliases email = (EmailAliases)ctor.newInstance(defaultAliases);
  30. email.printKeys();
  31. // production code should handle these exceptions more gracefully
  32. } catch (InstantiationException x) {
  33. x.printStackTrace();
  34. } catch (IllegalAccessException x) {
  35. x.printStackTrace();
  36. } catch (InvocationTargetException x) {
  37. x.printStackTrace();
  38. } catch (NoSuchMethodException x) {
  39. x.printStackTrace();
  40. }
  41. }
  42. }

此示例使用 Class.getDeclaredConstructor() 查找具有 java.util.HashMap 类型的单个参数的构造器。注意,传递HashMap.class就足够了,因为任何get*Constructor()方法的参数只需要类用于类型目的。由于类型擦除,以下表达式求值为true

  1. HashMap.class == defaultAliases.getClass()

然后,该示例使用此构造器使用 Constructor.newInstance() 创建类的新实例。

  1. $ java RestoreAliases
  2. Mail keys:
  3. Duke
  4. Fang