本教程是针对JDK 8编写的。本页面中描述的示例和实践未利用后续版本中引入的改进,并可能使用不再可用的技术。
请参阅Java语言更改,了解Java SE 9及后续版本中更新的语言特性的摘要。
请参阅JDK发布说明,了解所有JDK版本的新功能、增强功能以及已删除或弃用选项的信息。
创建类的实例有两种反射方法:java.lang.reflect.Constructor.newInstance()
和Class.newInstance()
。前者是首选,并且在这些示例中使用,原因如下:
Class.newInstance()
只能调用无参数构造函数,而Constructor.newInstance()
可以调用任何构造函数,无论参数个数如何。Class.newInstance()
会抛出构造函数抛出的任何异常,无论它是已检查异常还是未检查异常。Constructor.newInstance()
总是用InvocationTargetException
包装抛出的异常。Class.newInstance()
要求构造函数可见;Constructor.newInstance()
在某些情况下可以调用私有构造函数。有时候,可能希望从构造后才设置的对象中检索内部状态。考虑一个需要获取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("控制台字符集 : %s%n", f.get(c)); out.format("默认字符集 : %s%n", Charset.defaultCharset()); // 正式代码应更优雅地处理这些异常 } catch (InstantiationException x) { x.printStackTrace(); } catch (InvocationTargetException x) { x.printStackTrace(); } catch (IllegalAccessException x) { x.printStackTrace(); } catch (NoSuchFieldException x) { x.printStackTrace(); } } }
UNIX系统的示例输出:
$ java ConsoleCharset 控制台字符集 : ISO-8859-1 默认字符集 : ISO-8859-1
Windows系统的示例输出:
C:\> java ConsoleCharset 控制台字符集 : IBM437 默认字符集 : 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("邮件键:%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(); // 生产代码应该更优雅地处理这些异常 } 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 RestoreAliases 邮件键: Duke Fang