Java 教程是为 JDK 8 编写的。本页中描述的示例和实践未利用在后续版本中引入的改进。
创建类的实例有两种反射方法: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(); } } }
UNIX 系统的示例输出:
$ java ConsoleCharset Console charset : ISO-8859-1 Charset.defaultCharset() : ISO-8859-1
Windows 系统的示例输出:
C:\> java ConsoleCharset Console charset : IBM437 Charset.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()
方法的参数都只需要一个类用于类型目的。由于 type erasure (类型擦除),以下表达式求值为 true
:
HashMap.class == defaultAliases.getClass()
然后,该示例使用此构造函数使用 Constructor.newInstance()
创建类的新实例。
$ java RestoreAliases Mail keys: Duke Fang