本教程是针对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