创建新的类实例
原文: https://docs.oracle.com/javase/tutorial/reflect/member/ctorInstance.html
创建类的实例有两种反射方法: java.lang.reflect.Constructor.newInstance() 和 Class.newInstance() 。前者是首选,因此在这些示例中使用,因为:
Class.newInstance()只能调用零参数构造器,而Constructor.newInstance()可以调用任何构造器,而不管参数的数量。Class.newInstance()抛出构造器抛出的任何异常,无论是选中还是未选中。Constructor.newInstance()总是用InvocationTargetException包装抛出的异常。Class.newInstance()要求构造器可见;Constructor.newInstance()可能在某些情况下调用private构造器。
有时可能需要从仅在构造之后设置的对象检索内部状态。考虑需要获取 java.io.Console 使用的内部字符集的场景。 (Console字符集存储在私有字段中,不一定与 java.nio.charset.Charset.defaultCharset() 返回的 Java 虚拟机默认字符集相同。 ConsoleCharset 示例显示了如何实现这一目标:
import java.io.Console;import java.nio.charset.Charset;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import static java.lang.System.out;public class ConsoleCharset {public static void main(String... args) {Constructor[] ctors = Console.class.getDeclaredConstructors();Constructor ctor = null;for (int i = 0; i < ctors.length; i++) {ctor = ctors[i];if (ctor.getGenericParameterTypes().length == 0)break;}try {ctor.setAccessible(true);Console c = (Console)ctor.newInstance();Field f = c.getClass().getDeclaredField("cs");f.setAccessible(true);out.format("Console charset : %s%n", f.get(c));out.format("Charset.defaultCharset(): %s%n",Charset.defaultCharset());// production code should handle these exceptions more gracefully} catch (InstantiationException x) {x.printStackTrace();} catch (InvocationTargetException x) {x.printStackTrace();} catch (IllegalAccessException x) {x.printStackTrace();} catch (NoSuchFieldException x) {x.printStackTrace();}}}
Note:
Class.newInstance() 只有在构造器的参数为零且已经可访问时才会成功。否则,必须如上例所示使用 Constructor.newInstance() 。
UNIX 系统的示例输出:
$ java ConsoleCharsetConsole charset : ISO-8859-1Charset.defaultCharset() : ISO-8859-1
Windows 系统的示例输出:
C:\> java ConsoleCharsetConsole charset : IBM437Charset.defaultCharset() : windows-1252
Constructor.newInstance() 的另一个常见应用是调用带参数的构造器。 RestoreAliases 示例查找特定的单参数构造器并调用它:
import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.util.HashMap;import java.util.Map;import java.util.Set;import static java.lang.System.out;class EmailAliases {private Set<String> aliases;private EmailAliases(HashMap<String, String> h) {aliases = h.keySet();}public void printKeys() {out.format("Mail keys:%n");for (String k : aliases)out.format(" %s%n", k);}}public class RestoreAliases {private static Map<String, String> defaultAliases = new HashMap<String, String>();static {defaultAliases.put("Duke", "duke@i-love-java");defaultAliases.put("Fang", "fang@evil-jealous-twin");}public static void main(String... args) {try {Constructor ctor = EmailAliases.class.getDeclaredConstructor(HashMap.class);ctor.setAccessible(true);EmailAliases email = (EmailAliases)ctor.newInstance(defaultAliases);email.printKeys();// production code should handle these exceptions more gracefully} catch (InstantiationException x) {x.printStackTrace();} catch (IllegalAccessException x) {x.printStackTrace();} catch (InvocationTargetException x) {x.printStackTrace();} catch (NoSuchMethodException x) {x.printStackTrace();}}}
此示例使用 Class.getDeclaredConstructor() 查找具有 java.util.HashMap 类型的单个参数的构造器。注意,传递HashMap.class就足够了,因为任何get*Constructor()方法的参数只需要类用于类型目的。由于类型擦除,以下表达式求值为true:
HashMap.class == defaultAliases.getClass()
然后,该示例使用此构造器使用 Constructor.newInstance() 创建类的新实例。
$ java RestoreAliasesMail keys:DukeFang
