> 创建新的类实例
有两种创建类实例的反射方法: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示例显示了如何实现此 Object:
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 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()
方法仅需要一个用于类型 Object 的类。由于type erasure,以下表达式的计算结果为true
:
HashMap.class == defaultAliases.getClass()
然后,该示例使用带有Constructor.newInstance()的此构造方法创建该类的新实例。
$ java RestoreAliases
Mail keys:
Duke
Fang